Android7.0权限适配
2017-06-14 22:38
106 查看
权限变化
Android从6.0开始对隐私的保护越来越注重,从6.0动态申请权限到Android7.0的“私有目录被限制访问”,虽然对用户保护的力度加大了,但是对于我们开发者来说,之前我们写的代码必须要适配到7.0了,很讨厌哦~下面我们具体讲一下关于7.0方方面面的变化拍照
// 请求加载系统照相机 private static final int TAKE_PICTURE = 0x000001; // 跳转到系统照相机 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //判断是否有系统相机 if(cameraIntent.resolveActivity(getActivity().getPackageManager()) != null){ // 设置系统相机拍照后的输出路径 // 创建临时文件 mTmpFile = HexAlbumFileUtils.createTmpFile(getActivity()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){//判断当前版本号 cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile)); startActivityForResult(cameraIntent, TAKE_PICTURE); }else { ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Images.Media.DATA, mTmpFile.getAbsolutePath()); Uri uri = getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } startActivityForResult(cameraIntent, TAKE_PICTURE); }else{ Toast.makeText(getActivity(), R.string.hexalbum_msg_no_camera, Toast.LENGTH_SHORT).show(); }
创建文件
public static File createTmpFile(Context context){ String state = Environment.getExternalStorageState(); if(state.equals(Environment.MEDIA_MOUNTED)){ // 已挂载 File pic = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()); String fileName = "multi_image_"+timeStamp+""; File tmpFile = new File(pic, fileName+".jpg"); return tmpFile; }else{ File cacheDir = context.getCacheDir(); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()); String fileName = "multi_image_"+timeStamp+""; File tmpFile = new File(cacheDir, fileName+".jpg"); return tmpFile; } }
在7.0版本以前,我们打开相机保存图片通常方法为:
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile)); startActivityForResult(cameraIntent, TAKE_PICTURE);
但7.0版本后,由于Android7.0执行了“StrictMode API ”的原因,” StrictMode API 政策” 是指禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,应用失败,并出现 FileUriExposedException 异常。我们可以用下面的方法来实现
ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Images.Media.DATA, mTmpFile.getAbsolutePath()); Uri uri = getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
我们也可以通过使用FileProvider来实现打开相机保存图片
第一步:创建FileProvider
在清单文件创建:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.hexfuture.hexteacher.fileprovider" android:exported="false" android:grantUriPermissions="true"> <!-- 元数据 --> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限。
第二步:指定共享的目录:
在res/xml文件下创建xml文件名字无所谓 (名字可以随便起,只要和在manifest注册的provider所引用的resource保持一致即可))file_paths.xml:
<resources> <paths> <external-path path="" name="camera_photos" /> </paths> </resources>
上述代码中path=”“,是有特殊意义的,它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了,如果你将path设为path=”pictures”,
那么它代表着共享根目录下的pictures目录(/storage/emulated/0/pictures)任何一个文件,如果你向其它应用分享pictures目录范围之外的文件是不行的。
第三步:使用FileProvider
Uri imageUri = FileProvider.getUriForFile(context, "com.hexfuture.hexteacher.fileprovider", mTmpFile);//通过FileProvider创建一个content类型的Uri Intent intent = new Intent(); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI startActivityForResult(intent,TAKE_PICTURE);
这样我们就可以在7.0打开相机保存图片了,但sdk6.0以后,我们都需要动态申请权限.
裁剪图片
在7.0之前,我们调用系统裁剪:Uri outputUri = Uri.fromFile(mTmpFile); Uri imageUri=Uri.fromFile(new File("/storage/emulated/0/temp/1474960080319.jpg")); Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(imageUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("scale", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection startActivityForResult(intent,1008);
但在7.0以后我们需要使用fileprovide来进行裁剪图片
Uri outputUri = FileProvider.getUriForFile(context, "com.hexfuture.hexteacher.fileprovider",mTmpFile); Uri imageUri=FileProvider.getUriForFile(context, "com.hexfuture.hexteacher.fileprovider", new File("/storage/emulated/0/temp/1474960080319.jpg");//通过FileProvider创建一个content类型的Uri Intent intent = new Intent("com.android.camera.action.CROP"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(imageUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); intent.putExtra("scale", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); // no face detection startActivityForResult(intent,1008);
解析URI
在这我想讲一下URI的改变4.4之前
Uri : content://media/extenral/images/media/17766
4.4及以后
Uri : content://com.android.providers.media.documents/document/image%3A82482
URI转path路径
public static String getRealFilePath(final Context context, final Uri uri ) { if ( null == uri ) return null; final String scheme = uri.getScheme(); String data = null; if ( scheme == null ) data = uri.getPath(); else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) { data = uri.getPath(); } else if ( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) { //判断URI形式是4.4版本之前还是4.4版本及以后的 if (DocumentsContract.isDocumentUri(context, uri)) {//4.4及以后解析 String wholeID = DocumentsContract.getDocumentId(uri); // 使用':'分割 String id = wholeID.split(":")[1]; String[] projection = { MediaStore.Images.Media.DATA }; String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = { id }; Cursor cursor = context.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null); int columnIndex = cursor.getColumnIndex(projection[0]); if (cursor.moveToFirst()) { data = cursor.getString(columnIndex); } cursor.close(); }else {//4.4之前解析 Cursor cursor = context.getContentResolver().query( uri, new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null ); if ( null != cursor ) { if ( cursor.moveToFirst() ) { int index = cursor.getColumnIndex( MediaStore.Images.ImageColumns.DATA ); if ( index > -1 ) { data = cursor.getString( index ); } } cursor.close(); } } } return data; }
如果有什么问题,就可以问我
相关文章推荐
- android7.0适配权限问题
- Android app实现自更新和安装,权限检测适配Android6.0以下和Android6.0和Android7.0和Android8.0总结篇
- Android进阶封装之一个类实现兼容Android 6.0权限、适配Android7.0 拍照: 相机与相册上传图片就用我好啦!
- Android进阶封装之一个类实现兼容Android 6.0权限、适配Android7.0 拍照!
- Android7.0适配SD卡权限
- Android 一行代码搞定将错误日志放入到sd卡中且不需要任何权限,适配到android7.0
- Android权限管理之RxPermission解决Android 6.0 适配问题
- Android7.0+权限适配
- Android7.0适配心得(一)_拍照兼容
- android7.0 照相和图片剪裁 以及 应用内部apk更新下载安装适配
- Android6.0权限适配
- iOS10适配 完美解决相机、相册等权限的使用
- Android悬浮窗适配全机型,包含8.0,小米魅族华为悬浮窗权限适配
- bugly自动升级在android7.0系统上重复下载 bugly与takephoto适配7.0冲突问题
- iOS 10 开发适配系列 之 权限Crash问题
- Android 6.0 权限适配(PermissionsDipatcher)
- Android6.0权限适配实践
- Android7.0(Android N)适配教程,心得
- Android7.0适配所需要注意的一些事项
- Android 6.0权限适配