为什么要修改targetSDKVersion?
1、应用开发平台要求(小米)
2、更好的兼容新版本的手机
有targetSDKVersion的位置:
- App的targetSDKVersion
- Module中的targetSDKVersion
- 引入的第三方库中有targetSDKVersion
修改了App和Module中的targetSDKVersion。
存储权限和存储位置问题
修改原因:
Android10(targetSdkVersion29)以后加入了分区存储,针对App设置了外置私有存储空间,卸载App时会被删除,一定程度上解决了卸载App后外置内存仍占用问题。
Android10在获取存储权限上和之前版本有区别。
修改前:
- targetSDKVersion28
- 使用EasyPermission框架获取权限。(长时间不更新且不支持新版本Android)
- 视频、音频、文件存储在外置存储卡【Environment.getExternalStorageDirectory()】中。
修改后:
- targetSDKVersion30(Android 11)
- 使用Permission替换EasyPermission获取权限
- 视频、音频、文件存储存储在私有外置存储中【context.getExternalFilesDir()】。
修改内容:
- 兼容Android不同版本获取权限。
- App新版本要处理老版本存储在外置存储卡中的内容。
知识点:
使用创建、读写文件测试对Environment.getExternalStorageDirectory()和context.getExternalFilesDir()目录的使用。
Environment.getExternalStorageDirectory()
- Android13:同Android11
- Android11:
- 动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后创建读写文件时显示permission denied。
- 动态获取Manifest.permission.MANAGE_EXTERNAL_STORAGE权限后可以创建并读写文件,获取权限方式是通过Intent intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)跳转至设置页面,用户自行打开允许访问所有文件权限看后才可创建读写文件。
- Android10:同Android11
- Android9 动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后可以创建并读写文件。
- Android6:动态获取Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE权限后可以创建并读写文件。
- Android5:不用获取权限可以创建并读写文件。
context.getExternalFilesDir();
- Android13:不用获取权限可以创建并读写文件。
- Android11:不用获取权限可以创建并读写文件。
- Android10:不用获取权限可以创建并读写文件。
- Android9:不用获取权限可以创建并读写文件。
- Android6:不用获取权限可以创建并读写文件。
- Android5:不用获取权限可以创建并读写文件。
修改方案:
- 修改视频、音频和文件存储位置为外置私有存储空间且不需要获取权限。
- 为兼容App旧版本存储在外置存储卡中的视频、音频和文件,在读取外置存储卡中的视频、音频和文件时需获取存储权限。
读取相册照片和视频
修改原因:
- Android10之后读取相册的权限不同且方法也不同。
修改前:
- 使用RxGalleryFinal框架(长时间不更新且不适配新版本Android)
修改后:
- 使用PictureSelector框架(适配Android10以后版本-内置有获取权限功能)
保存图片至相册
修改原因:
- Android10 添加至相册方式和位置不同。
修改后:
- 兼容不同Android版本
处理方法:
- Android9及之前版本,将图片保存至外置存储卡,然后通知将其添加到相册数据库,并刷新即可(需存储权限)。
- Android10及以后版本,不能将图片放置在私有外置存储空间(ContentResolver不支持,会提示错误),将图片放在公共媒体存放位置,然后刷新,使用ContentResolver实现。
实现代码如下:
package com.ypkj.kfpt.http; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.provider.MediaStore; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import android.widget.Toast; import androidx.fragment.app.FragmentActivity; import com.learn.base.permission.PermissionsUtil; import com.ypkj.kfpt.R; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.UUID; /** * 下载图片至相册的工具类 */ public class DonwloadSaveImg { private static Context context; private static String filePath; private static Bitmap mBitmap; private static String mSaveMessage = "失败"; private final static String TAG = "PictureActivity"; public static void donwloadImg( Context contexts, String filePaths) { context = contexts; filePath = filePaths; PermissionsUtil.getInstance().requestStoragePerm((FragmentActivity) contexts, "", new PermissionsUtil.BaseRequestCallback() { @Override public void onCallBack() { new Thread(saveFileRunnable).start(); } }); } private static Runnable saveFileRunnable = new Runnable() { @Override public void run() { try { if (!TextUtils.isEmpty(filePath)) { //网络图片 // 对资源链接 URL url = new URL(filePath); //打开输入流 InputStream inputStream = url.openStream(); //对网上资源进行下载转换位图图片 mBitmap = BitmapFactory.decodeStream(inputStream); inputStream.close(); } saveBitmap(context, mBitmap); mSaveMessage = "图片保存成功!"; } catch (IOException e) { mSaveMessage = "图片保存失败!"; e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } messageHandler.sendMessage(messageHandler.obtainMessage()); } }; private static Handler messageHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.d(TAG, mSaveMessage); Toast.makeText(context, mSaveMessage, Toast.LENGTH_SHORT).show(); } }; // 保存bitmap到相册,并兼容AndroidQ public static boolean saveBitmap(Context context, Bitmap bitmap) { if (bitmap == null) { return false; } boolean isSaveSuccess; if (Build.VERSION.SDK_INT < 29) { isSaveSuccess = saveImageToGallery(context, bitmap); } else { isSaveSuccess = saveImageToGalleryQ(context, bitmap); } return isSaveSuccess; } /** * android 10 以下版本 */ private static boolean saveImageToGallery(Context context, Bitmap image) { // 首先保存图片 String storePath = getBitmapFileDir(); File appDir = new File(storePath); if (!appDir.exists()) { appDir.mkdir(); } String fileName = UUID.randomUUID().toString() + ".png"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); // 通过io流的方式来压缩保存图片 boolean isSuccess = image.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); // 保存图片后发送广播通知更新数据库 Uri uri = Uri.fromFile(file); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); if (isSuccess) { return true; } else { return false; } } catch (IOException e) { e.printStackTrace(); } return false; } /** * android 10 以上版本 */ private static boolean saveImageToGalleryQ(Context context, Bitmap image) { long mImageTime = System.currentTimeMillis(); String mImageFileName = UUID.randomUUID().toString() + ".png"; final ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.RELATIVE_PATH, getBitmapFileDir()); values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName); values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png"); values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000); values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000); values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000); values.put(MediaStore.MediaColumns.IS_PENDING, 1); ContentResolver resolver = context.getContentResolver(); final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); try { OutputStream out = resolver.openOutputStream(uri); if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) { return false; } values.clear(); values.put(MediaStore.MediaColumns.IS_PENDING, 0); values.putNull(MediaStore.MediaColumns.DATE_EXPIRES); resolver.update(uri, values, null, null); } catch (Exception e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { resolver.delete(uri, null); } return false; } return true; } public static String getBitmapFileDir() { String BITMAP_FILE_DIRECTORY = context.getString(R.string.appname); if (Build.VERSION.SDK_INT < 29) { // android 10 以下版本 return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + BITMAP_FILE_DIRECTORY; } else { return Environment.DIRECTORY_PICTURES + File.separator + BITMAP_FILE_DIRECTORY; } } }
参考文档:
- 一篇文章搞定《Android权限问题(全版本)》https://blog.csdn.net/weixin_45112340/article/details/128905213
- [ ]应用TargetSdkVersion 30升级适配指南 https://dev.mi.com/distribute/doc/details?pId=1738#_2
- 兼容不同Android版本
- Android10 添加至相册方式和位置不同。
- 使用PictureSelector框架(适配Android10以后版本-内置有获取权限功能)
- 使用RxGalleryFinal框架(长时间不更新且不适配新版本Android)
- Android10之后读取相册的权限不同且方法也不同。
还没有评论,来说两句吧...