其他分享
首页 > 其他分享> > 不使用MANAGE_EXTERNAL_STORAGE权限,适配Android11保存图片和分享图片

不使用MANAGE_EXTERNAL_STORAGE权限,适配Android11保存图片和分享图片

作者:互联网

一、其实Android11(targetSdkVersion 30)及以上在AndroidManifest.xml声明MANAGE_EXTERNAL_STORAGE权限(不在AndroidManifest.xml声明获得“所有文件访问权限”按钮为灰色不能授权)在加上下面的判断就能基本解决问题:

    //获取存储权限
    private void getPermissions() {
//        普通权限:只需要在清单文件中注册即可
//        危险权限(Android 6.0 之后):需要在代码中动态申请,以弹系统 Dialog 的形式进行请求
//        特殊权限(Android 11(含) 之后):需要在代码中动态申请,以跳系统 Activity 的形式进行请求
        //android版本大于等于11
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//必须要MANAGE_EXTERNAL_STORAGE权限,但Google Play Console审核不通过
            // 先判断有没有权限
            if (Environment.isExternalStorageManager()) {
                new Thread(saveFileRunnable).start();
            } else {
                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, 0);
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                //存储空间权限
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
            } else {
                //有权限后需要处理的功能
            }
        } else {
            //有权限后需要处理的功能
        }
    }

    // 提示是否獲取存储空间权限
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 0:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {// 取得权限
                    //有权限后需要处理的功能
                } else {// 未取得权限
                    Toast.makeText(getApplicationContext(), getString(R.string.language_noPermissions), Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

    //Android 11以上(含)同意存储权限直接保存图片
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (Environment.isExternalStorageManager()) {
                //有权限后需要处理的功能
            } else {
                Toast.makeText(getApplicationContext(), getString(R.string.language_noPermissions), Toast.LENGTH_SHORT).show();
            }
        }
    }

二、但问题是应用发布到Google Play Console因为声明了MANAGE_EXTERNAL_STORAGE权限,说这个权限有安全隐患,不是非必要权限说有了这篇文章;

三、适配Android11保存图片的方法;

1.获取位图(BitMap),我这是通过ImageLoaader获取bitmap对象

//ImageLoader获取bitmap对象
mBitmap = ImageLoader.getInstance().loadImageSync(listImage.get(indexImage));
//保存圖片
SaveImageUtil.saveFile(mBitmap, listImage.get(indexImage), getApplicationContext());

2.saveFile()保存图片的方法

//保存图片到相册
public static void saveFile(Bitmap bm, String url, Context context) throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { //android 11及以上版本保存圖片方法
        String mImageFileName = url.substring(url.lastIndexOf("/") + 1).toLowerCase();
        final ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + "station_image"); //图库中显示的文件夹名。
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/*");
        values.put(MediaStore.MediaColumns.IS_PENDING, 1);

        ContentResolver resolver = context.getContentResolver();
        final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        try {
            // 写下我们截图的实际数据
            try (OutputStream out = resolver.openOutputStream(uri)) {
                if (!bm.compress(Bitmap.CompressFormat.PNG, 100, out)) {
                    throw new IOException("Failed to compress");
                }
            }
            // 一切都很顺利、
            values.clear();
            values.put(MediaStore.MediaColumns.IS_PENDING, 0);
            values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
            resolver.update(uri, values, null, null);
        } catch (IOException e) {
            Toast.makeText(context, context.getString(R.string.language_pictureSavedFailed), Toast.LENGTH_SHORT).show();
        }
    } else {
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/";
        // 判断sd卡是否存在
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            // 判断文件夹是否存在
            File dirFile = new File(path);
            if (!dirFile.exists()) {
                dirFile.mkdirs();//创建此抽象路径指定的目录,包括所有必须但不存在的父目录。(及可以创建多级目录,无论是否存在父目录)
            }
            String fileName = url.substring(url.lastIndexOf("/") + 1, url.length()).toLowerCase();
            File file = new File(path + fileName);
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
            bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
            bos.flush();
            bos.close();
            // 通知图库更新
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file)));
        } else {
            Toast.makeText(context, context.getString(R.string.language_notSDCard), Toast.LENGTH_SHORT).show();
        }
    }
}

四、适配Android11分享图片到微信,微博,FaceBoook等平台

1.首先需要在AndroidManifest.xml中配置FileProvider,通过FileProvider,就允许第三方应用读取你的应用所分享的文件,而不会受到分区存储的限制

<provider
    android:name="androidx.core.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>

2.在res/xml目录下,添加文件file_provider_paths.xml,并添加如下内容:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="sharedata" path="shareData/"/>
</paths>

3.使用Bitmap保存文件和分享

 Bitmap mBitmap = ImageLoader.getInstance().loadImageSync(listImage.get(0));android 11
//分享图片到其他APP
SaveShareImageUtil.shareToOtherApp(mContext, mBitmap, listImage.get(0), packageName, isWeChat);

4.packageName是要分享到各应用的包名

5.保存图片的方法

/**
 * bitmap保存为文件
 *
 * @param bm       bitmap
 * @param filePath 文件路径
 * @return 返回保存结果 true:成功,false:失败
 */5.
private static Boolean saveBitmapToFile(Bitmap bm, String filePath) {
    try {
        File file = new File(filePath);
        file.deleteOnExit();
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));

        Boolean b = false;
        if (filePath.toLowerCase().endsWith(".png")) {
            b = bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
        } else {
            b = bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
        }
        bos.flush();
        bos.close();
        return b;
    } catch (IOException e) {

    }
    return false;
}

6.分享图片的方法

//分享图片到各平台
    public static void shareToOtherApp(Context context, Bitmap bitmap, String url, String packageName, int isWeChat) {
        String mImageFileName = url.substring(url.lastIndexOf("/") + 1).toLowerCase();
        String filePath = context.getExternalFilesDir(null).toString() + "/shareData/" + mImageFileName;
        // 该filePath对应于xml/file_provider_paths里的第一行配置:,因此才可被共享
        Boolean saveBitmap = saveBitmapToFile(bitmap, filePath);

        if (saveBitmap) {
            File file = new File(filePath);
            if (file == null || !file.exists()) {
                file.mkdirs();
            }
            //使用FileProvider,要与`AndroidManifest.xml`里配置的`authorities`一致,假设你的应用包名为com.example.app
            Uri contentPath = FileProvider.getUriForFile(context, "com.example.app.fileprovider", file);

            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setPackage(packageName);//自己选择分享到好友还是朋友圈

            switch (isWeChat) {
                case 0:
                    //com.tencent.mm.ui.tools.ShareImgUI   直接分享到微信好友,
                    intent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI"));
                    break;
                case 1:
                    //com.tencent.mm.ui.tools.ShareToTimeLineUI  直接分享到微信朋友圈,最多可以分享九张图片到微信朋友圈
                    intent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareToTimeLineUI"));
                    break;
//                case 2:
//                    //com.sina.weibo.page.ProfileInfoActivity 直接跳转到微博我的(简况)界面
//                    intent.setComponent(new ComponentName("com.sina.weibo", "com.sina.weibo.page.ProfileInfoActivity"));
//                    break;
            }

            intent.setType("image/*");
            intent.putExtra(Intent.EXTRA_STREAM, contentPath);
            context.startActivity(intent);
        }

7.功能图片展示

 

 

标签:context,适配,Android11,String,file,new,权限,com,图片
来源: https://blog.csdn.net/dawnZeng/article/details/122348623