7.0 FileProvide适配

7.0 Build.VERSION_CODES.N 24

为什么要适配

在7.0中 ,Uri.from(file),可能会触发FileUriExposedException
和动态权限一样,如果target选择24以下是不需要适配的,但还是应该尽早适配

解决办法:在24及以上,使用content:// 替代 file:// (Uri.from(file))

适配步骤

1.在res下,新建一个xml目录,在其中创建一个任意名字的xml文件,如file_provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
  <paths>
  <external-path
    name="external"
    path="." />
</paths>

xml说明

external-path  等同于    Environment.getExternalStorageDirectory()
如果将path设置为path=“pic”,则共享的文件限制为/storage/emulated/0/pic/
为空,则共享文件目录为storage/emulated/0/

<root-path/> 代表设备的根目录new File("/");
<files-path/> 代表context.getFilesDir()
<cache-path/> 代表context.getCacheDir()
<external-path/> 代表Environment.getExternalStorageDirectory()
<external-files-path>代表context.getExternalFilesDirs()
<external-cache-path>代表context.getExternalCacheDirs()

2.在Manifest文件中注册FileProvider对象

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_provider_paths"/>
</provider>

3.修改java代码,场景拍照,裁剪,安装apk

 fp=context.getPackageName()+".fileprovider";

3.1 拍照

imgPath = FileUtil.generateImgePath();
    File imgFile = new File(imgPath);  //设置拍照后的图片的保存位置
    Log.i("TAG", imgFile.getAbsolutePath());
    Uri imgUri = null;
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        //通过FileProvider创建一个content类型的Uri
        imgUri = FileProvider.getUriForFile(activity, fp, imgFile);
        //添加这一句表示对目标应用临时授权该Uri所代表的文件
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    } else {
        imgUri = Uri.fromFile(imgFile);
    }

    intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);

    final Uri uri = imgUri;
    activity.startActivityForResult(intent, new CallBackIntent() {
        @Override
        public void onResult(Intent data) {
            if (data == null || data.getData() == null)
                //  ToastUtils.startShort(activity, "拍照数据返回为空");
                Log.i("TAG", "拍照数据返回为空");

            startPhotoZoom(uri, activity, new IntentCallBack() {
                @Override
                public void onIntent(Intent intent) {
                    callBack.onBitmap(getImageView(intent));
                }
            });
        }
    });

imgPath,就是拍照后图片保存的路径

3.2 裁剪

//适配7.0的uri,(文件uri转化为ContentUri)
public  Uri getImageContentUri(Context context, File imageFile) {
    String filePath = imageFile.getAbsolutePath();
    Cursor cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            new String[] { MediaStore.Images.Media._ID },
            MediaStore.Images.Media.DATA + "=? ",
            new String[] { filePath }, null);

    if (cursor != null && cursor.moveToFirst()) {
        int id = cursor.getInt(cursor
                .getColumnIndex(MediaStore.MediaColumns._ID));
        Uri baseUri = Uri.parse("content://media/external/images/media");
        return Uri.withAppendedPath(baseUri, "" + id);
    } else {
        if (imageFile.exists()) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Images.Media.DATA, filePath);
            return context.getContentResolver().insert(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        } else {
            return null;
        }
    }
}

如果是由相机或者相册之后调用裁减,无需转化uri
如果是单独使用裁减功能,则需要利用上述方法进行转化

private void cropPicture(Uri uri) {
    Intent innerIntent = new Intent("com.android.camera.action.CROP");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        //添加这一句表示对目标应用临时授权该Uri所代表的文件
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
    innerIntent.setDataAndType(uri, "image/*");
    innerIntent.putExtra("crop", "true");

    //不是1比1 主要是为了适配华为手机一比一时,裁减是圆形的
    innerIntent.putExtra("aspectX", 9998);
    innerIntent.putExtra("aspectY", 9999);

    innerIntent.putExtra("outputX", 320);
    innerIntent.putExtra("outputY", 320);
    innerIntent.putExtra("return-data", true);
    innerIntent.putExtra("scale", true);

    activity.startActivityForResult(intent, new CallBackIntent() {
        @Override
        public void onResult(Intent data) {
            callBack.onIntent(data);
             //裁减后的图片
            //Bitmap bitmap = data.getParcelableExtra("data");
        }
    });
    
}

3.3 安装apk

private void installAPK(UpdateInfo updateInfo) {
    File apkfile = new File(mSavePath, updateInfo.getClientVersionName());
    if (!apkfile.exists()) {
        return;
    }
    String cmd = "chmod 777 " + apkfile.toString();
    try {
        Runtime.getRuntime().exec(cmd);
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 通过Intent安装APK文件
    Intent i = new Intent(Intent.ACTION_VIEW);
    if (Build.VERSION.SDK_INT >= 24) { //适配安卓7.0
        i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Uri apkFileUri = FileProvider.getUriForFile(context.getApplicationContext(), fp, apkfile);
        i.setDataAndType(apkFileUri, "application/vnd.android.package-archive");
    } else {
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.setDataAndType(Uri.parse("file://" + apkfile.toString()),"application/vnd.android.package-archive");
    }
    context.startActivity(i);
    //关闭当前程序
    int pid = android.os.Process.myPid();
    android.os.Process.killProcess(pid);
}
原文地址:https://www.cnblogs.com/ganchuanpu/p/8459987.html