做者:小強 貝聊移動開發部 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開放桌面角標設置的權限。因此,這個問題暫時尚未解決方法