媒体文件扫描原理解析

媒体文件扫描

1、扫描的时机

2、扫描的逻辑、添加数据库的流程

一、首先注册BroadcastReceiver,创建MediaScannerReceiver

1)、设置监听action

<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>

2)、在onReceive方法中启动具体的扫描操作

分为开机扫描和非开机扫描

a、开始扫描:内外存储都会被扫描

// Scan both internal and external storage
scan(context, MediaProvider.INTERNAL_VOLUME, null);

b、非开机扫描:

if (uri != null && uri.getScheme().equals("file")) {
// handle intents related to external storage
String path = uri.getPath();
String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath();

try {
    path = new File(path).getCanonicalPath();
} catch (Exception e) {
    Log.e(TAG, "couldn't canonicalize " + path);
    return;
}
if (path.startsWith(legacyPath)) {
    path = externalStoragePath + path.substring(legacyPath.length());
}

String sdPath = StorageManagerSmt.getExternalSdcardPath();

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

c、扫描操作

private void scan(Context context, String volume, String volumePath) {
Bundle args = new Bundle();
args.putString("volume", volume);
if (volumePath != null) {
    args.putString("volumepath", volumePath);
}
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));
}

因为扫描存储设备是耗时操作,所以新启service来处理

二、进入MediaScannerService,进行扫描逻辑的处理

directories为需要扫描的路径集合:

Handler->handleMessage->scan->MediaScanner.scanDirectories(directories, volumeName){

...

makeFileCache

...

processDirectory

...

}

private native void processDirectory(String path, MediaScannerClient client)

进入到native层processDirectory

MediaScaner.cpp(framewoks/av/media/libmedia/MediaScanner.cpp)

MediaScanner::processDirectory-->MediaScanner::doProcessDirectory{

...

DIR* dir = opendir(path);//打开路径

while ((entry = readdir(dir))) {

    if \(**doProcessDirectoryEntry**\(path, pathRemaining, client, noMedia, entry, fileSpot\)

            == MEDIA\_SCAN\_RESULT\_ERROR\) {

        result = MEDIA\_SCAN\_RESULT\_ERROR;

        break;

    }

}

...

}

-->MediaScanner::doProcessDirectoryEntry{

根据type为 DT_DIR , 或者为 DT_REG 分别进行处理

DT_DIR:

bool childNoMedia = noMedia;
// set noMedia flag on directories with a name that starts with '.'
// for example, the Mac ".Trashes" directory
if (name[0] == '.')
    childNoMedia = true;

// report the directory to the client
if (stat(path, &statbuf) == 0) {
    status_t status = client.scanFile(path, statbuf.st_mtime, 0,
            true /*isDirectory*/, childNoMedia);
    if (status) {
        return MEDIA_SCAN_RESULT_ERROR;
    }
}

// and now process its contents
strcat(fileSpot, "/");
MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1,
        client, childNoMedia);
if (result == MEDIA_SCAN_RESULT_ERROR) {
    return MEDIA_SCAN_RESULT_ERROR;
}

DT_REG:

stat(path, &statbuf);
status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size,
        false /*isDirectory*/, noMedia);
if (status) {
    return MEDIA_SCAN_RESULT_ERROR;
}

(a)、代码中的client.canFile由代码逻辑可知:

client就是MyMediaScannerClient ,即MediaScanner.java中的内部类

MyMediaScannerClient 实现了MediaScannerClient接口

public interface MediaScannerClient
{    
    public void scanFile(String path, long lastModified, long fileSize,
            boolean isDirectory, boolean noMedia);

    /**
     * Called by native code to return metadata extracted from media files.
     */
    public void handleStringTag(String name, String value);

    /**
     * Called by native code to return mime type extracted from DRM content.
     */
    public void setMimeType(String mimeType);
}

所以执行client.canFile就相当于从native层又回调到了java层MyMediaScannerClient的canFile方法

进入doScanFile{

beginFile ->

endFile -> 存入数据库中

}

(b)、doProcessDirectory 继续处理

}

results for ""

    No results matching ""