众所周知,Android版本更新飞快,各个版本都有一些新的特性增加,这就出现了许多坑,导致了开发者要不断进行适配,下面我就来谈谈从Android6.0到Android9.0主流特性的适配过程。
Anrdoid 6.0
1.动态权限
Android 6.0最大的更改就是动态权限申请,详细的说明请查看我的另一篇博客:Android开发之Android6.0权限管理
2.弃用HttpClient
Android6.0以后,AndroidSDK已经不再使用Apache Http的相关api,如果你的项目需要使用HttpClient,需要在app的build.gradle文件添加如下配置:
defaultConfig {
useLibrary 'org.apache.http.legacy'
}
注:Android 9.0之后已经完全移除了对Apache HTTP 客户端的支持,即时你配置了上面的也不起作用,详情请看下面Android9.0的适配。
Android 7.0
Android7.0在6.0的基础上对权限这一块更加严格地控制,主要体现有:
1.应用间共享文件
如果你的应用想要通过Intent访问本地文件(夹),那么在Android7.0或许会报这个错android.os.FileUriExposedException。
这是因为,7.0禁止了应用间文件的直接访问,具体解决方案请看:安卓7.0报错android.os.FileUriExposedException
2.App新签名方案V2
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 以上和Gradle 2.2 以上会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。
在应用打包时,必须勾选上两个
1)只勾选v1签名就是传统方案签署,但是在7.0上不会使用V2安全的验证方式。
2)只勾选V2签名7.0以下会显示未安装,7.0上则会使用了V2安全的验证方式。
3)同时勾选V1和V2则所有版本都没问题。
3.SharedPreferences问题
//Context.MODE_WORLD_READABLE:7.0以后不推荐使用这个,已经过时,这个也属于应用间共享文件的范畴,修改成MODE_PRIVATE
SharedPreferences sp = context.getSharedPreferences("key", Context.MODE_WORLD_READABLE);
MODE_WORLD_READABLE在安卓7.0后的api不再推荐使用,如果需要共享应用间配置,推荐使用ContentProvider或者Broadcast。
Android 8.0
1.新增权限
PHONE权限组新增两个权限:
android.permission.ANSWER_PHONE_CALLS:允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 acceptRingingCall() 方法。
android.permission.READ_PHONE_NUMBERS:允许您的应用读取设备中存储的电话号码。
安装未知来源应用的权限:
android.permission.REQUEST_INSTALL_PACKAGES:允许安装未知来源的应用
这样系统会自动询问用户完成授权。当然你也可以先使用 canRequestPackageInstalls()查询是否有此权限,如果没有的话使用Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES这个action将用户引导至安装未知应用权限界面去授权。
private void installAPK(String apkPath){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasPermission = getPackageManager().canRequestPackageInstalls();
if (hasPermission) {
//安装应用
install(apkPath);
} else {
//跳转至“允许安装未知应用”权限界面,引导用户开启权限
Uri packageUri = Uri.parse("package:" + this.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri);
startActivityForResult(intent, 101);
}
}else {
//安装应用
install(apkPath);
}
}
private void install(String apkPath){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", new File(apkPath));
} else {
uri = Uri.fromFile(new File(apkPath));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
2.Notification的变更
Notification这个东西是我觉得变化的最频繁的,一个版本一个api,十分坑爹。
Android 8.0 新增了NotificationChannel来区别不同渠道的通知。
下面是代码示例:
/**
* 使用一个ID来标识不同渠道,在手机中可以禁止接收来自某个渠道的通知
*/
public static final String CHANNEL_ID = "test";
private void showNotify(int progress) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//先创建一个新渠道:渠道ID,渠道名称,渠道重要性
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_LOW);
channel.enableVibration(false); //对于持续性的通知,有些手机会不断地震动和发出提示音,建议禁用
channel.enableLights(false); //禁止灯光闪烁提示
channel.setSound(null, null);
manager.createNotificationChannel(channel);
}
NotificationCompat.Builder notifyBuilder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//8.0之后,使用渠道,必须使用已经存在的渠道ID
notifyBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
} else {
notifyBuilder = new NotificationCompat.Builder(this);
}
notifyBuilder.setSound(null); //禁用提示音,对于持续性的通知,有些手机会不断地震动和发出提示音,建议禁用
notifyBuilder.setSmallIcon(getResources().getIdentifier("ic_launcher", "mipmap", getPackageName()));
notifyBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); //优先级
notifyBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
notifyBuilder.setShowWhen(true);
notifyBuilder.setOnlyAlertOnce(true);//只提示一次
notifyBuilder.setContentTitle("下载中...");
notifyBuilder.setAutoCancel(false);
notifyBuilder.setOngoing(true);
notifyBuilder.setProgress(100, progress, false);
Intent intent =new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 100, intent, PendingIntent.FLAG_UPDATE_CURRENT);
notifyBuilder.setContentIntent(pendingIntent);
Notification notification = notifyBuilder.build();
notification.priority = Notification.PRIORITY_HIGH;
manager.notify(100, notification);
}
3. 静态广播无法正常接收
Android 8.0 引入了新的广播接收器限制,导致需要移除所有为隐式广播 Intent 注册的广播接收器,使用动态广播代替静态广播。
4.Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
Android 8.0 非全屏透明页面不允许设置方向(后面8.1系统谷歌就去掉了这个限制)
解决方案:
(1)android:windowIsTranslucent设置为false
(2)如果还是想用的话,就去掉清单文件中Activity中的android:screenOrientation这个配置项,
Android 9.0
1.CLEARTEXT communication to life.115.com not permitted by network security policy
Android P 限制了明文流量的网络请求(普通的http请求),非加密的流量请求都会被系统禁止掉。
解决方案:在资源文件新建xml目录,新建文件network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
AndroidManifest配置:
<application
android:networkSecurityConfig="@xml/network_security_config">
<uses-library
android:name="org.apache.http.legacy"
android:required="false" /><!-- Android P添加对ApacheHttp的支持 -->
</application>
不过还是建议后台的接口都使用https来传递数据会更安全。
针对Android9.0之后的更多适配可看:AndroidP适配指南(必看)
好了,以上就是我遇到过的Android适配的问题,加上一些大神的经验,肯定还不算完整,以后遇到了再补充。