不使用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