Android8.0通知栏和悬浮窗新改动
通知栏
Android8.0新引入了Notification Channels(Notification Channels),以提供统一的系统来帮助用户管理通知,如果使用的8.0系统,必须调用Notification.Builder.setChannelId(String channelId)来设置渠道id。
具体可以参考Android O Preview 之 通知渠道
设置ChannelId的具体代码(Android8.0下不需要设置这个ChannelId)
// 通知渠道的id
String id = "my_channel_01";
// 用户可以看到的通知渠道的名字.
CharSequence name = getString(R.string.channel_name);
// 用户可以看到的通知渠道的描述
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
//注意Name和description不能为null或者""
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
// 配置通知渠道的属性
mChannel.setDescription(description);
// 设置通知出现时的闪灯(如果 android 设备支持的话)
mChannel.enableLights(true);
mChannel.setLightColor(Color.RED);
// 设置通知出现时的震动(如果 android 设备支持的话)
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
//最后在notificationmanager中创建该通知渠道 notificationManager.createNotificationChannel(mChannel);
builder.setChannelId(id);
展示一个简单的通知栏的完整代码:
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(this.getApplicationContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 通知渠道的id
String id = "my_channel_01";
// 用户可以看到的通知渠道的名字.
CharSequence name = getString(R.string.channel_name);
// 用户可以看到的通知渠道的描述
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_HIGH;
//注意Name和description不能为null或者""
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
// 配置通知渠道的属性
mChannel.setDescription(description);
// 设置通知出现时的闪灯(如果 android 设备支持的话)
mChannel.enableLights(false);
mChannel.setLightColor(Color.RED);
// 设置通知出现时的震动(如果 android 设备支持的话)
mChannel.enableVibration(false);
mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
//最后在notificationmanager中创建该通知渠道
notificationManager.createNotificationChannel(mChannel);
builder.setChannelId(id);
}
builder.setContentInfo("补充内容");
builder.setContentText("通知标题");
builder.setContentTitle("通知内容");
builder.setSmallIcon(R.mipmap.icon);
builder.setTicker("新消息");
builder.setAutoCancel(true);
builder.setWhen(System.currentTimeMillis());
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
notificationManager.notify(1, notification);
悬浮窗
如果你的项目targetSdkVersion是在23及以上,在Android6.0以上的手机想要实现悬浮窗这个功能就必须要加入android.permission.SYSTEM_ALERT_WINDOW这个权限,并且通过Android6.0后提供的权限申请方法来申请权限成功后再实现悬浮窗功能。当然国产某些Rom也会导致虽然targetSdkVersion低于23或者系统低于6.0,但还是需要SYSTEM_ALERT_WINDOW权限才能调用悬浮窗。
这里我就不详讲低于Android8.0的手机怎么去申请悬浮窗权限了,大家可以参考下Android悬浮窗权限适配来实现悬浮窗的权限适配。
相信的大家在完成上述权限适配后会发现基本在各种手机都会运行都能顺利申请到悬浮窗权限并且展示自己的悬浮窗,但却在8.0上发现运行悬浮窗会报出以下的Crash:
// xxxx为你选择WindowManager.LayoutParams()的 mParams.type具体参数
W/System.err: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@b5ad7ed -- permission denied for window type xxxx
W/System.err: at android.view.ViewRootImpl.setView(ViewRootImpl.java:788)
W/System.err: at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
W/System.err: at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:92)
...
为什么明明权限已经申请成功,在低版本的手机上会运行的好好的,在Android8.0上会这样呢?
原来是因为8.0新增了一些行为变更Android 8.0 行为变更,对悬浮窗进行了一些限制:
提醒窗口
使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。
所以我们只需要在为WindowManager.LayoutParams()设置type的时候改为:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
//xxxx为你原来给低版本设置的Type
mParams.type = WindowManager.LayoutParams.xxxx;
}
这样就可以适配到8.0的手机上使用悬浮窗功能啦~
具体详细原理在日后有时间再补上,目前先写到这里。