Android讀取本地照片和視頻相冊

前言

項目中經(jīng)常要選擇本地照片或者視頻的需求附帽,如果去掃描整個SD卡就太耗時間莲蜘,其實Android系統(tǒng)在啟動時就已經(jīng)把整個設(shè)備中的多媒體文件信息(文件名璃饱,類型脊凰,大小等)都存到了數(shù)據(jù)庫咽袜,然后提供了ContentPrivider這個API來管理這個數(shù)據(jù)庫雏掠,我們可以利用ContentPrivider來獲取所有的照片和視頻下愈。

ContentPrivider初識

先看下管理的的數(shù)據(jù)庫在哪
data/data/目錄下:有很多這種文件夾(日歷矫钓,聯(lián)系人毡琉,下載管理铁瞒,多媒體等)

image.png

我們需要的照片和視頻就在media下面,進去看看
進去找到database然后打開external.db桅滋,就可以看到多張表(音頻慧耍,文件,Log丐谋,圖像芍碧,視頻等)

image.png

照片相冊

那么獲取照片直接通過 ContentProvider讀取Images這個數(shù)據(jù)庫就OK了,這里開啟工作線程讀取所有.jpeg和.png的圖片号俐,附上代碼段:

    /**
     * 讀取手機中所有圖片信息
     */
    private void getAllPhotoInfo() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<MediaBean> mediaBeen = new ArrayList<>();
                HashMap<String,List<MediaBean>> allPhotosTemp = new HashMap<>();//所有照片
                Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                String[] projImage = { MediaStore.Images.Media._ID
                        , MediaStore.Images.Media.DATA
                        ,MediaStore.Images.Media.SIZE
                        ,MediaStore.Images.Media.DISPLAY_NAME};
                Cursor mCursor = getContentResolver().query(mImageUri,
                        projImage,
                        MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
                        new String[]{"image/jpeg", "image/png"},
                        MediaStore.Images.Media.DATE_MODIFIED+" desc");
 
                if(mCursor!=null){
                    while (mCursor.moveToNext()) {
                        // 獲取圖片的路徑
                        String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
                        int size = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE))/1024;
                        String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
                        //用于展示相冊初始化界面
                        mediaBeen.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));
                        // 獲取該圖片的父路徑名
                        String dirPath = new File(path).getParentFile().getAbsolutePath();
                        //存儲對應關(guān)系
                        if (allPhotosTemp.containsKey(dirPath)) {
                            List<MediaBean> data = allPhotosTemp.get(dirPath);
                            data.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));
                            continue;
                        } else {
                            List<MediaBean> data = new ArrayList<>();
                            data.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));
                            allPhotosTemp.put(dirPath,data);
                        }
                    }
                    mCursor.close();
                }
                //更新界面
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //...
                    }
                });
            }
        }).start();
    }

有四點需要注意:

  • MediaBean是文件實體類泌豆,代碼就不貼了
  • 照片集合不是放在List<MediaBean>這樣存儲的,而是HashMap<String,List<MediaBean>>吏饿,這樣把圖片已文件夾(也就是父目錄)分類踪危,更節(jié)省內(nèi)存蔬浙,其次支持相冊展示不同文件夾的照片
  • 貌似沒辦法獲取當前設(shè)備的拍照默認路徑,有的設(shè)備是/DCIM贞远,有的是/100andro還有/camera畴博,那相冊就默認展示最近所有照片吧。然后給用戶列出一個文件夾列表讓他選蓝仲,這時可以把這幾個文件夾放到最前面展示绎晃,算是小優(yōu)化吧。
  • 系統(tǒng)會時刻檢測數(shù)據(jù)變化杂曲,有新的照片這個數(shù)據(jù)庫會自動更新庶艾,不需干預。
    看下相冊效果
image.png

視頻相冊

獲取視頻文件和上面基本一樣擎勘,不過改下查詢條件就行了咱揍,實際中有個問題:視頻封面的獲取。
首先視頻封面縮略圖在這個videothumbnails數(shù)據(jù)庫棚饵,照片縮略圖在thumbnails煤裙,對應到本地SD卡就是在sdcard/DCIM/.thumbnails/文件夾(有的設(shè)備可能不同)
PS:這個文件夾是隱藏的,so你知道你的手機為何存儲空間越來越小了吧噪漾,拍的照片縮略圖全在這兒硼砰。。欣硼。非常非常多

image.png
//videoId是這個視頻文件在數(shù)據(jù)庫的ID
 MediaStore.Video.Thumbnails.getThumbnail(getContentResolver(), videoId, MediaStore.Video.Thumbnails.MICRO_KIND, null);
  • 并且這里封面和視頻不在一個數(shù)據(jù)庫诈胜,需要在兩個cursor來讀取
    我這里獲取整個SD的mp4格式視頻豹障,代碼段如下:
    /**
     * 獲取手機中所有視頻的信息
     */
    private void getAllVideoInfos(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                HashMap<String,List<MediaBean>> allPhotosTemp = new HashMap<>();//所有照片
                Uri mImageUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                String[] proj = { MediaStore.Video.Thumbnails._ID
                        , MediaStore.Video.Thumbnails.DATA
                        ,MediaStore.Video.Media.DURATION
                        ,MediaStore.Video.Media.SIZE
                        ,MediaStore.Video.Media.DISPLAY_NAME
                        ,MediaStore.Video.Media.DATE_MODIFIED};
                Cursor mCursor = getContentResolver().query(mImageUri,
                        proj,
                        MediaStore.Video.Media.MIME_TYPE + "=?",
                        new String[]{"video/mp4"},
                        MediaStore.Video.Media.DATE_MODIFIED+" desc");
                if(mCursor!=null){
                    while (mCursor.moveToNext()) {
                        // 獲取視頻的路徑
                        int videoId = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Video.Media._ID));
                        String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Video.Media.DATA));
                        int duration = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Video.Media.DURATION));
                        long size = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Video.Media.SIZE))/1024; //單位kb
                        if(size<0){
                            //某些設(shè)備獲取size<0,直接計算
                            Log.e("dml","this video size < 0 " + path);
                            size = new File(path).length()/1024;
                        }
                        String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME));
                        long modifyTime = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Video.Media.DATE_MODIFIED));//暫未用到

                        //提前生成縮略圖焦匈,再獲妊:http://stackoverflow.com/questions/27903264/how-to-get-the-video-thumbnail-path-and-not-the-bitmap
                        MediaStore.Video.Thumbnails.getThumbnail(getContentResolver(), videoId, MediaStore.Video.Thumbnails.MICRO_KIND, null);
                        String[] projection = { MediaStore.Video.Thumbnails._ID, MediaStore.Video.Thumbnails.DATA};
                        Cursor cursor = getContentResolver().query(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI
                                , projection
                                , MediaStore.Video.Thumbnails.VIDEO_ID + "=?"
                                , new String[]{videoId+""}
                                , null);
                        String thumbPath = "";
                        while (cursor.moveToNext()){
                            thumbPath = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Thumbnails.DATA));
                        }
                        cursor.close();
                        // 獲取該視頻的父路徑名
                        String dirPath = new File(path).getParentFile().getAbsolutePath();
                        //存儲對應關(guān)系
                        if (allPhotosTemp.containsKey(dirPath)) {
                            List<MediaBean> data = allPhotosTemp.get(dirPath);
                            data.add(new MediaBean(MediaBean.Type.Video,path,thumbPath,duration,size,displayName));
                            continue;
                        } else {
                            List<MediaBean> data = new ArrayList<>();
                            data.add(new MediaBean(MediaBean.Type.Video,path,thumbPath,duration,size,displayName));
                            allPhotosTemp.put(dirPath,data);
                        }
                    }
                    mCursor.close();
                }
                //更新界面
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //...
                    }
                });
            }
        }).start();
    }

后記

其實Android已經(jīng)提供叫做CursorLoader的API做這個事情,不需要手動new 工作線程缓熟,使用起來很簡單有需要可以對上面代碼改造累魔。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市够滑,隨后出現(xiàn)的幾起案子垦写,更是在濱河造成了極大的恐慌,老刑警劉巖版述,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梯澜,死亡現(xiàn)場離奇詭異,居然都是意外死亡渴析,警方通過查閱死者的電腦和手機晚伙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俭茧,“玉大人咆疗,你說我怎么就攤上這事∧刚” “怎么了午磁?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毡们。 經(jīng)常有香客問我迅皇,道長,這世上最難降的妖魔是什么衙熔? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任登颓,我火速辦了婚禮,結(jié)果婚禮上红氯,老公的妹妹穿的比我還像新娘框咙。我一直安慰自己,他們只是感情好痢甘,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布喇嘱。 她就那樣靜靜地躺著,像睡著了一般塞栅。 火紅的嫁衣襯著肌膚如雪者铜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天放椰,我揣著相機與錄音王暗,去河邊找鬼。 笑死庄敛,一個胖子當著我的面吹牛俗壹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藻烤,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼绷雏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怖亭?” 一聲冷哼從身側(cè)響起涎显,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兴猩,沒想到半個月后期吓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡倾芝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年讨勤,在試婚紗的時候發(fā)現(xiàn)自己被綠了箭跳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡潭千,死狀恐怖谱姓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刨晴,我是刑警寧澤屉来,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站狈癞,受9級特大地震影響茄靠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝶桶,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一慨绳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧莫瞬,春花似錦儡蔓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旁振,卻和暖如春获询,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拐袜。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工吉嚣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹬铺。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓尝哆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甜攀。 傳聞我的和親對象是個殘疾皇子秋泄,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容