美文网首页
android 7.0系统共享文件权限

android 7.0系统共享文件权限

作者: 小川君 | 来源:发表于2017-11-02 18:08 被阅读0次

    一、7.0系统,不再将应用程序的私有程序向使用者放宽,随之带来的就是你的App对外无法暴露file://类型的URI了;如果继续使用intent携带file://类型的uri去访问其他应用,比如说相机、安装apk等则会抛出FileUriExposedException异常;
    二、7.0之前,有种统一的调用相机的方式,细分下也可以说是两种,因为笔者就是用的第二种非主流的方式,当然,第二种仅限拓展,其实跟第一种也没什么区别,只是依然可以在7.0系统上运行,代码如下:

           String cachePath = getApplicationContext().getExternalCacheDir().getPath();
            File picFile = new File(cachePath, "test.jpg");
            Uri picUri = Uri.fromFile(picFile);
    
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT,picUri);
            startActivityForResult(intent, PHOTO_ONE_REQUEST_CODE);
    

    这种方式在7.0之前可以正常无误的使用,但是在7.0以及以上版本则会抛出FileUriExposedException异常;

    第二种笔者拓展的方法

            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(intent, PHOTO_TWO_REQUEST_CODE);
    

    有同学看了可能会扔板砖了,这他喵的不一样么!!!
    确实是一样的,不一样的是第一种在intent之中传了个路径,有了这个路径,相机拍完照之后是不会将图片保存到相册的,也就是说相册里面没有刚才拍的照;同时,重点来了 ,在onActivityResult的回调中intent类型的参数是没有值的 也就是null
    同样的第二种方法,onActivityResult回调中intent类型的参数是有值的,我们可以根据这个值来获取到bitmap对象,然后这样这样。。hh

    @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
                  if (data != null) {
                        String sdStatus = Environment.getExternalStorageState();
                        // 检测sd是否可用
                        if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) {
                            Log.i("小川",
                                    "SD card is not avaiable/writeable right now.");
                            return;
                        }
                        Bundle bundle = data.getExtras();
                        // 获取相机返回的数据,并转换为Bitmap图片格式
                        Bitmap bitmap = (Bitmap) bundle.get("data");
                        mImageView.setImageBitmap(bitmap);
                    } else {
                        Log.i("小川", " 打开相机 空集");
                    }
    }
    

    ps:当然这种方式不太推荐,大家还是按照正常的、谷歌推荐的方式来写
    三、使用FileProvider
    查看FileProvider的父类可知,FileProvider其实是个ContentProvider,不过不在本文的范围之内,有关的ContentProvider的知识,请自行度娘谷歌;
    使用FileProvider首先androidMainfest文件中注册:

    <provider
                android:name="com.chuan.jun.UpdateFileProvider"
                android:authorities="com.feihong.txgw.updatefileprovider"
                android:exported="false"
                android:grantUriPermissions="true"
                android:initOrder="101" >
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/update_cache_path"/>
            </provider>
    

    android:name 【固定值】 FileProvider的包名+类名
    这里可以直接引用v4包下面的provider 也可以去自定义实现provider类
    笔者这里是实现的provider子类,都是一样的

    android:name="android.support.v4.content.FileProvider"
    
    import android.support.v4.content.FileProvider;
    
    public class UpdateFileProvider extends FileProvider {
    }
    
    

    android:authorities 【自定义】 推荐以包名+”.fileprovider”方式命名,系统唯一
    android:exproted 要求必须为false,为true则会报安全异常
    android:grantUriPermissions 是否允许为文件设置临时权限
    android:initOrer: 优先级
    android:resource="@xml/update_cache_path"就是我们的共享路径配置的xml文件(此文件需要在res文件夹下面创建一个名字为xml的目录,然后在此目录下创建一个资源文件,这里的资源文件名称为update_cache_path)

    关于这个资源文件的内容

    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-cache-path name="update_cache" path=""/>
        <!--<external-path name="external_path" path=""/>-->
        <!--<root-path path="txgw" name="cache" />-->
        <!--/ path可以为空 表示指定目录下的所有文件、文件夹 都可以被共享-->
        <file-path name="my_image" path=""/>
    
        <!--物理路径相当于Context.getFilesDir() + /path/-->
        <!--<files-path name="name" path="path" />-->
    
        <!--物理路径相当于Context.getCacheDir() + /path/-->
        <!--<cache-path name="name" path="path" />-->
    
        <!--物理路径相当于Environment.getExternalStorageDirectory() + /path/。-->
        <!--<external-path name="name" path="path" />-->
    
    
        <!--物理路径相当于Context.getExternalFilesDir(String) + /path/。-->
        <!--注意:external-cache-path在support-v4:24.0.0这个版本并未支持,直到support-v4:25.0.0才支持-->
        <!--<external-files-path name="name" path="path" />-->
    
        <!--物理路径相当于Context.getExternalCacheDir() + /path/。-->
        <!--<external-cache-path name="name" path="path" />-->
    
        <!--如果你想使用外置SD卡,可以用这个:物理路径相当于/path/-->
        <!--<root-path name="name" path="path" />-->
    
    </paths>
    

    ps:具体用那种类型,取决于你文件存放的位置
    准备工作到这已经差不多了
    四、调用相机

    public class AndroidNActivity extends BaseActivity {
        private Toolbar mToolBar;
        private AppCompatImageView mImageView;
        private final static int PHOTO_ONE_REQUEST_CODE = 1;
        private final static int PHOTO_TWO_REQUEST_CODE = 2;
        private final static int PHOTO_THREE_REQUEST_CODE = 3;
        private final static int IMG_REQUEST_CODE = 4;
        private final static int CROP_REQUEST_CODE = 5;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_n_layout);
            mToolBar = (Toolbar) findViewById(R.id.android_N_ToolBar);
            setSupportActionBar(mToolBar);
            mToolBar.setTitleTextColor(getResources().getColor(R.color.colorAccent));
    
            mImageView = (AppCompatImageView) findViewById(R.id.local_img_show);
        }
    
        public void openImgsOrPhtoto(View view) {
            switch (view.getId()) {
                case R.id.openImg:
                    openImgs();
                    break;
                case R.id.openPhoto:
                    openPhoto();
                    break;
                default:
                    break;
            }
        }
    
        /**
         * 打开相机
         */
        private void openPhoto() {
            // 相机
            /**
             * 7.0以前有两种写法 也可以理解为一种
             * 1)打开的相机的时候 传一个保存路径进去 然后拍下来的照片会保存到这个路径里面  相应的intent返回的是null
             * 2)直接打开相机  不传路径进去  这时候 需要在拿到这个图片的时候 再去手动的将图片保存到本地  intent返回不为null
             * 网上大多都是第一种方案  也是系统推荐的一种方案
             * 但是在7.0以后  就会抛出FileUriExposedException异常
             * 第二种方案 可以正常使用  但是仅供参考 因为笔者以前用的就是这种非主流方案
             *
             * 在7.0以后的正常方案则是使用FileProvider
             */
    
            /**
             * 7.0以前第一种方案 (会报异常)
             */
    //        String cachePath = getApplicationContext().getExternalCacheDir().getPath();
    //        File picFile = new File(cachePath, "test.jpg");
    //        Uri picUri = Uri.fromFile(picFile);
    
    //        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //        intent.putExtra(MediaStore.EXTRA_OUTPUT,picUri);
    //        startActivityForResult(intent, PHOTO_ONE_REQUEST_CODE);
    
            /**
             * 7.0以前第二种非主流方案
             */
    
    //        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //        startActivityForResult(intent, PHOTO_TWO_REQUEST_CODE);
    
            /**
             * 7.0以上的正确姿势
             */
            String cachePath = getApplicationContext().getExternalCacheDir().getPath();
            File picFile = new File(cachePath, "imgName.jpg");
            Uri contentUri = getUriForFile(this,
                    "com.feihong.txgw.updatefileprovider", picFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT,contentUri);
            startActivityForResult(intent, PHOTO_THREE_REQUEST_CODE);
        }
    
        /**
         * 打开相册
         */
        private void openImgs() {
    
            Intent intent = new Intent(Intent.ACTION_PICK);
            //相片类型    相册
            intent.setType("image/*");
            startActivityForResult(intent, IMG_REQUEST_CODE);
        }
    
        private File localCropPath;// 裁剪之后的图片保存路径
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            switch (requestCode) {
                case PHOTO_ONE_REQUEST_CODE:
                    if (data != null) {
                      // 这里data必为null
                    } else {
                        Log.i("小川", " 打开相机 空集");
                    }
                    break;
                case PHOTO_TWO_REQUEST_CODE:
                        String sdStatus = Environment.getExternalStorageState();
                        // 检测sd是否可用
                        if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) {
                            Log.i("小川",
                                    "SD card is not avaiable/writeable right now.");
                            return;
                        }
                        Bundle bundle = data.getExtras();
                        // 获取相机返回的数据,并转换为Bitmap图片格式
                        Bitmap bitmap = (Bitmap) bundle.get("data");
                        mImageView.setImageBitmap(bitmap);
                    break;
                case PHOTO_THREE_REQUEST_CODE:
                    /**
                     * 7.0正确姿势 以及裁剪(注意uri格式)
                     */
    
                   // 拍照之后的照片路径的 uri
                    Uri contentUri = getUriForFile(this,
                            "com.feihong.txgw.updatefileprovider", new File(getApplicationContext().getExternalCacheDir().getPath() + File.separator + "imgName.jpg"));
                    Uri desUri = null;
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        String storage = getApplicationContext().getExternalCacheDir().getPath();
                        File dirFile = new File(storage + File.separator+"orgImg");
                        if(dirFile.exists()){
                            dirFile.delete();
                        }
                        dirFile.mkdirs();
                        localCropPath = new File(dirFile, "crop.jpg");
                        desUri = Uri.fromFile(localCropPath);
    
                        Intent intent = new Intent("com.android.camera.action.CROP");
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        }
                        intent.setDataAndType(contentUri, "image/*");
                        intent.putExtra("crop", "true");
                        intent.putExtra("aspectX", 1); // 裁剪框比例
                        intent.putExtra("aspectY", 1);
                        intent.putExtra("outputX", 320); // 输出图片大小
                        intent.putExtra("outputY", 320);
                        intent.putExtra("scale", true); // 是否可移动
                        intent.putExtra("return-data", false);
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); // 返回一个文件
                        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                        startActivityForResult(intent, CROP_REQUEST_CODE);
                    }
                    break;
                case IMG_REQUEST_CODE:
                    /**
                     * 从相册选择
                     */
                    if (data != null) {
                        try {
                            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
                            mImageView.setImageBitmap(bitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else {
                        Log.i("小川", "打开相册: 空集");
                    }
                    break;
                case CROP_REQUEST_CODE:
                    /**
                     * 裁剪
                     */
                    if(data != null){
                        // 读取uri所在的图片
                        FileOutputStream outputStream = null;
                        try {
                            Bitmap photoBitmap = MediaStore.Images.Media.getBitmap(getApplication().getContentResolver(), Uri.fromFile(localCropPath));
                            mImageView.setImageBitmap(photoBitmap);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
    
                    }else {
                        Log.i("小川", "  图片裁剪 空集");
                    }
                    break;
                default:
                    break;
            }
        }
    }
    
    

    五、关于安装apk

    Intent intent = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            } else {
                Uri uri = FileProvider.getUriForFile(context, context.getPackageName()+"updatefileprovider", file);
                intent.setDataAndType(uri, "application/vnd.android.package-archive");
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
    

    ok 7.0的共享文件权限到这就没啦 希望能帮助大家!!!

    相关文章

      网友评论

          本文标题:android 7.0系统共享文件权限

          本文链接:https://www.haomeiwen.com/subject/hacapxtx.html