Android 13 MediaProvider简单记录

源码分析2024年5月8日 pm5:38发布3个月前更新 3xcn.com@站长
0 0 0
导航号,我的单页导航
目录

前言

Android 9时扫描逻辑还在MediaScanner中(这块之前有介绍过),而后续Android高版本开始变化,以Android 13来说,扫描逻辑已经放在MediaProvider中了,也就是ModernMediaScanner

Android 10,11和12项目少

今天就简单介绍一下Android 13中MediaProvider的扫描流程。

正文

MediaProvider目录

packages\providers\MediaProvider

这里主要接扫下面几个java文件

  1. MediaProvider.java

  2. ModernMediaScanner.java

  3. MediaReceiver.java

  4. MediaService.java

  5. ExternalStorageServiceImpl.java

一开机时,系统会自动拉起MediaProvider,我们知道ContentProvider的启动比Application的启动更早,因此我这里以开机先启动介绍。

  1. 以插入U盘为例

  2. U盘挂载监听有两个地方,一个是MediaReceiver,另外一个地方ExternalStorageServiceImpl,后面都会单独介绍。

MediaProvider.java

开机时会先执行onCreate()方法。

@Override
public boolean onCreate() {
    //略
    //初始化ModernMediaScanner
    mMediaScanner = new ModernMediaScanner(context);
    //初始化DatabaseHelper,包括External和Internal的
    mInternalDatabase = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, false, false,
            Column.class, ExportedSince.class, Metrics::logSchemaChange, mFilesListener,
            MIGRATION_LISTENER, mIdGenerator, true);
    mExternalDatabase = new DatabaseHelper(context, EXTERNAL_DATABASE_NAME, false, false,
            Column.class, ExportedSince.class, Metrics::logSchemaChange, mFilesListener,
            MIGRATION_LISTENER, mIdGenerator, true);
    //略
    //attach内置磁盘
    attachVolume(MediaVolume.fromInternal(), /* validate */ false);
    //略
    return true;
}

很多不是很懂,暂时略去,只关注自己需要的。

上面初始化ModernMediaScanner对象,也就这里扫描数据的。

ModernMediaScanner.java

public ModernMediaScanner(Context context) {
    mContext = context;
    mDrmClient = new DrmManagerClient(context);
    for (DrmSupportInfo info : mDrmClient.getAvailableDrmSupportInfo()) {
        Iterator<String> mimeTypes = info.getMimeTypeIterator();
        while (mimeTypes.hasNext()) {
            mDrmMimeTypes.add(mimeTypes.next());
        }
    }
}

这里主要添加DRM的MimeTypes,略过。

MediaReceiver.java

广播监听很慢,是一个[致命]的缺点。因此U盘挂载和卸载流程走ExternalStorageServiceImpl中了。

插入U盘是监听U盘的挂载,这个是是通过静态广播监听。

先看AndroidManifest.xml注册了哪些广播

<receiver android:name="com.android.providers.media.MediaReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.LOCALE_CHANGED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
        <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
        <data android:scheme="package" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

这些广播都需要处理,我们这里以android.intent.action.MEDIA_MOUNTED为例,也就是U盘的挂载成功时发出来的广播。

public class MediaReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        //开机广播
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            IdleService.scheduleIdlePass(context);
        } else {
            //其他的广播走这里
            intent.setComponent(new ComponentName(context, MediaService.class));
            MediaService.enqueueWork(context, intent);
        }
    }
}

重点关注

MediaService.enqueueWork(context, intent);

MediaService.java

MediaService继承JobIntentService,至于JobIntentService的使用,可以看看《JobIntentService的使用》,这里不过多介绍。

enqueueWork()
public static void enqueueWork(Context context, Intent work) {
    enqueueWork(context, MediaService.class, JOB_ID, work);
}

传入的work是在onHandleWork()中处理。

onHandleWork()

隐藏内容!
评论后才能查看!

onMediaMountedBroadcast()
private static void onMediaMountedBroadcast(Context context, Intent intent)
        throws IOException {
    final StorageVolume volume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
    //挂载的磁盘信息,如果不为null才需要扫描
    if (volume != null) {
        MediaVolume mediaVolume = MediaVolume.fromStorageVolume(volume);
        try (ContentProviderClient cpc = context.getContentResolver()
                .acquireContentProviderClient(MediaStore.AUTHORITY)) {
            //判断是否已经isVolumeAttached
            if (!((MediaProvider)cpc.getLocalContentProvider()).isVolumeAttached(mediaVolume)) {
                //进入扫描逻辑
                onScanVolume(context, mediaVolume, REASON_MOUNTED);
            } else {
                //已经扫描过了
            }
        }
    } else {
        Log.e(TAG, "Couldn't retrieve StorageVolume from intent");
    }
}

isVolumeAttached()在启动后插入U盘时,这里大部分都是返回true的,也就是已经扫描过,因为ExternalStorageServiceImpl的对磁盘的监听更快。

这里假设没有扫描,进入onScanVolume()扫描阶段。

PS: 扫描流程一定是进入onScanVolume()

onScanVolume()

隐藏内容!
评论后才能查看!

  1. 扫描开始广播[ACTION_MEDIA_SCANNER_STARTED]发送

  2. 扫描结束广播[ACTION_MEDIA_SCANNER_FINISHED]发送

  3. 添加磁盘扫描标志位attachVolume()

  4. 扫描磁盘内容scanDirectory()

广播发送就不过多叙述了,依次进入attachVolume()和scanDirectory()。

MediaProvider.java

attachVolume()

隐藏内容!
评论后才能查看!

这里重点是

  1. 查看磁盘的盘符名是否支持

  2. 查看磁盘是否挂载成功,也就是文件[根目录]是否存在

  3. 添加到扫描过的磁盘列表,用于判断是否扫描过

回到上面,看scanDirectory()

scanDirectory()
public void scanDirectory(File file, int reason) {
    mMediaScanner.scanDirectory(file, reason);
}

哈哈,不干活的,抛给了mMediaScanner,也就是ModernMediaScanner的对象。

ModernMediaScanner.java

@Override
public void scanDirectory(File file, int reason) {
    try (Scan scan = new Scan(file, reason, /*ownerPackage*/ null)) {
        scan.run();
    } catch (OperationCanceledException ignored) {
    } catch (FileNotFoundException e) {
       Log.e(TAG, "Couldn't find directory to scan", e) ;
    }
}

呃呃,Scan继承Runnable。

暂时打住,我们今天之大概走一下U盘的挂载,至于怎么扫描后面单独分析。

ExternalStorageServiceImpl.java

我上面也说过,广播有个致命缺点,就是太慢了!

因此Android高版本引入了其他的方式监听U盘的挂载和卸载。

ExternalStorageServiceImpl继承ExternalStorageService,至于具体的使用,暂时没摸透,略过。

隐藏内容!
评论后才能查看!

小结

暂时就记录到这吧,后面有空会深入介绍。

参考文章

© 版权声明
导航号,我的单页导航

相关文章

导航号,我的单页导航

暂无评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...

网站升级中

网站升级中,可能存在一些bug。欢迎反馈 https://www.91es.com/we.html