Android 系統(tǒng)提供媒體庫 URI 與 數據庫的對應關系

[toc]

Android 系統(tǒng)提供媒體庫 URI 與 數據庫的對應關系

前言

在 Android 系統(tǒng)中笼吟,本地媒體(e.g. 音樂)文件會被檢索并且以 數據庫 的形式進行保存管理,在開發(fā) Android 程序的時候霸旗,我們可以使用 ContentProvider 設置 uri 去獲取相關的數據[1]贷帮。
在使用 ContentProvider 組件的時候,通常的做法是繼承父類 ContentProvider诱告,然后重載父類中 inser() 撵枢、 delete()update() 精居、 query() 等方法實現對數據的操作[2]锄禽。
既然本地媒體文件在系統(tǒng)中是以 數據庫 的形式來管理,并且提供了 uri 供我們使用靴姿,那么我猜在系統(tǒng)內部應該是有個 ***Provider 的去實現對 數據庫 的操作沃但。
通過 Google 和 Baidu 找到了相關名詞——MediaProvider,并且找到了源碼[3]空猜。

MediaProvider

public class MeidaProvider extends ContentProvider {
    private static final Uri MEDIA_URI = Uri.parse("content://media");
    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");

...

}

我想這個 MeidaProvider.class 也許會給我們想要的線索绽慈,因為使用 ContentResolver 獲取本地音樂中,使用的 uricontent://media/external/audio/media 辈毯,而這個 class 也出現了類似的字段坝疼。
MeidaProvider extends ContentProvider 那么應該會 重載 ContentProvider 的相關方法以向外提供數據操作方法。而在查詢音樂數據使用的方法為 query(Uri uri, ...) 傳入一個 uri 谆沃,所以先查看 query( ) 的內容钝凶。

query(Uri uri, ...)

//MediaProvider.class:1813
   public Cursor query(Uri uri, String[] projectionIn, String selection, String[] selectionArgs, String sort) {
        int table = URI_MATCHER.match(uri);
        
        ...

        String groupBy = null;
        DatabaseHelper helper = getDatabaseForUri(uri);

        ...

        switch (table) {

            ...
            
            case AUDIO_MEDIA:
                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
                          || selection.equalsIgnoreCase("is_podcast=1") )
                        && projectionIn[0].equalsIgnoreCase("count(*)")
                        && keywords != null) {
                    //Log.i("@@@@", "taking fast path for counting songs");
                    qb.setTables("audio_meta");
                } else {
                    qb.setTables("audio");
                    for (int i = 0; keywords != null && i < keywords.length; i++) {
                        if (i > 0) {
                            qb.appendWhere(" AND ");
                        }
                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
                        prependArgs.add("%" + keywords[i] + "%");
                    }
                }
                break;
            case AUDIO_MEDIA_ID:
                qb.setTables("audio");
                qb.appendWhere("_id=?");
                prependArgs.add(uri.getPathSegments().get(3));
                break;

            ...

    }

//MediaProvider.class:4716
 static
    {
        ...
        
        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);

        ...
    }

從以上代碼可以看出,在 query( ) 中唁影,使用 URI_MATCHER.match(uri) 對傳入的 uri 進行解析耕陷,然后在 switch( )setTables( ) 設置對應的表或者視圖。

getDatabaseForUri(uri);

//MediaProvider.java:4456
private DatabaseHelper getDatabaseForUri(Uri uri) {
    synchronized (mDatabases) {
        if (uri.getPathSegments().size() >= 1) {
                return mDatabases.get(uri.getPathSegments().get(0));
            }
        }
        return null;
    }

//MediaProvider.java:4490
private Uri attachVolume(String volume) {
    //將 “db” 路徑判斷存入 “mDatabases”
}

MediaProvider.onCrearte() 被創(chuàng)建啟動的時候會查看是否有外置存儲器据沈,并執(zhí)行 attachVolume() 方法(:508)哟沫,將內外置存儲器中的數據庫路徑存入 mDatabases

Uri

通用資源標識符(Uniform Resource Identifier)[2]

URI是一個用于標識某一互聯網資源名稱的字符串锌介。 該種標識允許用戶對任何(包括本地和互聯網)的資源通過特定的協(xié)議進行交互操作嗜诀。在ContentProvider機制中,使用ContentResolver對象通過URI定位ContentProvider提供的資源孔祸。
ContentProvider使用的URI語法結構如下:

content://<authority>/<data_path>/<id>
  • content:// 是通用前綴隆敢,表示該UIR用于ContentProvider定位資源。
  • < authority > 是授權者名稱崔慧,用來確定具體由哪一個ContentProvider提供資源拂蝎。因此一般< authority >都由類的小寫全稱組成,以保證唯一性惶室。
  • < data_path > 是數據路徑温自,用來確定請求的是哪個數據集玄货。如果ContentProvider近提供一個數據集,數據路徑則可以省略捣作;如果ContentProvider提供多個數據集誉结,數據路徑必須指明具體數據集。數據集的數據路徑可以寫成多段格式券躁,例如people/girl和people/boy惩坑。
  • < id > 是數據編號,用來唯一確定數據集中的一條記錄也拜,匹配數據集中_ID字段的值以舒。如果請求的數據不只一條,< id >可以省略慢哈。

如請求整個people數據集的URI為:

content://com.example.peopleprovider/people

而請求people數據集中第3條數據的URI則應寫為:

content://com.example.peopleprovider/people/3

URI_MATCHER.addURI( );

Add a URI to match, and the code to reutrn when this URI is matched. URI nodes may be exact match string, the token "*" that matches any text, or the token "#" that matches only numbers.[4]

//MediaProvider.class:4716
 static
    {
        ...
        
        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);

        ...
    }

添加 uri 匹配對應關系蔓钟。
第二個參數 "*/audio/media" 中的 "*" 給 internalexternal 預留位置,用于指明訪問的數據庫位于 內置存儲器 或是 外置存儲器 卵贱。

URI_MATCHER.match(uri);

源碼使用了 UriMatch 對傳入的 uri 進行匹配滥沫。

總結

MediaProvider 本質上就是一個 Provider ,用過 ContentProvider 去理解應該不難键俱。

附:MediaStore Uri 與 數據庫對應表(僅供參考)

URI (content://media/external/audio/) Table \ View (external.db)
media (specific) audio_meta
media (all) & media/# audio
media/#/genres & media/#/genres/# audio_genres
media/#/playlists & media/#/playlists/# audio_playlists
genres & genres/# audio_genres
genres/#/members audio_genres_map_noid
genres/all/members audio_genres_map_noid
playlists & playlists/# audio_playlists
playlists/#/members & playlists/#/members/# audio_playlists_map
artists (specific) audio_meta
artists (all) & artists/# artist_info
artists/#/albums [多張表聯合]
albums (specific) audio_meta
albums (all) & albums/# album_info
albumart/# album_art

P. S. MediaProvider.class 比較大兰绣,下載下來之后,放到 Android Studio 上可以方便查看代碼编振。

AS

'external.db'路徑:/data/data/com.android.providers.media/databases/


  1. Android中利用ContentResolver獲取本地音樂和相片 ?

  2. Android ContentProvider 完全解析及簡單DEMO ? ?

  3. MeidaProvider 源碼(需梯子) ?

  4. UriMatcher | Android Developers ?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末缀辩,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子踪央,更是在濱河造成了極大的恐慌臀玄,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畅蹂,死亡現場離奇詭異健无,居然都是意外死亡,警方通過查閱死者的電腦和手機液斜,發(fā)現死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門睬涧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人旗唁,你說我怎么就攤上這事”允” “怎么了检疫?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祷嘶。 經常有香客問我屎媳,道長夺溢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任烛谊,我火速辦了婚禮风响,結果婚禮上,老公的妹妹穿的比我還像新娘丹禀。我一直安慰自己状勤,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布双泪。 她就那樣靜靜地躺著持搜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪焙矛。 梳的紋絲不亂的頭發(fā)上葫盼,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音村斟,去河邊找鬼贫导。 笑死,一個胖子當著我的面吹牛蟆盹,可吹牛的內容都是我干的孩灯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼日缨,長吁一口氣:“原來是場噩夢啊……” “哼钱反!你這毒婦竟也來了?” 一聲冷哼從身側響起匣距,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤面哥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后毅待,有當地人在樹林里發(fā)現了一具尸體尚卫,經...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年尸红,在試婚紗的時候發(fā)現自己被綠了吱涉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡外里,死狀恐怖怎爵,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情盅蝗,我是刑警寧澤鳖链,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站墩莫,受9級特大地震影響芙委,放射性物質發(fā)生泄漏逞敷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一灌侣、第九天 我趴在偏房一處隱蔽的房頂上張望推捐。 院中可真熱鬧,春花似錦侧啼、人聲如沸牛柒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焰络。三九已至,卻和暖如春符喝,著一層夾襖步出監(jiān)牢的瞬間闪彼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工协饲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留畏腕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓茉稠,卻偏偏與公主長得像描馅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子而线,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內容

  • 1.什么是ContentProvider 首先铭污,ContentProvider(內容提供者)是android中的四...
    秦越人87閱讀 3,837評論 0 8
  • 2017年5月17日 Kylin_Wu 標注(★☆)為考綱明確給出考點(必考) 常見手機系統(tǒng)(★☆) And...
    Azur_wxj閱讀 1,815評論 0 10
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,190評論 25 707
  • 其實每個人心中都有一座城池俱萍,有的人知道饶氏,有的人不知道,藏的最深磺樱,護的最周全誓竿,最重要磅网,也最脆弱。 我心中的城池是中國...
    程煜閱讀 261評論 0 1
  • 木心說筷屡,從前的日色變得慢 涧偷,車,馬毙死,郵件都慢燎潮,一生只夠愛一個人。 偶然讀到這樣的句子扼倘,我是極有感觸的跟啤,我也曾期許一...
    啦啦啦楊大大閱讀 277評論 0 0