美文网首页
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