做者:小强 贝聊移动开发部 Android工程师 前言:本文主要讲述了如下三方面:android
先放一个传送门:GitHub传送门git
BadgeNumber
)显示的。咱们目前看到的能支持应用桌面角标显示的Android系统,都是第三方厂商本身定制的。经过实现一套本身的
launcher
而且提供外部接口给第三方应用来调用便可。
咱们公司的APP里涉及到IM的功能。因此常常会有用户向客服反馈,为何某Q、某信都支持应用桌面角标的显示,但大家的APP却不行......本着用户就是上帝的原则,因而应用桌面角标显示的优化就提上了日程。其实,测试部门在以前就已经跟咱们提过这事了,只不过当时正忙于项目开发,没时间优化。前段时间需求很少的时候,给公司的Android应用加上了桌面角标显示的支持。如今将这个优化的过程总结一下。github
若是你们有接触过这方面的优化,应该很快就能够在搜索引擎上找到某个被推荐次数较多的开源库 ShortcutBadger。bash
虽然这个库适配的覆盖机型貌似不少,但在实际的测试中发现,某些方法可能对于目前市面上的国产流行机型已经不奏效了。因此,不建议你们直接将这个开源项目用到项目中去。做为学习和参考却是一个不错的选择。并且,在实际方案抉择的过程当中,咱们发现,公司的APP主流机型排行榜中,前十的机型几乎被OPPO、vivo、华为、小米这四个品牌屠榜了。因此,咱们的优化目标暂时就先定下来了:先集中精力适配市面上的这四个主流品牌机型。其余的冷门机型,后面再慢慢完善。(其实实际上咱们也找不来那么多冷门的机型进行测试,因此对于没自身确认过奏效的方案,即便网上已经有人给出,出于谨慎仍是先不采纳)微信
在开始以前,先声明一下。第一,不是全部的国产手机都能找到支持角标显示的方案(即便理论上能够,可能人家只对某Q某信等一些国民级的应用开放设置应用角标的白名单)。第二,本文中涉及到的方案都是通过实际测试且奏效的了(由于测试手机有限,因此不敢说针对这四个品牌的手机机型百分百支持,但支持大部分的机型应该是没问题的)。并且,有些品牌的手机适配方案很容易找到,有些品牌的适配方案则很难找到,这部分我会放到后面的章节来讲。下面直接上适配方案:app
先在AndroidManifest
文件里配置好下面的权限:ide
<!--华为手机更新应用桌面角标须要的权限-->
<uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
复制代码
设置角标的方法以下:学习
public static void setBadgeNumber(Context context, int number) {
try {
if (number < 0) number = 0;
Bundle bundle = new Bundle();
bundle.putString("package", context.getPackageName());
String launchClassName = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().getClassName();
bundle.putString("class", launchClassName);
bundle.putInt("badgenumber", number);
context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
public static void setBadgeNumber(Context context, int number) {
try {
if (number == 0) {
number = -1;
}
Intent intent = new Intent("com.oppo.unsettledevent");
intent.putExtra("pakeageName", context.getPackageName());
intent.putExtra("number", number);
intent.putExtra("upgradeNumber", number);
if (canResolveBroadcast(context, intent)) {
context.sendBroadcast(intent);
} else {
try {
Bundle extras = new Bundle();
extras.putInt("app_badge_count", number);
context.getContentResolver().call(Uri.parse("content://com.android.badge/badge"), "setAppBadgeCount", null, extras);
} catch (Throwable t) {
t.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean canResolveBroadcast(Context context, Intent intent) {
PackageManager packageManager = context.getPackageManager();
List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
return receivers != null && receivers.size() > 0;
}
复制代码
public static void setBadgeNumber(Context context, int number) {
try {
Intent intent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
intent.putExtra("packageName", context.getPackageName());
String launchClassName = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().getClassName();
intent.putExtra("className", launchClassName);
intent.putExtra("notificationNum", number);
context.sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
小米的设置应用角标方式比较有个性,跟其余厂商的不太同样,是跟Notification
绑定在一块儿的。并且小米系统还有个比较特殊的地方,若是在应用内直接调用设置角标的方法,设置角标会不生效,因此只能在应用在后台而且收到推送的状况下进行角标的设置。另外,即便你设置了角标的显示,只要用户点击应用图标进入到应用内,应用的角标就会自动消失掉,即便应用内还存在新的未读消息。因此,针对小米机型,建议在收到推送后而且进行notification的时机更新应用角标。测试
//在调用NotificationManager.notify(notifyID, notification)这个方法以前先设置角标显示的数目
public static void setBadgeNumber(Notification notification, int number) {
try {
Field field = notification.getClass().getDeclaredField("extraNotification");
Object extraNotification = field.get(notification);
Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
method.invoke(extraNotification, number);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
在上面的适配方案中,最容易找到并且奏效的就是华为和小米的适配方案。而OPPO的适配方案,即便找到了,在现有的测试机型上却不奏效;vivo的适配方案则是最难找的。既然在网上找不到,而在国内某Q和某信貌似又是适配得最好的,这就说明,某Q和某信的源码里确定有现成的解决方案。那么,不如尝试一下反编译,看看能不能从这两个APP中找到一些灵感?优化
在对某Q的apk进行反编译后,在某个类下果真找到了设置应用角标的实现类:
从上图能够看出,某Q对于各类厂商的适配算是比较完善的了。除了小米、华为、OPPO、vivo,还适配了联想、三星、索尼等。
不一样机型的适配方法也都有具体的实现:(下面是对于OPPO和vivo的适配)
可是,咱们也不能直接拷贝过来就使用。由于说不定有些方法只针对某Q才生效呢是吧?
在对某信的apk进行反编译后,也能找到关于应用角标适配的代码:
总之,对比了一下某Q和某信的源码,在某些机型的适配方式上,可能两边会有些出入。实现方式可能也不太同样。但不得不说,不愧是大厂的APP,看了源码后,实在是学习了不少,特别是一些细节上的处理。
上面总结出的适配方案,其实就是在参考了网上各类资料以及某Q和某信的源码以后总结出来的可行的适配方案。若是还不知足你们的需求,你们能够发挥一下本身的主观能动性,找到本身想要的解决方案,并总结出一套属于本身的适配方案。
看完了某Q和某信的源码后,我发现两边都有一个共同点,那就是某个实现类里塞了不少适配的方法。估计也是可能涉及到不一样的人在不一样时期维护的历史缘由。但一个类里面的代码太多了,可能会对查阅和后续维护形成一些不便。
这里,我参考了Android源码里面NotificationManagerCompat
这个类的实现方式。Android源码中自己就涉及到不少关于不一样版本的适配的场景。某个方法,在不一样的版本下,可能实现方式不太同样。因而,怎么在不断往某个类增长不一样的实现方式的状况下,保持代码的美观以及扩展性易读性变成了一个问题。NotificationManagerCompat
这个类的实现就十分简洁美观。下面是一部分源码截图,有兴趣的能够直接去看一下完整的源码。
下面就是模仿后的实现:
public class BadgeNumberManager {
private Context mContext;
private BadgeNumberManager(Context context) {
mContext = context;
}
public static BadgeNumberManager from(Context context) {
return new BadgeNumberManager(context);
}
private static final BadgeNumberManager.Impl IMPL;
/**
* 设置应用在桌面上显示的角标数字
* @param number 显示的数字
*/
public void setBadgeNumber(int number) {
IMPL.setBadgeNumber(mContext, number);
}
interface Impl {
void setBadgeNumber(Context context, int number);
}
static class ImplHuaWei implements Impl {
@Override
public void setBadgeNumber(Context context, int number) {
BadgeNumberManagerHuaWei.setBadgeNumber(context, number);
}
}
static class ImplVIVO implements Impl {
@Override
public void setBadgeNumber(Context context, int number) {
BadgeNumberManagerVIVO.setBadgeNumber(context, number);
}
}
static class ImplBase implements Impl {
@Override
public void setBadgeNumber(Context context, int number) {
//do nothing
}
}
static {
String manufacturer = Build.MANUFACTURER;
if (manufacturer.equalsIgnoreCase("Huawei")) {
IMPL = new ImplHuaWei();
} else if (manufacturer.equalsIgnoreCase("vivo")) {
IMPL = new ImplVIVO();
} else if (manufacturer.equalsIgnoreCase("XXX")) {
//其余品牌机型的实现类
IMPL = new ImplXXX();
......
} else {
IMPL = new ImplBase();
}
}
}
复制代码
使用的时候,只须要调用BadgeNumberManager.from(context).setBadgeNumber(num)
就好了。BadgeNumberManagerHuaWei
、BadgeNumberManagerVIVO
等都是针对某个手机品牌的具体实现类。
固然,这只是一种实现的思路而已。具体去实现的时候,请根据本身项目的实际状况,怎样实现扩展性可读性较高就选哪一种。
为了响应部分童鞋的要求,现已将BadgeNumberManager
的实现源码上传到了GitHub: beiliao-mobile:BadgeNumberManager
若是有关于别的机型的适配方案,欢迎在评论下留言(最好是本身亲自测试过而且有效的)。若是文章中有出现错误的地方,欢迎指正。若是对于文章中的某些部分有不一样的理解和想法,或者有更好的想法, 也欢迎留言讨论。
注:本文仅供技术学习探讨使用。若是文中有不适宜的内容,请联系咱们,咱们会第一时间处理:)
通过测试,目前暂时不支持的机型:华为荣耀六、OPPO A5九、OPPO R9,OPPO R十一、vivo X9i(截止至2017.12.11)
一开始觉得某些机型不支持多是少了某些跟角标设置相关的权限,因而反编译微信、QQ、支付宝,从这些App中收集AndroidManifest
里配置的可能跟角标设置相关的权限,并添加到Demo中来测试,后来发现仍是不行
针对华为手机,在某些机型上,例如华为 mate9,在manifest
里除了须要配置com.huawei.android.launcher.permission.CHANGE_BADGE
权限以外,还须要配置android.permission.INTERNET
权限才能够正常设置桌面角标(不过通常的App应该都会配置了android.permission.INTERNET
权限)
关于OPPO手机,在一些较旧的机型上能够正常设置桌面角标,但在一些比较新的机型上(例如OPPO R9,OPPO R11等),只有在通知权限管理中,有“在桌面图标上显示角标”这个选项的App才能够正常设置角标。目前就只发现QQ,微信,钉钉有这个权限,就连支付宝都没有这个权限。因而尝试着写了个Demo,将Demo的包名改为了微信的包名,而后在通知权限管理中,就出现了“在桌面图标上显示图标”这个选项。因此,在新的机型上,OPPO应该是根据包名来维护了一个白名单,只针对一些比较大型的IM类型的App开放桌面角标设置的权限。因此,这个问题暂时尚未解决方法