http://blog.csdn.net/jsyzgygcj/article/details/44178545
插入u盘后,系统会发送广播android.intent.action.MEDIA_MOUNTED
位于packages/providers/MediaProvider/src/com/android/providers/media/MediaScannerService.java
MediaScannerService可以接收开机广播和u盘插入广播,收到广播后执行scan方法,将文件或文件夹路径存储到Bundle,并启动MediaScannerService,我们看MediaScannerService中ServiceHandler的代码
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span> {</span> <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) { Bundle arguments = (Bundle) msg.obj; String filePath = arguments.getString(<span class="hljs-string">"filepath"</span>);<span class="hljs-comment">//单个文件</span> String path = arguments.getString(<span class="hljs-string">"path"</span>);<span class="hljs-comment">//文件目录</span> <span class="hljs-keyword">try</span> { <span class="hljs-keyword">if</span> (filePath != <span class="hljs-keyword">null</span>) { IBinder binder = arguments.getIBinder(<span class="hljs-string">"listener"</span>); IMediaScannerListener listener = (binder == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">null</span> : IMediaScannerListener.Stub.asInterface(binder)); Uri uri = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">try</span> { uri = scanFile(filePath, arguments.getString(<span class="hljs-string">"mimetype"</span>));<span class="hljs-comment">//扫描单个文件</span> } <span class="hljs-keyword">catch</span> (Exception e) { Log.e(TAG, <span class="hljs-string">"Exception scanning file"</span>, e); } <span class="hljs-keyword">if</span> (listener != <span class="hljs-keyword">null</span>) { listener.scanCompleted(filePath, uri); } } <span class="hljs-keyword">else</span> { String volume = arguments.getString(<span class="hljs-string">"volume"</span>); String[] directories = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">if</span> (MediaProvider.INTERNAL_VOLUME.equals(volume)) { <span class="hljs-comment">// scan internal media storage</span> directories = <span class="hljs-keyword">new</span> String[] { Environment.getRootDirectory() + <span class="hljs-string">"/media"</span>, }; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (MediaProvider.EXTERNAL_VOLUME.equals(volume)) { <span class="hljs-comment">// scan external storage volumes</span> <span class="hljs-keyword">if</span> (path == <span class="hljs-keyword">null</span>) { directories = mExternalStoragePaths; } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (path.startsWith(<span class="hljs-string">"/mnt/usb_storage"</span>)) { directories = <span class="hljs-keyword">new</span> String[] {path};<span class="hljs-comment">//{"/mnt/usb_storage"};</span> } <span class="hljs-keyword">else</span> { directories = <span class="hljs-keyword">new</span> String[] {path}; } } <span class="hljs-keyword">if</span> (directories != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">if</span> (<span class="hljs-keyword">true</span>) Log.d(TAG, <span class="hljs-string">"start scanning volume "</span> + volume + <span class="hljs-string">": "</span> + Arrays.toString(directories)); scan(directories, volume);<span class="hljs-comment">//扫描文件列表</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">true</span>) Log.d(TAG, <span class="hljs-string">"done scanning volume "</span> + volume); } } } <span class="hljs-keyword">catch</span> (Exception e) { Log.e(TAG, <span class="hljs-string">"Exception in handleMessage"</span>, e); } stopSelf(msg.arg1); } };</code>
可以看到代码会调用scan或者scanFile扫描
<code class="hljs cs has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">scan</span>(String[] directories, String volumeName) { Uri uri = Uri.parse(<span class="hljs-string">"file://"</span> + directories[<span class="hljs-number">0</span>]); <span class="hljs-comment">// don't sleep while scanning</span> mWakeLock.acquire(); <span class="hljs-keyword">try</span> { ContentValues values = <span class="hljs-keyword">new</span> ContentValues(); values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName); Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values); sendBroadcast(<span class="hljs-keyword">new</span> Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri)); <span class="hljs-keyword">try</span> { <span class="hljs-keyword">if</span> (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) { openDatabase(volumeName); } MediaScanner scanner = createMediaScanner(); <span class="hljs-keyword">if</span>(directories!=<span class="hljs-keyword">null</span>){ scanner.scanDirectories(directories, volumeName); } } <span class="hljs-keyword">catch</span> (Exception e) { Log.e(TAG, <span class="hljs-string">"exception in MediaScanner.scan()"</span>, e); } getContentResolver().delete(scanUri, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>); } <span class="hljs-keyword">finally</span> { sendBroadcast(<span class="hljs-keyword">new</span> Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri)); mWakeLock.release(); } }</code>
<code class="hljs lasso has-numbering"> <span class="hljs-keyword">private</span> Uri scanFile(<span class="hljs-built_in">String</span> path, <span class="hljs-built_in">String</span> mimeType) { <span class="hljs-built_in">String</span> volumeName <span class="hljs-subst">=</span> MediaProvider<span class="hljs-built_in">.</span>EXTERNAL_VOLUME; openDatabase(volumeName); MediaScanner scanner <span class="hljs-subst">=</span> createMediaScanner(); try { <span class="hljs-comment">// make sure the file path is in canonical form</span> <span class="hljs-built_in">String</span> canonicalPath <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> File(path)<span class="hljs-built_in">.</span>getCanonicalPath(); <span class="hljs-keyword">return</span> scanner<span class="hljs-built_in">.</span>scanSingleFile(canonicalPath, volumeName, mimeType); } catch (Exception e) { <span class="hljs-keyword">Log</span><span class="hljs-built_in">.</span>e(<span class="hljs-built_in">TAG</span>, <span class="hljs-string">"bad path "</span> <span class="hljs-subst">+</span> path <span class="hljs-subst">+</span> <span class="hljs-string">" in scanFile()"</span>, e); <span class="hljs-keyword">return</span> <span class="hljs-built_in">null</span>; } }</code>下一步进入到MediaScanner.java,文件在
<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/java/android/media/MediaScanner.java</code>下面是扫描单个文件的代码
<code class="hljs cs has-numbering"> <span class="hljs-comment">// this function is used to scan a single file</span> <span class="hljs-keyword">public</span> Uri <span class="hljs-title">scanSingleFile</span>(String path, String volumeName, String mimeType) { <span class="hljs-keyword">try</span> { initialize(volumeName); prescan(path, <span class="hljs-keyword">true</span>); File file = <span class="hljs-keyword">new</span> File(path); <span class="hljs-keyword">if</span> (!file.exists()) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>; } <span class="hljs-comment">// lastModified is in milliseconds on Files.</span> <span class="hljs-keyword">long</span> lastModifiedSeconds = file.lastModified() / <span class="hljs-number">1000</span>; <span class="hljs-comment">// always scan the file, so we can return the content://media Uri for existing files</span> <span class="hljs-keyword">return</span> mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), <span class="hljs-keyword">false</span>, <span class="hljs-keyword">true</span>, MediaScanner.isNoMediaPath(path)); } <span class="hljs-keyword">catch</span> (RemoteException e) { Log.e(TAG, <span class="hljs-string">"RemoteException in MediaScanner.scanFile()"</span>, e); <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>; } } <span class="hljs-comment">//初始化默认的uri</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initialize</span>(String volumeName) { mMediaProvider = mContext.getContentResolver().acquireProvider(<span class="hljs-string">"media"</span>); mAudioUri = Audio.Media.getContentUri(volumeName); mVideoUri = Video.Media.getContentUri(volumeName); mImagesUri = Images.Media.getContentUri(volumeName); mThumbsUri = Images.Thumbnails.getContentUri(volumeName); mFilesUri = Files.getContentUri(volumeName); mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter(<span class="hljs-string">"nonotify"</span>, <span class="hljs-string">"1"</span>).build(); <span class="hljs-keyword">if</span> (!volumeName.equals(<span class="hljs-string">"internal"</span>)) { <span class="hljs-comment">// we only support playlists on external media</span> mProcessPlaylists = <span class="hljs-keyword">true</span>; mProcessGenres = <span class="hljs-keyword">true</span>; mPlaylistsUri = Playlists.getContentUri(volumeName); mCaseInsensitivePaths = <span class="hljs-keyword">true</span>; } } <span class="hljs-comment">//扫描预处理,暂时不知道做什么用</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">prescan</span>(String filePath, boolean prescanFiles) throws RemoteException { Cursor c = <span class="hljs-keyword">null</span>; String <span class="hljs-keyword">where</span> = <span class="hljs-keyword">null</span>; String[] selectionArgs = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">if</span> (mPlayLists == <span class="hljs-keyword">null</span>) { mPlayLists = <span class="hljs-keyword">new</span> ArrayList<FileEntry>(); } <span class="hljs-keyword">else</span> { mPlayLists.clear(); } <span class="hljs-keyword">if</span> (filePath != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// query for only one file</span> <span class="hljs-keyword">where</span> = MediaStore.Files.FileColumns._ID + <span class="hljs-string">">?"</span> + <span class="hljs-string">" AND "</span> + Files.FileColumns.DATA + <span class="hljs-string">" like '"</span>+filePath+<span class="hljs-string">"%' "</span>; selectionArgs = <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">""</span> }; } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">where</span> = MediaStore.Files.FileColumns._ID + <span class="hljs-string">">?"</span>; selectionArgs = <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">""</span> }; } <span class="hljs-keyword">if</span> (DEBUG_MEDIASCANNER) Log.d(TAG,<span class="hljs-string">"-----------enter prescan,filePath = "</span>+filePath+<span class="hljs-string">"where = "</span>+<span class="hljs-keyword">where</span>); <span class="hljs-comment">// Tell the provider to not delete the file.</span> <span class="hljs-comment">// If the file is truly gone the delete is unnecessary, and we want to avoid</span> <span class="hljs-comment">// accidentally deleting files that are really there (this may happen if the</span> <span class="hljs-comment">// filesystem is mounted and unmounted while the scanner is running).</span> Uri.Builder builder = mFilesUri.buildUpon(); builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, <span class="hljs-string">"false"</span>); MediaBulkDeleter deleter = <span class="hljs-keyword">new</span> MediaBulkDeleter(mMediaProvider, mPackageName, builder.build()); <span class="hljs-comment">// Build the list of files from the content provider</span> <span class="hljs-keyword">try</span> { <span class="hljs-keyword">if</span> (prescanFiles) { <span class="hljs-comment">// First read existing files from the files table.</span> <span class="hljs-comment">// Because we'll be deleting entries for missing files as we go,</span> <span class="hljs-comment">// we need to query the database in small batches, to avoid problems</span> <span class="hljs-comment">// with CursorWindow positioning.</span> <span class="hljs-keyword">long</span> lastId = Long.MIN_VALUE; Uri limitUri = mFilesUri.buildUpon().appendQueryParameter(<span class="hljs-string">"limit"</span>, <span class="hljs-string">"1000"</span>).build(); mWasEmptyPriorToScan = <span class="hljs-keyword">true</span>; <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) { selectionArgs[<span class="hljs-number">0</span>] = <span class="hljs-string">""</span> + lastId; <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) { c.close(); c = <span class="hljs-keyword">null</span>; } c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION, <span class="hljs-keyword">where</span>, selectionArgs, MediaStore.Files.FileColumns._ID, <span class="hljs-keyword">null</span>); <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">int</span> num = c.getCount(); <span class="hljs-keyword">if</span> (num == <span class="hljs-number">0</span>) { <span class="hljs-keyword">break</span>; } mWasEmptyPriorToScan = <span class="hljs-keyword">false</span>; <span class="hljs-keyword">while</span> (c.moveToNext()) { <span class="hljs-keyword">long</span> rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); <span class="hljs-keyword">int</span> format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); <span class="hljs-keyword">long</span> lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); lastId = rowId; <span class="hljs-comment">// Only consider entries with absolute path names.</span> <span class="hljs-comment">// This allows storing URIs in the database without the</span> <span class="hljs-comment">// media scanner removing them.</span> <span class="hljs-keyword">if</span> (path != <span class="hljs-keyword">null</span> && path.startsWith(<span class="hljs-string">"/"</span>)) { boolean exists = <span class="hljs-keyword">false</span>; <span class="hljs-keyword">try</span> { exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK); } <span class="hljs-keyword">catch</span> (ErrnoException e1) { } <span class="hljs-keyword">if</span> (!exists && !MtpConstants.isAbstractObject(format)) { <span class="hljs-comment">// do not delete missing playlists, since they may have been</span> <span class="hljs-comment">// modified by the user.</span> <span class="hljs-comment">// The user can delete them in the media player instead.</span> <span class="hljs-comment">// instead, clear the path and lastModified fields in the row</span> MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); <span class="hljs-keyword">int</span> fileType = (mediaFileType == <span class="hljs-keyword">null</span> ? <span class="hljs-number">0</span> : mediaFileType.fileType); <span class="hljs-keyword">if</span> (!MediaFile.isPlayListFileType(fileType)) { deleter.delete(rowId); <span class="hljs-keyword">if</span> (path.toLowerCase(Locale.US).endsWith(<span class="hljs-string">"/.nomedia"</span>)) { deleter.flush(); String parent = <span class="hljs-keyword">new</span> File(path).getParent(); mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, parent, <span class="hljs-keyword">null</span>); } } } } } } } } <span class="hljs-keyword">finally</span> { <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) { c.close(); } deleter.flush(); } <span class="hljs-comment">// compute original size of images</span> mOriginalCount = <span class="hljs-number">0</span>; c = mMediaProvider.query(mPackageName, mImagesUri, ID_PROJECTION, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>); <span class="hljs-keyword">if</span> (c != <span class="hljs-keyword">null</span>) { mOriginalCount = c.getCount(); c.close(); } }</code>下面是真正的扫描实现
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> Uri <span class="hljs-title">doScanFile</span>(String path, String mimeType, <span class="hljs-keyword">long</span> lastModified, <span class="hljs-keyword">long</span> fileSize, <span class="hljs-keyword">boolean</span> isDirectory, <span class="hljs-keyword">boolean</span> scanAlways, <span class="hljs-keyword">boolean</span> noMedia) { Uri result = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// long t1 = System.currentTimeMillis();</span> <span class="hljs-keyword">try</span> { FileEntry entry = beginFile(path, mimeType, lastModified, fileSize, isDirectory, noMedia); <span class="hljs-keyword">if</span> (path.startsWith(<span class="hljs-string">"/mnt/usb_storage"</span>)) mIsUsbStorage =<span class="hljs-keyword">true</span>; <span class="hljs-keyword">else</span> mIsUsbStorage =<span class="hljs-keyword">false</span>; <span class="hljs-comment">// if this file was just inserted via mtp, set the rowid to zero</span> <span class="hljs-comment">// (even though it already exists in the database), to trigger</span> <span class="hljs-comment">// the correct code path for updating its entry</span> <span class="hljs-keyword">if</span> (mMtpObjectHandle != <span class="hljs-number">0</span>) { entry.mRowId = <span class="hljs-number">0</span>; } <span class="hljs-comment">// rescan for metadata if file was modified since last scan</span> <span class="hljs-keyword">if</span> (entry != <span class="hljs-keyword">null</span> && (entry.mLastModifiedChanged || scanAlways)) { <span class="hljs-keyword">if</span> (noMedia) { result = endFile(entry, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">false</span>); } <span class="hljs-keyword">else</span> { String lowpath = path.toLowerCase(Locale.ROOT); <span class="hljs-keyword">boolean</span> ringtones = (lowpath.indexOf(RINGTONES_DIR) > <span class="hljs-number">0</span>); <span class="hljs-keyword">boolean</span> notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > <span class="hljs-number">0</span>); <span class="hljs-keyword">boolean</span> alarms = (lowpath.indexOf(ALARMS_DIR) > <span class="hljs-number">0</span>); <span class="hljs-keyword">boolean</span> podcasts = (lowpath.indexOf(PODCAST_DIR) > <span class="hljs-number">0</span>); <span class="hljs-keyword">boolean</span> music = (lowpath.indexOf(MUSIC_DIR) > <span class="hljs-number">0</span>) || (!ringtones && !notifications && !alarms && !podcasts); <span class="hljs-keyword">boolean</span> isaudio = MediaFile.isAudioFileType(mFileType); <span class="hljs-keyword">boolean</span> isvideo = MediaFile.isVideoFileType(mFileType); <span class="hljs-keyword">boolean</span> isimage = MediaFile.isImageFileType(mFileType); <span class="hljs-keyword">if</span> (isaudio || isvideo || isimage) { <span class="hljs-keyword">if</span> (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) { <span class="hljs-comment">// try to rewrite the path to bypass the sd card fuse layer</span> String directPath = Environment.getMediaStorageDirectory() + path.substring(mExternalStoragePath.length()); File f = <span class="hljs-keyword">new</span> File(directPath); <span class="hljs-keyword">if</span> (f.exists()) { path = directPath; } } } <span class="hljs-comment">// we only extract metadata for audio and video files</span> <span class="hljs-keyword">if</span> (isaudio <span class="hljs-comment">/*|| isvideo */</span>) { <span class="hljs-comment">//jni方法调用</span> processFile(path, mimeType, <span class="hljs-keyword">this</span>); } <span class="hljs-comment">//if (isimage) {</span> <span class="hljs-comment">// processImageFile(path);</span> <span class="hljs-comment">//}</span> result = endFile(entry, ringtones, notifications, alarms, music, podcasts); } } } <span class="hljs-keyword">catch</span> (RemoteException e) { Log.e(TAG, <span class="hljs-string">"RemoteException in MediaScanner.scanFile()"</span>, e); } <span class="hljs-comment">// long t2 = System.currentTimeMillis();</span> <span class="hljs-comment">// Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));</span> <span class="hljs-keyword">return</span> result; }</code>
我们看一下英文说明
<code class="hljs applescript has-numbering"> MediaScanner.processFile(). * - MediaScanner.processFile() calls one <span class="hljs-keyword">of</span> several methods, depending <span class="hljs-function_start"><span class="hljs-keyword">on</span></span> <span class="hljs-keyword">the</span> type <span class="hljs-keyword">of</span> <span class="hljs-keyword">the</span> * <span class="hljs-type">file</span>: parseMP3, parseMP4, parseMidi, parseOgg <span class="hljs-keyword">or</span> parseWMA. * - each <span class="hljs-keyword">of</span> these methods gets metadata key/value pairs <span class="hljs-keyword">from</span> <span class="hljs-keyword">the</span> <span class="hljs-type">file</span>, <span class="hljs-keyword">and</span> repeatedly * calls native MyMediaScannerClient.handleStringTag, which calls <span class="hljs-keyword">back</span> up <span class="hljs-keyword">to</span> <span class="hljs-keyword">its</span> Java * counterparts <span class="hljs-keyword">in</span> this <span class="hljs-type">file</span>.</code>
大意就是,processFile会根据文件类型调用不用的方法,并由MyMediaScannerClient.handleStringTag回调
这段native方法可以在下面的文件目录找到
<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/jni/android_media_MediaScanner.cpp</code>
<code class="hljs lasso has-numbering">static <span class="hljs-literal">void</span> android_media_MediaScanner_processFile( JNIEnv <span class="hljs-subst">*</span>env, jobject thiz, jstring path, jstring mimeType, jobject client) { ALOGV(<span class="hljs-string">"processFile"</span>); <span class="hljs-comment">// Lock already hold by processDirectory</span> MediaScanner <span class="hljs-subst">*</span>mp <span class="hljs-subst">=</span> getNativeScanner_l(env, thiz); <span class="hljs-keyword">if</span> (mp <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) { jniThrowException(env, kRunTimeException, <span class="hljs-string">"No scanner available"</span>); <span class="hljs-keyword">return</span>; } <span class="hljs-keyword">if</span> (path <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) { jniThrowException(env, kIllegalArgumentException, <span class="hljs-built_in">NULL</span>); <span class="hljs-keyword">return</span>; } const char <span class="hljs-subst">*</span>pathStr <span class="hljs-subst">=</span> env<span class="hljs-subst">-></span>GetStringUTFChars(path, <span class="hljs-built_in">NULL</span>); <span class="hljs-keyword">if</span> (pathStr <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) { <span class="hljs-comment">// Out of memory</span> <span class="hljs-keyword">return</span>; } const char <span class="hljs-subst">*</span>mimeTypeStr <span class="hljs-subst">=</span> (mimeType <span class="hljs-subst">?</span> env<span class="hljs-subst">-></span>GetStringUTFChars(mimeType, <span class="hljs-built_in">NULL</span>) : <span class="hljs-built_in">NULL</span>); <span class="hljs-keyword">if</span> (mimeType <span class="hljs-subst">&&</span> mimeTypeStr <span class="hljs-subst">==</span> <span class="hljs-built_in">NULL</span>) { <span class="hljs-comment">// Out of memory</span> <span class="hljs-comment">// ReleaseStringUTFChars can be called with an exception pending.</span> env<span class="hljs-subst">-></span>ReleaseStringUTFChars(path, pathStr); <span class="hljs-keyword">return</span>; } MyMediaScannerClient myClient(env, client); MediaScanResult result <span class="hljs-subst">=</span> mp<span class="hljs-subst">-></span>processFile(pathStr, mimeTypeStr, myClient); <span class="hljs-keyword">if</span> (result <span class="hljs-subst">==</span> MEDIA_SCAN_RESULT_ERROR) { ALOGE(<span class="hljs-string">"An error occurred while scanning file '%s'."</span>, pathStr); } env<span class="hljs-subst">-></span>ReleaseStringUTFChars(path, pathStr); <span class="hljs-keyword">if</span> (mimeType) { env<span class="hljs-subst">-></span>ReleaseStringUTFChars(mimeType, mimeTypeStr); } }</code>mp->processFile在路径
<code class="hljs perl has-numbering">通过<span class="hljs-keyword">grep</span>搜索发现,方法定义在文件frameworks/av/media/libmedia/MediaScanner.h</code>可以看到这是一个虚函数
<code class="hljs cs has-numbering"> <span class="hljs-keyword">virtual</span> MediaScanResult processFile( <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType, MediaScannerClient &client) = <span class="hljs-number">0</span>;</code>虚函数定义如下:如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 我们把这样的函数(方法)称为纯虚函数如果一个类包含了纯虚函数,称此类为抽象类。
我们接着往下跟,找到真正实现的地方
回到前一个jni
<code class="hljs cs has-numbering">frameworks/<span class="hljs-keyword">base</span>/media/jni/android_media_MediaScanner.cpp</code>
<code class="hljs cs has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) { ALOGV(<span class="hljs-string">"native_setup"</span>); MediaScanner *mp = <span class="hljs-keyword">new</span> StagefrightMediaScanner; <span class="hljs-keyword">if</span> (mp == NULL) { jniThrowException(env, kRunTimeException, <span class="hljs-string">"Out of memory"</span>); <span class="hljs-keyword">return</span>; } env->SetIntField(thiz, fields.context, (<span class="hljs-keyword">int</span>)mp); }</code>马上就能找到实例化的地方了,打开
<code class="hljs tex has-numbering">frameworks<span class="hljs-command">\av</span><span class="hljs-command">\media</span><span class="hljs-command">\libstagefright</span><span class="hljs-command">\StagefrightMediaScanner</span>.cpp</code>
<code class="hljs cs has-numbering">MediaScanResult StagefrightMediaScanner::processFile( <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType, MediaScannerClient &client) { ALOGV(<span class="hljs-string">"processFile '%s'."</span>, path); client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); client.endFile(); <span class="hljs-keyword">return</span> result; } MediaScanResult StagefrightMediaScanner::processFileInternal( <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *path, <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *mimeType, MediaScannerClient &client) { <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *extension = strrchr(path, <span class="hljs-string">'.'</span>); <span class="hljs-keyword">if</span> (!extension) { <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_SKIPPED; } <span class="hljs-comment">//过滤类型</span> <span class="hljs-keyword">if</span> (!FileHasAcceptableExtension(extension)) { <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_SKIPPED; } <span class="hljs-comment">//根据扩展名调用不同的解析方法</span> <span class="hljs-keyword">if</span> (!strcasecmp(extension, <span class="hljs-string">".mid"</span>) || !strcasecmp(extension, <span class="hljs-string">".smf"</span>) || !strcasecmp(extension, <span class="hljs-string">".imy"</span>) || !strcasecmp(extension, <span class="hljs-string">".midi"</span>) || !strcasecmp(extension, <span class="hljs-string">".xmf"</span>) || !strcasecmp(extension, <span class="hljs-string">".rtttl"</span>) || !strcasecmp(extension, <span class="hljs-string">".rtx"</span>) || !strcasecmp(extension, <span class="hljs-string">".ota"</span>) || !strcasecmp(extension, <span class="hljs-string">".mxmf"</span>)) { <span class="hljs-keyword">return</span> HandleMIDI(path, &client); } <span class="hljs-comment">//parse ape audio file</span> <span class="hljs-keyword">if</span> (!strcasecmp(extension, <span class="hljs-string">".ape"</span>)) { <span class="hljs-keyword">return</span> parseAPE(path, client); } sp<MediaMetadataRetriever> mRetriever(<span class="hljs-keyword">new</span> MediaMetadataRetriever); <span class="hljs-keyword">int</span> fd = open(path, O_RDONLY | O_LARGEFILE); status_t status; <span class="hljs-keyword">if</span> (fd < <span class="hljs-number">0</span>) { <span class="hljs-comment">// couldn't open it locally, maybe the media server can?</span> status = mRetriever->setDataSource(path); } <span class="hljs-keyword">else</span> { status = mRetriever->setDataSource(fd, <span class="hljs-number">0</span>, <span class="hljs-number">0x7ffffffffffffff</span>L); close(fd); } <span class="hljs-keyword">if</span> (status) { <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_ERROR; } <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *<span class="hljs-keyword">value</span>; <span class="hljs-keyword">if</span> ((<span class="hljs-keyword">value</span> = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { status = client.setMimeType(<span class="hljs-keyword">value</span>); <span class="hljs-keyword">if</span> (status) { <span class="hljs-keyword">return</span> MEDIA_SCAN_RESULT_ERROR; } }</code>
client.endFile()这个方法的实现在
<code class="hljs tex has-numbering">frameworks<span class="hljs-command">\av</span><span class="hljs-command">\media</span><span class="hljs-command">\libmedia</span><span class="hljs-command">\MediaScannerClient</span>.cpp</code>
<code class="hljs cpp has-numbering"><span class="hljs-keyword">void</span> MediaScannerClient::endFile() { <span class="hljs-keyword">if</span> (mLocaleEncoding != kEncodingNone) { <span class="hljs-keyword">int</span> size = mNames->size(); uint32_t encoding = kEncodingAll; <span class="hljs-comment">// compute a bit mask containing all possible encodings</span> <span class="hljs-keyword">char</span>* isUtf8 = <span class="hljs-keyword">new</span> <span class="hljs-keyword">char</span>[size]; <span class="hljs-built_in">memset</span>(isUtf8,<span class="hljs-number">0</span>,size); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mNames->size(); i++) { <span class="hljs-comment">// ALOGE("I=%d ,names=%s , values=%s,mLocaleEncoding =0x%x \n",i,mNames->getEntry(i),mValues->getEntry(i),mLocaleEncoding);</span> uint32_t tmpEncoding = kEncodingAll; <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> * name = mNames->getEntry(i); <span class="hljs-comment">//when the system languge is russian ,the title/album/artist/must think it is win1251 .</span> <span class="hljs-keyword">if</span>((mLocaleEncoding == kEncodingWin1251 || mLocaleEncoding == kEncodingWin1252) && (!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"tit"</span>, <span class="hljs-number">3</span>)||!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"alb"</span>, <span class="hljs-number">3</span>)||!<span class="hljs-built_in">strncmp</span>(name, <span class="hljs-string">"art"</span>, <span class="hljs-number">3</span>))) tmpEncoding = kEncodingNone; <span class="hljs-keyword">else</span> tmpEncoding = possibleEncodings(mValues->getEntry(i)); <span class="hljs-keyword">if</span>(tmpEncoding == kEncodingNone) { ALOGI(<span class="hljs-string">"%s value can't discern,the value is %s"</span>,mNames->getEntry(i),mValues->getEntry(i)); <span class="hljs-keyword">if</span>( mLocaleEncoding == kEncodingWin1251 || mLocaleEncoding == kEncodingWin1252 ) { <span class="hljs-comment">//revert the string value the original string</span> int8_t ch1, ch2; int8_t *src = (int8_t *)mValues->getEntry(i); int8_t *dst = src; <span class="hljs-keyword">while</span> ((ch1 = *src++)) { <span class="hljs-keyword">if</span> (ch1 & <span class="hljs-number">0x80</span>) { ch2 = *src++; ch1 = ((ch1 << <span class="hljs-number">6</span>) & <span class="hljs-number">0xC0</span>) | (ch2 & <span class="hljs-number">0x3F</span>); } *dst++ = ch1; } *dst = <span class="hljs-string">'\0'</span>; <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> value[<span class="hljs-number">1024</span>]; <span class="hljs-built_in">memset</span>(value,<span class="hljs-number">0</span>,<span class="hljs-number">1024</span>); <span class="hljs-keyword">if</span>( mLocaleEncoding == kEncodingWin1251) Win1251ToUtf8((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)mValues->getEntry(i),<span class="hljs-built_in">strlen</span>(mValues->getEntry(i)),value,<span class="hljs-number">1024</span>); <span class="hljs-keyword">else</span> Win1252ToUtf8((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">char</span> *)mValues->getEntry(i),<span class="hljs-built_in">strlen</span>(mValues->getEntry(i)),value,<span class="hljs-number">1024</span>); <span class="hljs-comment">//ALOGI("%s value do change encode type,the value is %s",mNames->getEntry(i),value);</span> handleStringTag(name, (<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *)value); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">//if the id3 value can't discern the encoding,we can think it is a utf8</span> handleStringTag(name, mValues->getEntry(i)); } isUtf8[i] = <span class="hljs-number">0xff</span>; <span class="hljs-keyword">continue</span>; } <span class="hljs-comment">//LOGI("%s value is %x coding,the value is %s",mNames->getEntry(i),tmpEncoding,mValues->getEntry(i));</span> encoding &= tmpEncoding; } <span class="hljs-keyword">if</span>(encoding != kEncodingAll) { <span class="hljs-keyword">if</span>(encoding == kEncodingNone) encoding = mLocaleEncoding; <span class="hljs-comment">// if the locale encoding matches, then assume we have a native encoding.</span> <span class="hljs-keyword">if</span> (encoding & mLocaleEncoding) convertValues(mLocaleEncoding); <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingEUCKR) convertValues(kEncodingEUCKR); <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingGBK) convertValues(kEncodingGBK); <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingBig5) convertValues(kEncodingBig5); <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(encoding & kEncodingShiftJIS) convertValues(kEncodingShiftJIS); } <span class="hljs-comment">// finally, push all name/value pairs to the client</span> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < mNames->size(); i++) { <span class="hljs-keyword">if</span>(isUtf8[i] == <span class="hljs-number">0xff</span>) <span class="hljs-keyword">continue</span>; status_t status = handleStringTag(mNames->getEntry(i), mValues->getEntry(i)); <span class="hljs-keyword">if</span> (status) { <span class="hljs-keyword">break</span>; } } } <span class="hljs-comment">// else addStringTag() has done all the work so we have nothing to do</span> <span class="hljs-keyword">delete</span> mNames; <span class="hljs-keyword">delete</span> mValues; mNames = NULL; mValues = NULL; }</code>