为什么要修改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实现。
实现代码如下:文章来源:https://www.toymoban.com/news/detail-784115.html
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;
}
}
}
参考文档:文章来源地址https://www.toymoban.com/news/detail-784115.html
- 一篇文章搞定《Android权限问题(全版本)》https://blog.csdn.net/weixin_45112340/article/details/128905213
- [ ]应用TargetSdkVersion 30升级适配指南 https://dev.mi.com/distribute/doc/details?pId=1738#_2
到了这里,关于Android App-targetSDKVersion28升级为30的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!