本文主要说Android 8.0适配

Android 8.0适配

准备工作

将我们项目中的targetSdkVersion改为 26(8.0) 或者 27(8.1),记住不要超过27,毕竟我还没有告诉你Android P怎么适配(/滑稽)。

Android 8.0 行为变更

1.提醒窗口

如果应用使用

权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
1
2
3
4
5
6
```
- TYPE_PHONE
- TYPE_PRIORITY_PHONE
- TYPE_SYSTEM_ALERT
- TYPE_SYSTEM_OVERLAY
- TYPE_SYSTEM_ERROR

相反,应用必须使用名为

的新窗口类型。
1
也就是说需要在之前的基础上判断一下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}else {
mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}

1
当然记得需要有权限

1
2

使用 ```TYPE_APPLICATION_OVERLAY``` 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
  • 应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
  • 系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
  • 通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。
    1
    2
    ### 2.网页表单自动填充
    现在,Android 自动填充框架提供对自动填充功能的内置支持,对于安装到运行 Android 8.0 的设备上的应用,与 WebView 对象相关的下列函数已经发生变化:

WebSettings

  • getSaveFormData() 函数现在返回 false。之前,此函数返回 true。
  • 调用 setSaveFormData() 不再有任何效果。
    WebViewDatabase
  • 调用 clearFormData() 不再有任何效果。
  • hasFormData() 函数现在返回 false。之前,当表单包含数据时,此函数返回 true。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
### 3.集合的处理
在 Android 8.0 中,Collections.sort() 是在 List.sort() 的基础上实现的。在 Android 7.x(API 级别 24 和 25)中,则恰恰相反。在过去,List.sort() 的默认实现会调用 Collections.sort()。
- List.sort() 的实现不能调用 Collections.sort(),因为这会导致堆栈因无限递归而溢出。相反,如果您需要 List 实现的默认行为,应避免重写 sort()。
此项变更使 Collections.sort() 可以利用优化的 List.sort() 实现,但具有以下限制:
- 如果父类以不适当的方法实现 sort() ,通常最好使用在 List.toArray()、Arrays.sort() 和 ListIterator.set() 的基础上构建的实现重写 List.sort()。

- 如果您选择后者只是因为您希望开发一种适用于所有 API 级别的 sort() 函数,可以考虑赋予其一个唯一的名称,例如 sortCompat(),而不是重写 sort()。
- 现在,Collections.sort() 只是对调用 sort() 的 List 实现进行的一项结构性修改。例如,在 Android 8.0 之前的平台版本中,如果通过调用 List.sort() 进行排序,则当迭代处理 ArrayList 以及在迭代过程中调用 sort() 时,会引发 ConcurrentModificationException。而 Collections.sort() 则不会引发异常。
此项变更使平台行为更加一致:现在,两种方法都会引发 ConcurrentModificationException。

现在,```AbstractCollection.removeAll() ```和 ```AbstractCollection.retainAll()``` 始终引发 ```NullPointerException```;之前,当集合为空时不会引发 ```NullPointerException``` 所以我们需要做判空处理。。
### 4.运行时权限
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

#### 所以总结下来,如果你之前是用什么权限就去申请什么权限,那么恭喜你,这个变化不会影响到你。
#### 如果你只申请了权限组中的某些权限,却用了同组的其他权限,那么你就需要去适配一下了。

那么怎么适配呢,如果你去检查之前每个申请权限的地方,未免太过麻烦。那么你可以根据你项目中的Manifest文件中需要的权限与权限组去对比,整理出你需要申请的各个权限组,然后申请该权限组
### 5.通知适配
8.0在通知这里变化还挺多的,比如通知渠道、通知标志、通知超时、背景颜色的等,详细的说明可以去看官方的Android 8.0 [功能和 API](https://developer.android.google.cn/guide/topics/ui/notifiers/notifications#ManageChannels)。
#### - 通知渠道:
Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。[详见 https://developer.android.com/training/notify-user/channels](https://developer.android.google.cn/training/notify-user/channels )
![image](http://upload-images.jianshu.io/upload_images/4974296-689527687f761e84.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以看到高德地图分的很细致,分为四个组共13个类别(华为貌似对组不生效)。这样有个好处,我们可以控制我们想收到的通知,比如我不喜欢运营活动通知,那我就可以把它关闭。

这样避免大量的不必要通知,否则使得用户觉得烦,一棒子打死。直接关闭你的允许通知。当然了,大量app都还没有适配,适配的也都分的不是很细致.

当然更重要的问题是,如果不去适配,可能通知都不会弹出来。那么适配的方法如下:

private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

    NotificationManager notificationManager = (NotificationManager)
            getSystemService(Context.NOTIFICATION_SERVICE);

    //分组(可选)
    //groupId要唯一
    String groupId = "group_001";
    NotificationChannelGroup group = new NotificationChannelGroup(groupId, "广告");

    //创建group
    notificationManager.createNotificationChannelGroup(group);

    //channelId要唯一
    String channelId = "channel_001";

    NotificationChannel adChannel = new NotificationChannel(channelId,
            "推广信息", NotificationManager.IMPORTANCE_DEFAULT);
    //补充channel的含义(可选)
    adChannel.setDescription("推广信息");
    //将渠道添加进组(先创建组才能添加)
    adChannel.setGroup(groupId);
    //创建channel
    notificationManager.createNotificationChannel(adChannel);

    //创建通知时,标记你的渠道id
    Notification notification = new Notification.Builder(MainActivity.this, channelId)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
            .setContentTitle("一条新通知")
            .setContentText("这是一条测试消息")
            .setAutoCancel(true)
            .build();
    notificationManager.notify(1, notification);

}

}

1
2
3
4
5
6
7
8
效果如下:

![image](http://upload-images.jianshu.io/upload_images/4974296-247c2245c0bc42f6.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
华为手机当只有一个渠道时,不会显示,会当做默认通知处理,除非一个以上。

注意:当Channel已经存在时,后面的createNotificationChannel方法仅能更新其name/description,以及对importance进行降级,其余配置均无法更新。所以如果有必要的修改只能创建新的渠道,删除旧渠道。

删除渠道代码如下:

private void deleteNotificationChannel(String channelId){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.deleteNotificationChannel(channelId);
}
}
`

6.后台限制执行

具体说明https://developer.android.google.cn/about/versions/oreo/background

应用在两个方面受到限制:

后台服务限制: 处于空闲状态时,应用可以使用的后台服务存在限制。 这些限制不适用于前台服务,因为前台服务更容易引起用户注意。

广播限制: 除了有限的例外情况,应用无法使用清单注册隐式广播。 它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。

在大多数情况下,应用都可以使用 JobScheduler 克服这些限制。 这种方式让应用安排为在未活跃运行时执行工作,不过仍能够使系统可以在不影响用户体验的情况下安排这些作业。

关于的用法可以参考官方例子:android-JobScheduler
https://github.com/googlesamples/android-JobScheduler

当然还有后台位置的限制需要去注意。
https://developer.android.google.cn/about/versions/oreo/background-location-limits

参考