Android版本适配需要注意的坑
⼀、Android 8.0 适配 -- targetSdkVersion 升级成26 需要注意的⼀些坑
1. MODE_WORLD_READABLE 模式(表⽰当前⽂件可以被其他应⽤读取) 废弃(ContentProvider、BroadcastReceiver、Service、
SharedPreferences)
Caused by: java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
Android api 24后,对于⽂件权限进⾏了限制。 类似苹果的沙盒模式,应⽤创建的⽂件夹,其他应⽤⽆权限访问
MODE_WORLD_READABLE 模式的要替换成 MODE_PRIVATE
MODE_PRIVATE 模式
所以如果原代码中有使⽤MODE_WORLD_READABLE
2. 获取通话记录
Caused by: java.lang.SecurityException: Permission Denial: opening provider
com.acts.CallLogProvider from ProcessRecord{8c75d80
lient/u0a122} (pid=31624, uid=10122) requires android.permission.READ_CALL_LOG or android.permission.WRITE_CALL_LOG
android.permission.WRITE_CALL_LOG做权限适配android.permission.READ_CALL_LOG or android.permission.WRITE_CALL_LOG
针对android.permission.READ_CALL_LOG
3、图⽚选择和裁剪(通过FileProvider实现应⽤间共享⽂件)
Caused by: android.os.FileUriExposedException:
file:///storage/emulated/0/Android/lient/lient/camere/1547090088847.jpg exposed beyond app through Uri()
在l清单⽂件中注册 provider
<provider android:name="android.t.FileProvider"
android:authorities="fileProvider" android:grantUriPermissions="true" android:exported="false"> <!--元数据-->
<meta-data android:name="android.support.FILE_PROVIDER_PATH" android:resource="@xml/file_paths" /> </provider>
需要注意⼀下⼏点:
exported:必须为false
grantUriPermissions:true,表⽰授予 URI 临时访问权限。
authorities 组件标识,都以包名开头,避免和其它应⽤发⽣冲突。
第⼆步
第⼆步:
指定共享⽂件的⽬录,需要在res⽂件夹中新建xml⽬录,并且创建file_paths
<resources xmlns:android="schemas.android/apk/res/android">
<paths>
<external-path path="" name="download"/>
</paths>
</resources>
path=”“,是有特殊意义的,它代表根⽬录,也就是说你可以向其它的应⽤共享根⽬录及其⼦⽬录下任何⼀个⽂件了。
name="", 指对应⽬录下的对应的⽂件
第三步:
第三步
使⽤FileProvider
根据版本号把Uri改成使⽤FiliProvider创建的Uri,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
cameraFileUri = UriForFile(mContext, "fileProvider[authorities 对应的值]
} else {
cameraFileUri = Uri.fromFile(new File(saveCamerePath, saveCameraFileName));
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)来对⽬标应⽤临时授权该Uri所代表的⽂件
添加intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //添加这⼀句表⽰对⽬标应⽤临时授权该Uri所代表的⽂件 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); }
在设置裁剪要保存的 intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);的时候,这个outUri是要
使⽤Uri.fromFile(file)⽣成的,⽽不是使
⽤UriForFile。
FileProvider所⽀持的⼏种path类型
从Android官⽅⽂档上可以看出FileProvider提供以下⼏种path类型:
files-path path="" name="camera_photos" />
<files-path
cache-path name="name" path="path" />
<cache-path
getCacheDir返回的路径:eg:“/data//cache”;
该⽅式提供在应⽤的内部存储区的缓存⼦⽬录的⽂件。它对应getCacheDir
external-path name="name" path="path" />
<external-path
该⽅式提供在外部存储区域根⽬录下的⽂件。它对应ExternalStorageDirectory
eg:"/storage/emulated/0";
external-files-path name="name" path="path" />
getsavefilename<external-files-path
该⽅式提供在应⽤的外部存储区根⽬录的下的⽂件。它对应Context#getExternalFilesDir(String)
Context#getExternalFilesDir(String)
external-cache-path name="name" path="path" />
<external-cache-path
该⽅式提供在应⽤的外部缓存区根⽬录的⽂件。它对应ExternalCacheDir()
eg:"/storage/emulated/0/Android//cache"
4. 获取以content开头的⽂件拿不到正确路径
java.lang.IllegalArgumentException: column '_data' does not exist
拿到uri之后进⾏版本判断⼤于等于24(即Android7.0)⽤最新的获取路径⽅式
Stringstr ="";
if(Build.VERSION.SDK_INT >=24) {
str = getFilePathFromURI(this, uri);//新的⽅式
}else{
str = getPath(this, uri);你⾃⼰之前的获取⽅法
}
public String getFilePathFromURI(Context context, Uri contentUri) {
File rootDataDir = FilesDir();
String fileName =getFileName(contentUri);
if (!TextUtils.isEmpty(fileName)) {
File copyFile =new File(rootDataDir + File.separator + fileName);
copyFile(context, contentUri, copyFile);
AbsolutePath();
}
return null;
}
public static String getFileName(Uri uri) {
if (uri ==null)return null;
String fileName =null;
String path = Path();
int cut = path.lastIndexOf('/');
if (cut != -1) {
fileName = path.substring(cut +1);
}
return fileName;
}
public void copyFile(Context context, Uri srcUri, File dstFile) {
try {
InputStream inputStream = ContentResolver().openInputStream(srcUri);
if (inputStream ==null)return;
OutputStream outputStream =new FileOutputStream(dstFile);
copyStream(inputStream, outputStream);
inputStream.close();
outputStream.close();
}catch (Exception e) {
e.printStackTrace();
}
}
public int copyStream(InputStream input, OutputStream output)throws Exception, IOException { final int BUFFER_SIZE =1024 *2;
byte[] buffer =new byte[BUFFER_SIZE];
BufferedInputStream in =new BufferedInputStream(input, BUFFER_SIZE); BufferedOutputStream out =new BufferedOutputStream(output, BUFFER_SIZE);
int count =0, n =0;
try {
while ((n = in.read(buffer,0, BUFFER_SIZE)) != -1) {
out.write(buffer,0, n);
count += n;
}
out.flush();
}finally {
try {
out.close();
}catch (IOException e) {
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论