Android版本适配问题处理
整理了一些Android各版本中适配需要注意的坑~可以在开发功能过程中提供一些参考,例如Android的广播通知和图片裁剪等等…
Android 8.0 适配
– targetSdkVersion 升级成26 需要注意的一些坑
第一点
MODE_WORLD_READABLE 模式(表示当前文件可以被其他应用读取) 被废弃 (ContentProvider、BroadcastReceiver、Service、SharedPreferences),Android api 24后对于文件权限进行了限制,会出现以下报错:
Caused by: java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
所以如果原代码中有使用MODE_WORLD_READABLE 模式的要替换成 MODE_PRIVATE 模式。
第二点
图片选择和裁剪(通过FileProvider实现应用间共享文件)
Android 7.0以上不允许intent带有file://的URI离开自身的应用了,要不然会抛出FileUriExposedException,想要在自己应用和其他应用之间共享File数据,只能使用content://的方式,并且还需要把该URI赋予临时的访问权限
Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.leniu.test/files/com.ct.client/camere/1547090088847.jpg exposed beyond app through ClipData.Item.getUri()
第一步:
在AndroidManifest.xml清单文件中注册 provider
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.xx.xxx.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="http://schemas.android.com/apk/res/android">
<paths>
<external-path path="" name="picture"/>
</paths>
</resources>
path="",代表根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了。
name="", 指对应目录下的对应的文件。
第三步:
使用FileProvider,根据版本号把Uri改成使用FiliProvider创建的Uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
cameraFileUri = FileProvider.getUriForFile(mContext, "com.xx.xxx.fileProvider[authorities 对应的值]", new File(saveCamerePath, saveCameraFileName));
} else {
cameraFileUri = Uri.fromFile(new File(saveCamerePath, saveCameraFileName));
}
添加intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)来对目标应用临时授权该Uri所代表的文件
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)生成的,而不是使用FileProvider.getUriForFile。
拓展添加
FileProvider所支持的几种path类型
从Android官方文档上可以看出FileProvider提供以下几种path类型:
<files-path path="" name="camera_photos" />
该方式提供在应用的内部存储区的文件/子目录的文件。它对应Context.getFilesDir返回的路径:eg:"/data/data/com.xx.xxx/files"。
<cache-path name="name" path="path" />
该方式提供在应用的内部存储区的缓存子目录的文件。它对应getCacheDir返回的路径:eg:“/data/data/com.xx.xxx/cache”;
external-path name="name" path="path" />
该方式提供在外部存储区域根目录下的文件。它对应Environment.getExternalStorageDirectory返回的路径:eg:"/storage/emulated/0";
<external-files-path name="name" path="path" />
该方式提供在应用的外部存储区根目录的下的文件。它对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)返回的路径。eg:"/storage/emulated/0/Android/data/com.xx.xxx/files"。
<external-cache-path name="name" path="path" />
该方式提供在应用的外部缓存区根目录的文件。它对应Context.getExternalCacheDir()返回的路径。eg:"/storage/emulated/0/Android/data/com.xx.xxx/cache"
第三点
BroadcastReceiver无法接收广播,在Android 8之后需要setComponent才能收到广播消息,其中,ComponentName接收两个参数,参数1是自定义广播的包名,参数2是自定义广播的路径。
Intent intent = new Intent(action);
intent.putExtra(INTENT_DATA_PUSH, data);
intent.addCategory(context.getPackageName());
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
intent.setComponent(new ComponentName(context.getPackageName(), com.xx.broadcastReceiver);
}
context.sendBroadcast(intent);
Android 9.0 适配
– targetSdkVersion 升级成28 需要注意的一些坑
第一点
限制 HTTP 网络请求(*),Android 9.0 中限制了 HTTP-明文传输的网络请求,若仍继续使用HTTP请求,则会在日志中提示以下异常,只是无法正常发出请求,不会导致应用崩溃。
java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy
适配的方法如下:
在资源目录中新建一个 xml 文件作为网络安全配置文件,例如 xml/network_security_config.xml,然后在文件中填写以下内容:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" overridePins="true" />
<certificates src="user" overridePins="true" />
</trust-anchors>
</base-config>
</network-security-config>
在AndroidManifest.xml进行配置:
<application
...
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
第二点
弃用 Apache HTTP Client,由于官方在 Android 9.0 中移除了所有 Apache HTTP Client 相关的类,因此我们的应用或是一些第三方库如果使用了这些类,就会抛出找不到类的异常:
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;
若需要继续使用 Apache HTTP Client ,可通过以下方法进行适配:
在 AndroidManifest.xml 中添加以下内容:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
或者在应用中直接将 Apache HTTP Client 相关的类打包并进行引用
其他 API 方面
Build.SERIAL 被弃用(*)
Android 9.0 之前,开发者可以使用Build.SERIAL获取设备的序列号。现在这个方法被弃用了,Build.SERIAL将始终设置为 “UNKNOWN” 以保护用户的隐私。
适配的方法为先请求READ_PHONE_STATE权限,然后调用Build.getSerial()方法。