安卓应用角标那些事儿
rj4806
8年前
<h2>什么是应用角标?</h2> <p>应用角标最开始是在ios系统中出现的,大概长这样:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/f5dcc2ce5a984d7ad62269e25ff51e42.jpg"></p> <p style="text-align:center">角标.jpg</p> <p>不知道从什么时候开始,国内各大安卓手机系统上,也慢慢出现了应用角标的身影,到现在几乎成为了安卓系统的标配,发张图片让大家近距离感受一下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/8bcf1bf26d4409ba9e9a36a0f4d9a832.png"></p> <p style="text-align:center">screenshot.png</p> <p>但是有一点要特别提一下,那就是 原生的Android系统,是不支持应用角标的 (这也是文章标题叫“安卓角标”而不是“Android角标”的原因)。毕竟应用角标是一个带有争议的设计,有些强迫症用户对应用角标咬牙切齿,有些用户看不到应用角标又浑身痒痒,对于这一点我也只能说“仁者见仁,智者见智”吧。</p> <h2>如何支持应用角标?</h2> <p>安卓应用的角标是由Launcher支持的,而原生的Android系统Launcher并没有提供角标支持,所以各大手机厂商只能自己定制Launcher来实现,然后提供接口给外部使用。主流的做法都是通过广播Intent的形式来提供接口,不同的手机系统所支持的广播Intent也不太一样,所以需要针对不同的手机系统来做适配(恭喜你又成功跳入一个系统适配的坑!)。</p> <h2>角标开源库介绍</h2> <p>相信每个安卓开发者看到系统适配这几个字,都不会不由自主喷出一口老血。。。但是这次不用慌,因为已经有不怕死的家伙为我们造好了轮子,接下来就为大家隆重推出 <a href="/misc/goto?guid=4959740084759230906" rel="nofollow,noindex">ShortcutBadger</a> (github大法好啊~真是好~)。这个开源库为我们封装了各种系统适配的细节,如文档所描述的,拥有了ShortcutBadger,你也就免除了以下系统适配的烦恼:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/989eca40f7a45f24740a613796610260.jpg"></p> <p style="text-align:center">角标02.png</p> <p>但在实际使用时,你会发现,图片很美,现实却很残酷!经不完全测试(原谅我的“不完全”,毕竟安卓机型碎片化太过严重),我发现这个开源库对于小米、华为、vivo和oppo等主流机型的支持都是有各种问题的,要么完全不奏效,要么就有bug。当然这个开源库也在不断完善,但是在它还不够稳定的情况下,建议大家还是先用来当备选方案,支持一些冷门机型可以,主流机型还是自己适配比较靠谱点。</p> <h2>特殊机型适配</h2> <h3>华为系列</h3> <p>传送门: <a href="/misc/goto?guid=4959740084862332512" rel="nofollow,noindex">华为角标官方文档</a></p> <p>按照华为官方文档的介绍,如果要获得应用角标支持,需要为应用申请华为证书,然后在“权签平台”下载开发者证书,再将开发者证书集成到应用中,整个流程跑下来还是很繁琐的。</p> <p>那么吃瓜群众肯定就要问了,有没有简单一点的方法呢?答案当然是有的!结合华为的《角标开发指导书》中的代码参考和亲身实践,我发现只需要其中两步,也可以实现华为角标支持(目前只验证了华为mate8 和华为 p7,其他机型手头暂时没有,欢迎大家补充):</p> <ol> <li>在AndroidManifest.xml中申请华为角标权限 <pre> <code class="language-java"><uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" /></code></pre> </li> <li>参考以下代码,设置应用角标即可 <pre> <code class="language-java">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", msgCount); context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bundle);</code></pre> </li> </ol> <h3>小米系列</h3> <p>传送门: <a href="/misc/goto?guid=4959740084940676744" rel="nofollow,noindex">小米角标官方文档</a></p> <p>在小米机型上,系统默认是支持应用角标的,不过支持的方式非常反人类,导致经常有用户来反馈说角标数字不对的问题。小米官方文档关于默认情况是这么描述的:</p> <p>当app向通知栏发送了一条通知 (通知不带进度条并且用户可以删除的),那么桌面app icon角标就会显示1。此时app显示的角标数是和通知栏里app发送的通知数对应的,即向通知栏发送了多少通知就会显示多少角标。</p> <p>那么吃瓜群众肯定又要问了,What are you talking about?!不要慌,我举一个例子大家就明白了,比如说千牛客户端,每收到一条未读的聊天消息,就会往通知栏发送一条通知(由于业务需要,通知id都是同一个),假如用户收到了5条未读消息,理论上来说千牛的角标数字应该显示5,但是在小米机型上你会惊奇地发现角标数字居然是1。这是因为默认情况下,应用的角标数字跟应用往通知栏发送的通知数( 注意!这个通知数是根据通知id来计算的 )相对应的,在刚刚说的场景中, 虽然千牛往通知栏发送了5条通知,但是按照通知id来统计时,对应的通知数就是1,所以角标数字也就显示成1了 。</p> <p>小米官方可能也意识到了,这个奇葩的设计会导致很多用户产生困惑,所以在官方文档中也提供了修改角标数的接口。不同于其他手机系统通过广播来修改角标,小米系统是通过反射机制来修改角标,关键代码参考如下:</p> <pre> <code class="language-java">Field field = notification.getClass().getDeclaredField("extraNotification"); Object extraNotification = field.get(notification); Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class); method.invoke(extraNotification, unreadCount);</code></pre> <h3>OPPO系列</h3> <p>抱歉,没有发现官方文档。</p> <p>联系过oppo的开发者客服,客服MM表示:</p> <p>oppo角标提醒目前只针对内部软件还有微信、QQ开放,其他的暂时无法提供</p> <p>啊,多么痛的领悟~不过客服MM也及时发了一张好人卡:</p> <p>后续应该会计划对其他应用开放的,敬请关注我们的开发平台</p> <p>嗯,虽然被拒绝了,但是希望还是有的,那就继续关注 <a href="/misc/goto?guid=4959740085026111786" rel="nofollow,noindex">oppo开发者官网</a> 吧~</p> <h3>VIVO系列</h3> <p>抱歉,没有发现官方文档。</p> <p>不过最近有幸在网上看到一段代码,据说能支持vivo角标展示,抱着“死马当作活马医”的心态,我尝试了一把,最后发现居然奏效了。不多说了,贴代码共赏之:</p> <pre> <code class="language-java">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", msgCount); context.sendBroadcast(intent);</code></pre> <h3>Sony系列</h3> <p>参考资料: <a href="/misc/goto?guid=4959740085113034334" rel="nofollow,noindex">https://developer.sony.com/2016/06/23/xperia-home-badge-api-now-publicly-available/</a></p> <p> </p> <p>来自:http://www.jianshu.com/p/f429777f798d</p> <p> </p>