赞
踩
Android Q版本出来也有一段时间了,但是大部分我们都没有去适配过它,首选说一下Android Q版,最大的亮点集中在隐私安全和智能交互两方面,其中在隐私安全方面Android Q增加了外部存储策略变更、位置权限的后台访问限制、后台应用(不限于摄像头、麦克风等)的启动限制、设备识别码限制权限收敛举措,其将限制应用在安卓设备上接受通话日志和短信权限的能力,并不再通过安卓通讯录API提供联系人互动数据,这些举措都是针对社交属性的权限进行的。这里说再多的解释还不如大家去官方文档看的更加详细,我这里只记录下自已适配Q版有关于读取系统多媒体文件的心得(参考)。
内部存储访问变化:
多媒体文件(图片,视频等)
1.使用MediaStore来获取图片和视频,然后通过Cursor来得到多媒体文件的相关信息
``MediaStore.Images.Media``表示媒体文件为图片
``MediaStore.Images.Media``表示媒体文件为视频
图片:
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.Thumbnails.DATA }; //全部图片 String where = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?"; //指定格式 String[] whereArgs = {"image/jpeg", "image/png", "image/jpg"}; //查询 Cursor mCursor = activity.getContentResolver().query( mImageUri, projection, null, null, null); if (mCursor != null) { while (mCursor.moveToNext()) { // 获取图片的路径 int thumbPathIndex = mCursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA); int timeIndex = mCursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED); int pathIndex = mCursor.getColumnIndex(MediaStore.Images.Media.DATA); int id = mCursor.getColumnIndex(MediaStore.Images.Media._ID); Long date = mCursor.getLong(timeIndex)*1000; String filepath,thumbPath; //适配Android Q if(Build.VERSION.SDK_INT == Build.VERSION_CODES.P){ thumbPath =MediaStore.Images.Media .EXTERNAL_CONTENT_URI .buildUpon() .appendPath(String.valueOf(mCursor.getInt(id))).build().toString(); filepath = thumbPath; if(FileManagerUtils.isContentUriExists(MSApplication.getmContext(), Uri.parse(filepath))){ MediaData fi = new MediaData(id,MediaConstant.IMAGE,filepath,"",getPhotoUri(mCursor),date,"",false); mediaBeen.add(fi); } }else{ thumbPath = mCursor.getString(thumbPathIndex); filepath = mCursor.getString(pathIndex); //判断文件是否存在,存在才去加入 boolean b = FileManagerUtils.fileIsExists(filepath); if (b) { File f = new File(filepath); MediaData fi = new MediaData(id,MediaConstant.IMAGE,filepath,thumbPath,null,date,f.getName(),false); mediaBeen.add(fi); } } } mCursor.close(); }
视频:
final List<MediaData> videoList = new ArrayList<>(); Uri mVideoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.Video.Thumbnails._ID , MediaStore.Video.Thumbnails.DATA , MediaStore.Video.Media.DURATION , MediaStore.Video.Media.SIZE , MediaStore.Video.Media.DATE_ADDED , MediaStore.Video.Media.DISPLAY_NAME , MediaStore.Video.Media.DATE_MODIFIED}; //全部视频 String where = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=? or " + MediaStore.Video.Media.MIME_TYPE + "=?"; String[] whereArgs = {"video/mp4", "video/3gp", "video/aiv", "video/rmvb", "video/vob", "video/flv", "video/mkv", "video/mov", "video/mpg"}; Cursor mCursor = activity.getContentResolver().query(mVideoUri, projection, null, null, MediaStore.Video.Media.DATE_ADDED + " DESC "); if (mCursor != null) { while (mCursor.moveToNext()) { // 获取视频的路径 int videoId = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Video.Media._ID)); String path; if(Build.VERSION.SDK_INT == Build.VERSION_CODES.P){ path =MediaStore.Video.Media .EXTERNAL_CONTENT_URI .buildUpon() .appendPath(String.valueOf(videoId)).build().toString(); }else{ path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Video.Media.DATA)); } long duration = mCursor.getLong(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)); //用于展示相册初始化界面 int timeIndex = mCursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED); Long date = mCursor.getLong(timeIndex) *1000; //需要判断当前文件是否存在 一定要加,不然有些文件已经不存在图片显示不出来。这里适配Android Q synchronized (activity) { boolean fileIsExists; if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) { fileIsExists = FileManagerUtils.isContentUriExists(MSApplication.getmContext(), Uri.parse(path)); if (fileIsExists) { videoList.add(new MediaData(videoId, MediaConstant.VIDEO, path, "", getVideoUri(mCursor), duration, date, displayName, false)); } } else { fileIsExists = FileManagerUtils.fileIsExists(path); if (fileIsExists) { videoList.add(new MediaData(videoId, MediaConstant.VIDEO, path, path, null, duration, date, displayName, false)); } } } } mCursor.close(); }
2.使用Content Uri
在以上代码中我们可以看到都适配了Andorid Q,这是为什么呢?因为DATA 数据在 Android Q 以前代表了文件的路径,但在 Android Q上该路径无法被访问,因此没有意义。所以既然DATA不可用,那么我们可以用ID 在 Android Q 上读取文件,即使用ID拼装出Content Uri,如1中的代码:
图片:
int id = mCursor.getColumnIndex(MediaStore.Images.Media._ID);
String thumbPath = MediaStore.Images.Media
.EXTERNAL_CONTENT_URI
.buildUpon()
.appendPath(String.valueOf(mCursor.getInt(id))).build().toString();
视频:
int videoId = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Video.Media._ID));
String path = MediaStore.Video.Media
.EXTERNAL_CONTENT_URI
.buildUpon()
.appendPath(String.valueOf(videoId)).build().toString();
3.读取和写入
public static 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) { return false; } else { try { afd.close(); } catch (IOException e) { } } } catch (FileNotFoundException e) { return false; } return true; }
这种方法最大的问题即是,对应于一个同步 I/O 调用,易造成线程等待。因此,目前对于 MediaStore 中扫描出来的文件可能不存在的情况,没有直接的好方法可以解决过滤。
public static void copy(File src, ParcelFileDescriptor parcelFileDescriptor) throws IOException { FileInputStream istream = new FileInputStream(src); try { FileOutputStream ostream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor()); try { IOUtil.copy(istream, ostream); } finally { ostream.close(); } } finally { istream.close(); } } public static void copy(ParcelFileDescriptor parcelFileDescriptor, File dst) throws IOException { FileInputStream istream = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); try { FileOutputStream ostream = new FileOutputStream(dst); try { IOUtil.copy(istream, ostream); } finally { ostream.close(); } } finally { istream.close(); } } public static void copy(InputStream ist, OutputStream ost) throws IOException { byte[] buffer = new byte[4096]; int byteCount = 0; while ((byteCount = ist.read(buffer)) != -1) { // 循环从输入流读取 buffer字节 ost.write(buffer, 0, byteCount); // 将读取的输入流写入到输出流 } }
public static Uri insertVideoIntoMediaStore(Context context, String fileName) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
contentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
Uri uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
return uri;
}
4.关于视频Video的缩略图问题,在 Android Q 上已经拿不到 Video 的 Thumbnail 路径了,又由于没有暴露 Video 的 Thumbnail 的 id ,导致了 Video 的 Thumbnail 只能使用实时获取 Bitmap 的方法,即
private Bitmap getThumbnail(ContentResolver cr, long videoId) throws Throwable {
return MediaStore.Video.Thumbnails.getThumbnail(cr, videoId, MediaStore.Video.Thumbnails.MINI_KIND,
null);
}
5.在3中我们需要通过FileInputStream 来出来文件,比如加载到ImageView,我感觉有点麻烦,所以这里我是用了Glide来加载Uri,这样也就没有和流相关的操作了。
public static Uri getPhotoUri(Cursor cursor) {
return getMediaUri(cursor, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
public static Uri getVideoUri(Cursor cursor) {
return getMediaUri(cursor, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
}
public static Uri getMediaUri(Cursor cursor, Uri uri) {
String id = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
return Uri.withAppendedPath(uri, id);
}
加载:
Glide.with(XXApplication.getmContext())
.load(thumbPathUri)
.apply(options)
.into(iv_item_image);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。