田海立@csdn
2012-05-19
本文分析MediaScanner對單個檔案的掃描過程。單個檔案的掃描是MediaScanner的基礎,對路徑的掃描也要用到對Media檔案的掃描。本文從MediaScannerService的scanFile入口開始,詳細分析了MediaScanner和MediaScannerClient對單個媒體檔案的掃描處理過程。
一、MediaScannerService.scanFile()
上文對MediaScannerService的分析,知道對單個檔案的掃描是調用MediaScannerService.scanFile()完成的。下面看scanFile()的實現:
scanFile()中判斷如果是外部媒體檔案(只掃描外部媒體檔案),建立MediaScanner(定義在frameworks/base/media/java/android/media)執行個體,並設定locale資訊,然後調用MediaScanner的scanSingleFile()開始掃描。
二、MediaScanner.scanSingleFile()
MediaScanner.scanSingleFile()是具體的實現。看它完成的工作:
順序執行了
- initialize(); 3.1節中講解該方法的工作;
- prescan(); 3.2節中講解該方法的工作;
- MyMediaScannerClient.doScanFile() 3.3節中講解該方法的工作。
下面分章節著重講解這些方法裡面都做了哪些工作。
三、MediaScanner
位於/framework/base/media/java/android/media/。
3.1 MediaScanner.initialize(volumeName: String)
initialize()對MediaScanner的MediaProvider/Audio/Video/Image等媒體庫的URI進行初始化擷取,要擷取的屬性有下面這些:
另外,如果掃描的是外部volume,要處理playlist和genre,所以mProcessPlaylists和mProcessGenres被設定為true;建立mGenreCache;擷取Genres和playlists的URI。
3.2 MediaScanner.prescan()
- 從Audio、Video和Image各自的資料庫中分別讀取出這些媒體檔案資訊(ID,Path, Modified time),寫入mFileCache: HashMap<String,FileCacheEntry>中;
- 從Playlist資料庫中讀取出播放清單檔案資訊(ID,Path, Modified time),寫入mFileCache: HashMap<String,FileCacheEntry>中;
注意:新建立的FileCacheEntry的mSeenInFileSystem預設值為false。
3.3 MyMediaScannerClient.doScanFile()
MyMediaScannerClient是MediaScanner的內部類,實現了MediaScannerClient。
MyMediaScannerClient提供了doScanFile()方法供外部調用。另外實現了MediaScannerClient這個Interface,這個Interface在JNI實現中非常重要,講到那裡時再詳細闡述。
- doScanFile()中調用beginFile(),擷取FileType和MimeType資訊,並初始化內部結構;
- 如果返回了FileCacheEntry,並且不是Image檔案,調用JNI方法processFile()解析到具體的檔案資訊在MyMediaScannerClient中;
- 調用endFile()做後續的媒體庫更新等處理。
3.3.1 beginFile()
參數:path:String; mimeType: String; lastModified: long, fileSize: long
- 用MediaFile擷取FileType和MimeType,並分別賦值給mFileType和mMimeType;
- 從mFileCache中擷取(檔案已經在資料庫中了:3.2preScan()中已經把資料庫中的檔案都寫入到mFileCache),或產生新的entry:FileCacheEntry,加入到mFileCache中;
- 設定entry:FileCacheEntry的屬性mSeenInFileSystem為 true【掃描到了該檔案】;
- 如果是播放清單檔案,加入到mPlaylists:ArrayList<FileCacheEntry>中,並直接返回null;
- 為MyMediaScannerClient中的各個屬性(mArtist/ mAlbum / Artist / mAlbum / mTitle / mComposer / mGenre / mTrack / mYear /mDuration / mWriter / mCompliation)賦初值,為mPath / mLastModified賦值。返回 2)產生的FileCacheEntry的執行個體。
3.3.2 processFile()
JNI調用。詳細分析在文//TODO中。
3.3.3 endFile(entry: FileCacheEntry, …)
- 1) 根據mFileType(Audio/Video/Images),選擇不同的資料庫,並把相應的URI賦值給entry.mTableUri;
- 2) 寫入values:ContentValues資訊
- ①分別用MediaScannerClient中的下面各個屬性值,初始化values:
- MediaStore.MediaColumns.DATA: mPath
- MediaStore.MediaColumns.TITLE: mTitle
- MediaStore.MediaColumns.DATE_MODIFIED: mLastModifed
- MediaStore.MediaColumns.SIZE: mFileSize
- MediaStore.MediaColumns.MIME_TYPE: mMimeType
- 對於Video檔案
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.DURATION: mDuration
o MediaStore.MediaColumns.ARTIST: mArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM_ARTIST: mAlbumArtist/MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.ALBUM: mAlbum/ MediaStore.UNKNOWN_STRING
o MediaStore.MediaColumns.COMPOSER: mComposer
o MediaStore.MediaColumns.YEAR: mYear
o MediaStore.MediaColumns.TRACK: mTrack
o MediaStore.MediaColumns.DURATION: mDuration
o MediaStore.MediaColumns.COMPILATION: mCompilation
- ②對Title和Album做特殊處理
- 如果Title為空白,從MediaStore.MediaColumns.DATA:mPath 中分離出檔案名稱(不含路徑和尾碼名),賦到value的MediaStore.MediaColumns.TITLE裡;
- 如果Album為MediaStore.UNKNOWN_STRING也就是檔案中不含有Album資訊,把MediaStore.MediaColumns.DATA:mPath 的父目錄名賦到value的MediaStore.MediaColumns.ALBUM裡。
新加入的Audio,寫入下列資訊:
- MediaStore.Audio.Media.IS_RINGTONE: ringtones
- MediaStore.Audio.Media.IS_NOTIFICATION: notifications
- MediaStore.Audio.Media.IS_ALARM: alarms
- MediaStore.Audio.Media.IS_MUSIC: music
- MediaStore.Audio.Media.IS_PODCAST: podcasts
對於JPG檔案,用android.media.ExifInterface獲得下列資訊:
- MediaStore.Images.Media.LATITUDE;
- MediaStore.Images.Media.LONGTITUDE;
- MediaStore.Images.Media.DATE_TAKEN: gps dateTime;
- MediaStore.Images.Media.ORIENTATION。
- 3) 用2)中的values添加或更新1) 中URI指向的Audio/Video/Images資料庫。
- 4) 如果要處理Genres(判斷標誌mProcessGenres,外部媒體都要處理),
- 用Genre的名字mGenre查詢Genres的資料庫,更新或插入一條由把mGenre賦給MediaStore.Audio.Genres.NAME欄位組成的記錄到MediaStore.Audio.Genres資料庫中,並返回URI;
- 把 {genre, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY}加入mGenreCache:HashMap<String, Uri>中;
- 把當前檔案所在媒體庫中的rowId賦值給MediaStore.Audio.Genres.Members.AUDIO_ID欄位,組成一條記錄插入到MediaStore.Audio.Genres.Members資料庫中。
更新一個媒體檔案所屬的Genre,需要更新Genres資料庫,和Genres資料庫對應的記錄所在的Genres.Members資料庫。
- 5) 對notification/ringtone/alarm進行處理
如果是notification/ringtone/alarm,並且還未設定notification/ringtone/alarm,那麼設定Settings.System的Settings.System.NOTIFICATION_SOUND/ RINGTONE / ALARM_ALERT為媒體資料庫的Uri形式。
- 6) 返回媒體資料庫的Uri。