Android Badge技术分析

Badge红点最初来自于IOS的UX设计之中,早期版本的Android原生并没有这个设计,从Android O(SDK 26)开始,Google才开始提供官方的API。在这之前我们看到的各种手机系统上的Badge实际上是各个手机厂商的Rom中,自己对Launcher添加了这个Feature,因此也造成了一些问题,比如碎片化严重,没有统一的API。APP如果要使用Badge需要针对不同厂商的ROM进行定向开发。下面是目前主流的手机厂商Badge功能的具体实现。

API 26之前:

在API 26之前,由于没有统一的系统API,需要对判断手机型号,并定向处理。主要的方式有三种:第一种是通过反射调用厂商自己定义的framework方法(如小米),第二种是发送Badge的对应广播(如三星),第三种是通过ContentProvider实现(如华为)。有的手机ROM甚至还要添加特定的权限才能够生效(如华为)。根据测试结果,实际还是有很多手机上无法生效。一方面可能是因为一些手机ROM中没有Badge的Feature,另一方面是厂商对这一权限有比较严格的限制,比如设置白名单,默认禁止通知等。

@Unreliable
public class NotificationBadgeUtil {public static final String TAG = "NotificationBadgeUtil";private static final String SYSTEM_XIAOMI = "XIAOMI";private static final String SYSTEM_SAMSUNG = "SAMSUNG";private static final String SYSTEM_HUAWEI_HONOR = "HONOR";private static final String SYSTEM_HUAWEI = "HUAWEI";private static final String SYSTEM_NOVA = "NOVA";private static final String SYSTEM_SONY = "SONY";private static final String SYSTEM_VIVO = "VIVO";private static final String SYSTEM_OPPO = "OPPO";private static final String SYSTEM_LG = "LG";private static final String SYSTEM_ZUK = "ZUK";private static final String SYSTEM_HTC = "HTC";private static boolean hasInit = false;private static String OSName = null;/*** The static method to show a badge while using notification.** Google provide system API for badges works in launcher since Android O,* if SDK version is older than Android O, you can using variant phone custom* launcher badge ways like dealing in this method. But some specific phone* models seems don`t support system API even though API over Android O, like* my HUAWEI P9 Android O.** @param context Context for notification dependence.* @param notification Notification object correspond to badge.* @param NOTIFICATION_ID The notification channel id.* @param num The count of badges. Cancel notification if count is 0.*/public static void showBadge(Context context, Notification notification, int NOTIFICATION_ID, int num) {if (!hasInit) {init();}OSName = Build.BRAND.trim().toUpperCase();if (notification != null) {if (num < 0) num = 0;if (num > 99) num = 99;Log.e("system_name", OSName);if (OSName != null) {if (OSName.equals(SYSTEM_XIAOMI)) {setBadgeOfXiaomi(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_SAMSUNG) || OSName.equals(SYSTEM_LG)) {setBadgeOfSamsung(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_HUAWEI_HONOR) || OSName.equals(SYSTEM_HUAWEI)) {setBadgeOfHuaWei(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_SONY)) {setBadgeOfSony(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_VIVO)) {setBadgeOfVIVO(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_OPPO)) {setBadgeOfOPPO(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_ZUK)) {setBadgeOfZUK(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_HTC)) {setBadgeOfHTC(context, notification, NOTIFICATION_ID, num);} else if (OSName.equals(SYSTEM_NOVA)) {setBadgeOfNOVA(context, notification, NOTIFICATION_ID, num);} else {setBadgeOfDefault(context, notification, NOTIFICATION_ID, num);}}}}private static void init() {OSName = android.os.Build.BRAND.trim().toUpperCase();hasInit = true;}private static void setBadgeOfXiaomi(final Context context, final Notification notification, final int NOTIFICATION_ID, final int num) {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {try {Field field = notification.getClass().getDeclaredField("extraNotification");Object extraNotification = field.get(notification);Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);method.invoke(extraNotification, num);} catch (Exception e) {e.printStackTrace();Log.e("Xiaomi" + " Badge error", "set Badge failed");}NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);}}, 550);}private static void setBadgeOfSamsung(Context context, Notification notification, int NOTIFICATION_ID, int num) {String launcherClassName = getLauncherClassName(context);if (launcherClassName == null) {return;}try {Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");intent.putExtra("badge_count", num);intent.putExtra("badge_count_package_name", context.getPackageName());intent.putExtra("badge_count_class_name", launcherClassName);context.sendBroadcast(intent);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("SAMSUNG" + " Badge error", "set Badge failed");}}/*Need permission for HUAWEI models:<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />*/private static void setBadgeOfHuaWei(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {Bundle localBundle = new Bundle();localBundle.putString("package", context.getPackageName());localBundle.putString("class", getLauncherClassName(context));localBundle.putInt("badgenumber", num);context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, localBundle);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("HUAWEI" + " Badge error", "set Badge failed");}}private static void setBadgeOfSony(Context context, Notification notification, int NOTIFICATION_ID, int num) {String numString = "";String activityName = getLauncherClassName(context);if (activityName == null) {return;}Intent localIntent = new Intent();boolean isShow = true;if (num < 1) {numString = "";isShow = false;} else if (num > 99) {numString = "99";}try {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", activityName);localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", numString);localIntent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", context.getPackageName());context.sendBroadcast(localIntent);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("SONY" + " Badge error", "set Badge failed");}}private static void setBadgeOfVIVO(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {Intent localIntent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");localIntent.putExtra("packageName", context.getPackageName());localIntent.putExtra("className", getLauncherClassName(context));localIntent.putExtra("notificationNum", num);context.sendBroadcast(localIntent);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("VIVO" + " Badge error", "set Badge failed");}}private static void setBadgeOfOPPO(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {if (num == 0) {num = -1;}Intent intent = new Intent("com.oppo.unsettledevent");intent.putExtra("pakeageName", context.getPackageName());intent.putExtra("number", num);intent.putExtra("upgradeNumber", num);if (canResolveBroadcast(context, intent)) {context.sendBroadcast(intent);} else {try {Bundle extras = new Bundle();extras.putInt("app_badge_count", num);context.getContentResolver().call(Uri.parse("content://com.android.badge/badge"), "setAppBadgeCount", null, extras);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Throwable th) {Log.e("OPPO" + " Badge error", "unable to resolve intent: " + intent.toString());}}} catch (Exception e) {e.printStackTrace();Log.e("OPPO" + " Badge error", "set Badge failed");}}private static void setBadgeOfZUK(Context context, Notification notification, int NOTIFICATION_ID, int num) {final Uri CONTENT_URI = Uri.parse("content://com.android.badge/badge");try {Bundle extra = new Bundle();extra.putInt("app_badge_count", num);context.getContentResolver().call(CONTENT_URI, "setAppBadgeCount", null, extra);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("ZUK" + " Badge error", "set Badge failed");}}private static void setBadgeOfHTC(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {Intent intent1 = new Intent("com.htc.launcher.action.SET_NOTIFICATION");intent1.putExtra("com.htc.launcher.extra.COMPONENT", context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()).getComponent().flattenToShortString());intent1.putExtra("com.htc.launcher.extra.COUNT", num);Intent intent = new Intent("com.htc.launcher.action.UPDATE_SHORTCUT");intent.putExtra("packagename", context.getPackageName());intent.putExtra("count", num);if (canResolveBroadcast(context, intent1) || canResolveBroadcast(context, intent)) {context.sendBroadcast(intent1);context.sendBroadcast(intent);} else {Log.e("HTC" + " Badge error", "unable to resolve intent: " + intent.toString());}NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("HTC" + " Badge error", "set Badge failed");}}private static void setBadgeOfNOVA(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {ContentValues contentValues = new ContentValues();contentValues.put("tag", context.getPackageName() + "/" + getLauncherClassName(context));contentValues.put("count", num);context.getContentResolver().insert(Uri.parse("content://com.teslacoilsw.notifier/unread_count"), contentValues);NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("NOVA" + " Badge error", "set Badge failed");}}private static void setBadgeOfDefault(Context context, Notification notification, int NOTIFICATION_ID, int num) {try {Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");intent.putExtra("badge_count", num);intent.putExtra("badge_count_package_name", context.getPackageName());intent.putExtra("badge_count_class_name", getLauncherClassName(context));if (canResolveBroadcast(context, intent)) {context.sendBroadcast(intent);} else {Log.e("Default" + " Badge error", "unable to resolve intent: " + intent.toString());}NotificationManager notifyMgr = (NotificationManager) (context.getSystemService(NOTIFICATION_SERVICE));if (num != 0) notifyMgr.notify(NOTIFICATION_ID, notification);else notifyMgr.cancel(NOTIFICATION_ID);} catch (Exception e) {e.printStackTrace();Log.e("Default" + " Badge error", "set Badge failed");}}private static String getLauncherClassName(Context context) {PackageManager packageManager = context.getPackageManager();Intent intent = new Intent(Intent.ACTION_MAIN);intent.setPackage(context.getPackageName());intent.addCategory(Intent.CATEGORY_LAUNCHER);ResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);if (info == null) {info = packageManager.resolveActivity(intent, 0);}return info.activityInfo.name;}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;}
}

API 26及之后:

Google在Android O中终于添加了Badge的API。只需要在mShowBadge为true的NotificationChannel中发送notification即可实现Badge。官方文档地址:https://developer.android.com/training/notify-user/badges

            val mChannel = NotificationChannel(_channelId, packageName, NotificationManager.IMPORTANCE_LOW).apply {description = ""setShowBadge(true)}val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.createNotificationChannel(mChannel).....................val notification = NotificationCompat.Builder(this@BadgeTestActivity, _channelId).setContentTitle("New Messages").setContentText("You've received 3 new messages.").setSmallIcon(R.drawable.notification_icon_background).setNumber(count).build().......................val notifyMgr = getSystemService(NOTIFICATION_SERVICE) as NotificationManagernotifyMgr.notify(0, notification)

但是需要注意的是,并非所有的Android O以上的系统都可以使用Google提供的API来实现Badge。我手边的两台Android O手机中,三星S9是可以的,而华为P9则不行。华为P9即使在8.0系统上,依旧需要使用Android O之前的方法来实现Badge,Google的API并不会生效。猜测原因是华为的Launcher Badge并没有去支持Google的API,而是依旧使用自己ROM里面之前的实现方法。

Demo地址(存放在我的Framework库中,参见NotificationBadgeUtil.java):

https://github.com/Haocxx/HaocxxFrameworkForAndroid

参考资料:

http://www.androidchina.net/4093.html

https://github.com/Cedric-Xuan/SNSNotificationBadge

[Android] Android Badge技术分析相关推荐

  1. Android系统内核cpufreq技术分析

    一.Andorid系统内核cpufreq技术机制介绍 1.什么是变频技术: 主流CPU都支持变频(frequency scaling)技术,如美国的Intel CPU 支持的Enhanced Spee ...

  2. miui与android的不同,技术分析:android OS和MIUI OS的不同

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 目录结构为了完整性,我还是简单地介绍一下: 1. META-INF 这里面主要是签名文名和刷机时执行的脚本(updater-script),移植时,我们主 ...

  3. Android Java虚拟机拦截技术分析

    2019独角兽企业重金招聘Python工程师标准>>> 最近反编译金山毒霸,分析其广告拦截功能是如何实现的.根据金山毒霸的介绍,采用了java虚拟机拦截技术,带着好奇去研究了一下.在 ...

  4. Android安全 Hook技术,AndroidHook技术分析.pdf-北京理工大学信息系统及安全对抗实验中心.PDF...

    AndroidHook技术分析.pdf-北京理工大学信息系统及安全对抗实验中心.PDF The name of the DepartmentBeijing Forest Studio 北京理工大学信息 ...

  5. 《纯技术分析阿里云OS和Android之间的关系》补充

    前面一份投稿的错误很抱歉,rom本身就找错了,用了一个AOSP-like的刷机包.好在这份内容错误的投稿多少起到了抛砖引玉的作用,当天晚上,就给出了正确的rom的分析<纯技术分析阿里云OS和An ...

  6. Android Gallery2技术分析

    概述   图库和相机在内部实现上其实是共用的一个app,即Gallery2.它们是Gallery2的两个不同的入口.图库的主要功能是展示和管理设备中的或者网络上的媒体内容(照片.视频),虽然名字是图库 ...

  7. Android动画技术分析

    Android动画技术分析 本文将介绍Android动画的实现技术.Android动画实现有三种技术,逐帧动画(Drawable Animation),补间动画(View Animation)和属性动 ...

  8. 《App研发录:架构设计、Crash分析和竞品技术分析》— Android 书籍

    文章目录 第1章 重构,夜未眠 3 第2章 Android网络底层框架设计 19 第3章 Android经典场景设计 53 第4章 Android命名规范和编码规范 83 第5章 Crash异常收集与 ...

  9. 【错误记录】Android 编译时技术报错 ( 注解处理器 process 方法多次调用问题 )

    文章目录 一.报错信息 二.问题分析 三.解决方案 注解处理器 AbstractProcessor 中的 process 方法可能会调用多次 , 在生成代码时 , 一定要注意 , 检测到 注解节点 后 ...

最新文章

  1. kali2020进入单模式_2021神途手游:超级变态单职业
  2. postgresql数据库增删改:使用pgadmin以及SQL语句来实现
  3. 罗小黑用flash做的_董小姐说电影丨这次都听我的,去看《罗小黑战记》
  4. 混合精度训练-Pytorch
  5. 1.10 编程基础之简单排序_10 单词排序
  6. vscode 结束_21 个VSCode 快捷键,让代码更快,更有趣
  7. 萝卜魂军曹机器人_【BANDAI】萝卜魂 全金属狂潮TSR RK-92 野蛮人 沙色
  8. 恕我直言,你们的模型训练都还不够快
  9. 移植 Qt4.8.5到Tiny210
  10. C语言小案例_关于爱普生喷墨机APG复位错误(APG reset error)的最终答案: 故障案例 每日一例 【第1358篇】...
  11. 程序员,对自己好一点
  12. X264实现H264编码以及MediaMuxer的另类用法「第八章,Android音视频编码那点破事」
  13. ffmpeg解复用FLV文件
  14. opengl绘制卡通人物哆啦A梦
  15. 7-2 符号配对 (20 分) c语言版
  16. PKUSC 模拟赛 day1 下午总结
  17. ECCV2016 部分文章
  18. 【NDN基础】Information-Centric Networking: A Natural Design for Social Network Applications 全文翻译
  19. 开源代码和软件汇总!激光雷达和相机联合标定(2004-2021)
  20. Unity游戏开发客户端面经——热更新(初级)

热门文章

  1. 深度学习评价指标总结及代码实现
  2. 数据丢了谁负责,我来帮你算算账!
  3. 马航发布会确认MH370失事:这是史无前例的悲剧
  4. Jedis和JedisPool的使用
  5. 【Shiro】权限控制注解
  6. matlab dpsk,Matlab之DPSK
  7. 2012年图灵技术图书大盘点
  8. 机器人潘森护盾_最实用的护盾技能,潘森被动上榜,第一比别人对一条命
  9. 介绍一款私有云存储、企业网盘、云盘软件
  10. MSVCR140.dll文件丢失?程序无法运行?解决办法!!!