Android读取本地照片和视频相册实例代码

前言

项目中经常要选择本地照片或者视频的需求,如果去扫描整个SD卡就太耗时间,其实Android系统在启动时就已经把整个设备中的多媒体文件信息(文件名,类型,大小等)都存到了数据库,然后提供了ContentPrivider这个API来管理这个数据库,我们可以利用ContentPrivider来获取所有的照片和视频。

ContentPrivider初识

先看下管理的的数据库在哪

data/data/目录下:有很多这种文件夹(日历,联系人,下载管理,多媒体等)


我们需要的照片和视频就在media下面,进去看看。进去找到database然后打开external.db,就可以看到多张表(音频,文件,Log,图像,视频等)

照片相册

那么获取照片直接通过 ContentProvider读取Images这个数据库就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();
      //存储对应关系
      if (allPhotostemp.containsKey(dirPath)) {
       List<MediaBean> data = allPhotostemp.get(dirPath);
       data.add(new MediaBean(MediaBean.Type.Image,displayName));
       continue;
      } else {
       List<MediaBean> data = new ArrayList<>();
       data.add(new MediaBean(MediaBean.Type.Image,displayName));
       allPhotostemp.put(dirPath,data);
      }
     }
     mCursor.close();
    }
    //更新界面
    runOnUiThread(new Runnable() {
     @Override
     public void run() {
      //...
     }
    });
   }
  }).start();
 }

有四点需要注意:

  1. MediaBean是文件实体类代码就不贴了
  2. 照片集合不是放在List<MediaBean>这样存储的,而是HashMap<String,List<MediaBean>>,这样把图片文件夹(也就是父目录)分类,更节省内存,其次支持相册展示不同文件夹的照片
  3. 貌似没办法获取当前设备的拍照认路径,有的设备是/DCIM,有的是/100andro还有/camera,那相册就认展示最近所有照片吧。然后给用户列出一个文件夹列表让他选,这时可以把这几个文件夹放到最前面展示,算是小优化吧。
  4. 系统会时刻检测数据变化,有新的照片这个数据库自动更新,不需干预。

视频相册

获取视频文件和上面基本一样,不过改下查询条件就行了,实际中有个问题:视频封面的获取

首先视频封面缩略图在这个videothumbnails数据库,照片缩略图在thumbnails,对应到本地SD卡就是在sdcard/DCIM/.thumbnails/文件夹(有的设备可能不同)

PS:这个文件夹是隐藏的,so你知道你的手机为何存储空间越来越小了吧,拍的照片缩略图在这儿。。。非常非常多


实际中发现读取不到新录制的视频封面,需要手动调用一个方法,来生成这个封面然后才能在videothumbnails读取到:

参考:http://stackoverflow.com/questions/27903264/how-to-get-the-video-thumbnail-path-and-not-the-bitmap

//videoId是这个视频文件数据库的ID
MediaStore.Video.Thumbnails.getThumbnail(getContentResolver(),videoId,MediaStore.Video.Thumbnails.MICRO_KIND,null);

并且这里封面和视频不在一个数据库,需要在两个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){
       //某些设备获取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(),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();
      //存储对应关系
      if (allPhotostemp.containsKey(dirPath)) {
       List<MediaBean> data = allPhotostemp.get(dirPath);
       data.add(new MediaBean(MediaBean.Type.Video,thumbPath,duration,displayName));
       continue;
      } else {
       List<MediaBean> data = new ArrayList<>();
       data.add(new MediaBean(MediaBean.Type.Video,data);
      }
     }
     mCursor.close();
    }
    //更新界面
    runOnUiThread(new Runnable() {
     @Override
     public void run() {
      //...
     }
    });
   }
  }).start();
 }

后记

其实Android已经提供叫做CursorLoader的API做这个事情,不需要手动new 工作线程,使用起来很简单有需要可以对上面代码改造。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

相关文章

Android性能优化——之控件的优化 前面讲了图像的优化,接下...
前言 上一篇已经讲了如何实现textView中粗字体效果,里面主要...
最近项目重构,涉及到了数据库和文件下载,发现GreenDao这个...
WebView加载页面的两种方式 一、加载网络页面 加载网络页面,...
给APP全局设置字体主要分为两个方面来介绍 一、给原生界面设...
前言 最近UI大牛出了一版新的效果图,按照IOS的效果做的,页...