一、背景
本篇介绍一下笔者在维护IM应用时,设置App角标的相关经验。同时这里设置角标都是基于系统厂商的Launcher,没有适配三方的Launcher应用,因为我们统计下来发现近些年使用三方Launcher应用比较少了,大部分用户还是以系统Launcher为主。所在在我们的项目中,主要是适配各个厂商。
二、厂商角标设置规则
华为
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);
荣耀
荣耀从华为独立之后,其设置角标的规则也进行更改,不过整体的改动不大:
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.hihonor.android.launcher.settings/badge/"), "change_badge", null, bundle);
vivo
Funtouch OS
申请权限
<!--funtouch os-->
<uses-permission android:name="com.vivo.notification.permission.BADGE_ICON" />
适配代码:
Intent intent = new Intent();
int missedCalls = 10;
intent.setAction("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
intent.putExtra("packageName", "com.android.xxxx");//接入方自己的包名
intent.putExtra("className", "com.android.xxxx.Mainxxxx");//对应接入方的launcher入口的activity全路径activity名字(AndroidManifest中标识了android.intent.category.LAUNCHER的activity)
intent.putExtra("notificationNum", missedCalls);
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
sendBroadcast(intent);
注意: 在ard8.0以后,还需要给Intent加上下面的Flag
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
如果此Flag获取不到,则改为此方法
public static int invokeIntconstants(String CanonicalName, String name, int default_value) {
int value = default_value;
try {
Class<?> c = Class.forName(CanonicalName);
Field Field = c.getField(name);
value = (int) Field.get(c);
} catch (Exception e) {
e.printStackTrace();
} finally {
return value;
}
}
Origin OS
确定系统版本
Class<?> spClass = Class.forName("android.os.SystemProperties");
Method method = spClass.getMethod("get", String.class, String.class);
method.setAccessible(true);
currentOsName= (String) method.invoke(null, "ro.vivo.os.name" ,defName);
currentOsVersion=(String) method.invoke(null, "ro.vivo.os.version" ,defVersion);
申请权限
<!--origin os-->
<uses-permission android:name="com.vivo.abe.permission.launcher.notification.num" />
设置代码
public static void setBadgeNumber() {
Uri uri = Uri.parse("content://" +"com.vivo.abe.provider.launcher.notification.num");
Bundle extra = new Bundle();
extra.putString("package", String);//接入的App包名
extra.putString("class", String);//接入的App class名
extra.putInt("badgenumber", int);//目标的角标数
/*这里一定要先使用 ContentProviderClient 建立非稳连接,不可以直接通过 getContentResolver()调用 call 方法,会有 Server 端崩溃带崩 Client 端的风险*/
ContentProviderClient client = null;
try {
client = getContentResolver().acquireUnstableContentProviderClient(uri);
if (client != null) {
int result = client.call("change_badge", null, extra).getInt("result");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.N){ client.close();
} else {
client.release();
}
}
}
}
魅族
申请权限
<!--魅族角标-->
<uses-permission android:name="com.meizu.flyme.launcher.permission.WRITE_BADGE_EXTRAS"/>
设置代码
Bundle extra = new Bundle();
extra.putString("package", context.getPackageName());
extra.putString("class", "yourLauncherClassName");
extra.putInt("badge_number", number);
context.getContentResolver().call(Uri.parse("content://" + "com.meizu.flyme.launcher.app_extras" + "/badge_extras"), "change_badge", null, extra);
OPPO
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();
}
}
private static boolean canResolveBroadcast(Context context, Intent intent) {
PackageManager packageManager = context.getPackageManager();
List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
return receivers != null && receivers.size() > 0;
}
小米
小米手机比较特殊,其App角标与App通知相关联,无法脱离通知栏独立设置角标未读数量。
三星
public static void setBadgeNumber(Context context, int number) {
Intent localIntent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
//数字
localIntent.putExtra("badge_count", number);
//包名
localIntent.putExtra("badge_count_package_name", context.getPackageName());
//启动页
localIntent.putExtra("badge_count_class_name", BadgeNumberManager.getLauncherClassName(context));
context.sendBroadcast(localIntent);
}
sony
public static void setBadgeNumber(Context context, int number) {
boolean isShow = true;
if ("0".equals(number)) {
isShow = false;
}
Intent localIntent = new Intent();
//是否显示
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", isShow);
localIntent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
//启动页
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", BadgeNumberManager.getLauncherClassName(context));
//数字
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", number);
//包名
localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());
context.sendBroadcast(localIntent);
}
三、总结&注意事项
封装建议
以上各个厂商设置角标的方式都不同,可以考虑封装成不同的策略,基于Build.MANUFACTURER
字段来选择不同的策略,使上层无感知。
注意事项
在实践过程中,我们发现频繁设置角标可能会引发卡顿。
起因是我们的卡顿监控发现部分卡顿堆栈竟然是设置角标,经过排查发现用户接收消息非常频繁。而我们的策略是只要应用内的未读数发生变化,那么就会立刻更新角标的未读数。这就导致可能会非常频繁的更新应用角标。
发现此问题后,我们调整了策略。优化后的策略是当App在前台时,不更新角标。当App切换到后台再统一更新,这样可以大大的减少更新频率。优化策略后线上由于频繁更新角标引发的卡顿也就消失了。
总结
本篇主要是总结一下各个厂商的角标适配代码,以及过程中我们遇到的问题。读者遇到相关需求直接拿来使用就好,不过厂商也可能会随时更新,使用时也请做好测试。