Gallery3d 学习笔记(2)

  上一次我们只是研究了一下Gallery3d的大致情况,发现了一些问题,就是什么时候发出开始扫描信息的和何时扫描的处理没有找到。


   我们不得不扩大研究范围了,开始看在package\provider\mediaprovider,否则我们理解不了上述疑问。


   mediaprovider 也是一个相关的APK,这个应用是对多媒体相关应用提供支持,如gallery3D music camera.


为什么要把扫描放在这里呢?因为多媒体的文件是多个应用处理的,每个应用都去扫描一遍是不是很浪费资源啊?对吧,再说每个应用扫描的数据库都保存一份也很浪费空间啊。


那么我们来简要的看下他的内容。


  (使用的是android 4.2的mediaprovider ,因手头只有4.*的mediaprovider,研究的内容或许和2.3有些差别)

        <receiver android:name="MediaScannerReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </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_UNMOUNTED" />
                <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>

我们看到了上面这个receiver,这里注册了

四种情况,我们再在MediaScannerReceiver中看他的处理

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Uri uri = intent.getData();
        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
            // scan internal storage
            scan(context, MediaProvider.INTERNAL_VOLUME);
        } else {
            if (uri.getScheme().equals("file")) {
                // handle intents related to external storage
                String path = uri.getPath();
                String externalStoragePath = Environment.getExternalStorageDirectory().getPath();

                Log.d(TAG, "action: " + action + " path: " + path);
                if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
                    // scan whenever any volume is mounted
                    scan(context, MediaProvider.EXTERNAL_VOLUME);
                } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
                        path != null && path.startsWith(externalStoragePath + "/")) {
                    scanFile(context, path);
                }
            }
        }
    }

开机完成后就会对内部存储器进行扫描,而其他的情况,会去取得url的位置,如果是Action_Media_Scanner_Scan_File去扫描url 指定位置,如果其他的外部存储器也会去扫描。


那么说明开机就会全部扫描内部存储器,外部存储器挂载时也会扫描,另外还可以主动要求扫描某个文件。

(谁会主动发起文件扫描呢?比如彩信接受到一个图片存在一个地方,这个时候想让图库扫描到,可以使用

  SendBroadcast(new Intent(Intent,ACTION_MEDIA_SCANNER_SCAN_FILE,uri)); 主动让图库扫描到。)


扫描的发起我们都弄清楚了,看下怎么扫描的。


    private void scan(Context context, String volume) {
        Bundle args = new Bundle();
        args.putString("volume", volume);
        context.startService(
                new Intent(context, MediaScannerService.class).putExtras(args));
    }    

    private void scanFile(Context context, String path) {
        Bundle args = new Bundle();
        args.putString("filepath", path);
        context.startService(
                new Intent(context, MediaScannerService.class).putExtras(args));
    } 

不管扫描文件还是扫描存储器,都是启动一个服务去做,只是参数不同。


那么扫描内部是怎么处理的呢?我们在MediaScannerService.java中学习


    @Override
    public void onCreate()
    {
        PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        StorageManager storageManager = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
        mExternalStoragePaths = storageManager.getVolumePaths();

        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.
        Thread thr = new Thread(null, this, "MediaScannerService");
        thr.start();
    }

我们看到他开启了一个线程,最后调用到scan 成员函数

   private void scan(String[] directories, String volumeName) {
        // don't sleep while scanning
        mWakeLock.acquire();

        ContentValues values = new ContentValues();
        values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
        Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);

        Uri uri = Uri.parse("file://" + directories[0]);
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
        
        try {
            if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
                openDatabase(volumeName);
            }

            MediaScanner scanner = createMediaScanner();
            scanner.scanDirectories(directories, volumeName);
        } catch (Exception e) {
            Log.e(TAG, "exception in MediaScanner.scan()", e);
        }

        getContentResolver().delete(scanUri, null, null);

        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
        mWakeLock.release();
    }

整个扫描过程就是首先防止手机睡眠,找到扫描的目录,发送开始扫描的广播,扫描,扫描后,将目录设置成已经扫描过,发送扫描完成的广播,而扫描过程就是在数据库里面添加数据的过程,以后找文件就可以直接在数据库里面找了。

可能有人还想知道这里面到底是怎么扫描的

            MediaScanner scanner = createMediaScanner();
            scanner.scanDirectories(directories, volumeName);
在framework/base/media/java/android/media的MediaScanner.java 有scanDirectories 中会继续调用Jni的接口到MediaScanner.cpp中去,实现就在里面。

再会调用到processDirectory中再通过递归调用doProcessDirectory来实现对目录和文件的枚举。


那么数据库在哪里呢?是怎么存储的呢?我们下个回合分解   


参考文档

http://wenku.baidu.com/view/9151d3d349649b6648d747d2.html

你可能感兴趣的:(Gallery3d 学习笔记(2))