当前位置 主页 > 服务器问题 > Linux/apache问题 >

    AndroidQ 沙箱适配多媒体文件(小结)

    栏目:Linux/apache问题 时间:2019-11-27 09:52

    综述

    所有内容的访问变化见下图:

    外部媒体文件的扫描,读取和写入

    最容易被踩坑的应该是,对外部媒体文件,照片,视频,图片的读取或写入。

    扫描

    首先是扫描。扫描依然是使用 query MediaStore 的方式。一句话介绍 MediaStore,MediaStore 就是Android系统中的一个多媒体数据库。代码如下图所示,以搜索本地视频为例子:

    protected List<VideoInfo> doInBackground(Void... params) {
      mContentResolver = context.getContentResolver();
    
      String[] mediaColumns = { MediaStore.Video.Media._ID, MediaStore.Video.Media.DATA,
          MediaStore.Video.Media.TITLE, MediaStore.Video.Media.MIME_TYPE,
          MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.SIZE,
          MediaStore.Video.Media.DATE_ADDED, MediaStore.Video.Media.DURATION,
          MediaStore.Video.Media.WIDTH, MediaStore.Video.Media.HEIGHT };
    
      Cursor mCursor = mContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns,
          null, null, MediaStore.Video.Media.DATE_ADDED);
    
    
      if (mCursor == null) {
        return null;
      }
    
      // 注意,DATA 数据在 Android Q 以前代表了文件的路径,但在 Android Q上该路径无法被访问,因此没有意义。
      ixData = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
      ixMime = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE);
      // ID 是在 Android Q 上读取文件的关键字段
      ixId = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
      ixSize = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE);
      ixTitle = mCursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
    
      allImages = new ArrayList<VideoInfo>();
      mTotalVideoCount = 0;
    
      mCursor.moveToLast();
      
      while (mCursor.moveToPrevious()) {
        if (addVideo(mCursor) == 0) {
          continue;
        } else if (addVideo(mCursor) == 1) {
          break;
        }
      }
    
      mCursor.close();
      
      return allImages;
    }

    既然 data 不可用,就需要知晓 id 的使用方式,首先是使用 id 拼装出 content uri ,如下所示:

    public getRealPath(String id) {
      return MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build().toString();
    }

    Image 同理换成 MediaStore.Images。

    读取和写入

    其次,是读取 content uri。这里需要注意 File file = new File(contentUri); 是无法获取到文件的。file.exist() 为 false。

    那么就产生两个问题:1. 如何确定 ContentUri 形式的文件存在 2. 如何读取或写入文件。

    首先,对于 Content Uri 的读取,必须借助于 ContentResolver。

    其次,对于 1,没有找到 Google 文档中提供比较容易的API,只能采用打开 FileDescriptor 是否成功的形式,代码如下所示:

    public boolean isContentUriExists(Context context, Uri uri) {
      if (null == context) {
        return false;
      }
      ContentResolver cr = context.getContentResolver();
      try {
        AssetFileDescriptor afd = cr.openAssetFileDescriptor(uri, "r");
        if (null == afd) {
          iterator.remove();
        } else {
          try {
            afd.close();
          } catch (IOException e) {
          }
        }
      } catch (FileNotFoundException e) {
        return false;
      }
    
      return true;
    }