Android探索之旅 | 爲應用添加角標(Badge)

-- 做者 謝恩銘 轉載請註明出處javascript

內容簡介


  1. 需求簡介
  2. Android角標起源
  3. 不錯的Github項目
  4. 清除角標
  5. 小問題糾錯
  6. 總結

1.需求簡介


角標是什麼意思呢?java

看下圖便可明瞭:android

能夠看到圖中的樂購這個app右上角的紅色的圓圈,裏面有10這個數字的,就是一種角標。git

角標,英語是badge,也就是「徽章,像章,獎章; 象徵,標記」的意思。程序員

通常來講,應用的角標是用來標記有多少條提醒(Notification)沒讀(unread),一旦點擊提示進應用閱讀了,角標也會消失。github

2. Android角標起源


角標本來是蘋果的iOS中的東西,Android原生並不支持角標,由於Google的意思是讓你們用Notification(提示欄)便可,角標實在大有讓處女座「跳崖」的風險。幸虧我不是...編程

最近公司的項目中,客戶的一個新需求是在咱們的加密信息應用上加上角標功能,由於咱們的合做夥伴是三星(能夠參看個人這篇文章:程序員在法國 | 我被法國國防部盯上了!),所以我就去網上找相關資料。微信

找的時候,才知道上面所說的Android原生不支持角標一事。不過無妨,厲害的Android第三方廠商能夠經過在自定義的Launcher(啓動器)中操做來實現添加角標。app

我在第一時間固然是去找三星的移動設備如何添加角標,不過卻有幸找到了Github上的比較普適的項目。ide

把我導向Github的天然是Stack Overflow,而把我導向Stack Overflow的就是Google,所以我會說:爲何程序員必定要會用Google和Stack Overflow?

3. 不錯的Github項目


  • 通常來講,如今被引用最多的Android添加和去除角標的Github項目是這位中國人寫的:github.com/leolin31014…

    這個項目挺不錯,雖然更新不是特別勤快,但最近一次更新是在2016年10月31日,也就是兩個月前,還能夠接受。

  • 《Android羣英傳》和《Android羣英傳:神兵利器》的做者 徐宜生 也在本身的Github上建了一個項目:github.com/xuyisheng/S… ,挺有意思,裏面還有號稱「瘋狂模式」的爲全部在手機桌面上的應用加上99的角標數的功能,固然了,去除的代碼也有,否則處女座豈不是要暈了~

添加角標的原理就是發送一個Broadcast(廣播),在廣播的Intent中指定須要被添加角標的應用的packageName(包名),className(類名),count(角標數目)。固然了,不一樣廠商的手機的角標操做的Intent的action是不同的。

所以,咱們若是要給本身的手機裏的應用添加角標,只須要簡單的利用上面兩個項目中的代碼便可,通常不須要把所有項目搬過來。固然了,若是你要適配全部手機,那麼能夠全盤引用項目。

好比我要給三星的手機的應用添加角標,那麼我只須要作如下的幾步便可:

  • 在AndroidManifest.xml中添加讀取和寫入角標的權限:
<uses-permission android:name="com.sec.android.provider.badge.permission.READ" />
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE" />複製代碼
  • 本身寫一個類,隨便取名字,好比叫作 BadgeUtils,在類中添加以下內容:
public class BadgeUtils {
  private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
  private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
  private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
  private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

  public static void setBadgeCount(Context context, ComponentName componentName, int badgeCount) {
    Intent intent = new Intent(INTENT_ACTION);     
    intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);    
    intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());    
    intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
    context.sendBroadcast(intent);  
  }
}複製代碼
  • 使用上面的代碼時,只須要傳入三個參數,也就是:

    1. Context : 應用的Context。簡單。
    2. ComponentName :組件名,略有點麻煩。能夠這樣來獲取(applicationContext就是應用的Context) :
      applicationContext.getPackageManager()
      .getLaunchIntentForPackage(applicationContext.getPackageName())
      .getComponent()複製代碼
    3. badgeCount :角標的數目,例如10。簡單。

固然了,若是你不想要傳入三個參數這麼麻煩,你也能夠再寫一個方法getLauncherClassName,就只須要傳入兩個參數便可。BadgeUtils中的代碼變爲:

public class BadgeUtils {
  private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
  private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
  private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
  private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

  public static void setBadgeCount(Context context, int badgeCount) {
    String launcherClassName = getLauncherClassName(context);
    if (launcherClassName == null) {
      return;
    }

    Intent intent = new Intent(INTENT_ACTION);     
    intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);    
    intent.putExtra(INTENT_EXTRA_PACKAGENAME, context.getPackageName());    
    intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, launcherClassName);

    context.sendBroadcast(intent);  
  }

  private static String getLauncherClassName(Context context) {
    PackageManager pm = context.getPackageManager();

    Intent intent = new Intent(Intent.ACTION_MAIN); 
    intent.addCategory(Intent.CATEGORY_LAUNCHER);

    List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
    for (ResolveInfo resolveInfo : resolveInfos) {
      String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
      if (pkgName.equalsIgnoreCase(context.getPackageName())) {
        String className = resolveInfo.activityInfo.name;
        return className;
      }
    }
    return null; 
  }
}複製代碼

使用時傳入兩個參數便可:

  1. Context : 應用的Context。
  2. badgeCount :角標的數目,例如10。

4. 清除角標


要清除應用的角標就很簡單了,給badgeCount傳入0便可。

BadgeUtils.setBadgeCount(context,       
context.getPackageManager()               
.getLaunchIntentForPackage(context.getPackageName())                
.getComponent(),        
0);複製代碼

或者

BadgeUtils.setBadgeCount(context, 0);複製代碼

5. 小問題糾錯


上面的 github.com/leolin31014… 這個項目中,基本已經包含了大多數能夠定製角標的Android生產廠商的添加角標的代碼實現,不過它也提到:

三星和LG(這兩個難兄難弟)的代碼有不少相似,連角標處理的廣播的Intent中的action也是同樣的,都是:

"android.intent.action.BADGE_COUNT_UPDATE"複製代碼

可是做者在三星和LG的兩個角標操做實現類中寫了註釋:

// Deprecated, Samsung devices will use DefaultBadger

// Deprecated, LG devices will use DefaultBadger複製代碼

意思是「三星和LG的實現代碼已經Deprecated(失效了),請用DefaultBadger類」。

所以,這兩個須要用 github.com/leolin31014… 中的實現:

private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

@Override
public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
  Intent intent = new Intent(INTENT_ACTION);
  intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
  intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
  intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());

  if (BroadcastHelper.canResolveBroadcast(context, intent)) {
    context.sendBroadcast(intent);
  } else {
    throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
  }
}複製代碼

不過上面的代碼有一個小問題,就是那句

if (BroadcastHelper.canResolveBroadcast(context, intent)) {複製代碼

在有些設備(好比Samsung Galaxy S5)上會拋出異常(Exception),找不處處理"android.intent.action.BADGE_COUNT_UPDATE"這個Intent的BroadcastReceiver,很奇怪。

但有些設備(好比Samsung Galaxy A5)上又運行正常,沒有拋出異常。

解決辦法是去除這一個檢測,把

if (BroadcastHelper.canResolveBroadcast(context, intent)) {
  context.sendBroadcast(intent);
} else {
  throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
}複製代碼

替換爲簡單的

context.sendBroadcast(intent);複製代碼

就能夠了。

也就是我上面本身實做時的代碼。

6. 總結


  1. Android的角標添加和移除畢竟是基於各大手機廠商的Launcher的定製,所以不是正統的Android技巧,隨着廠商的Launcher的改變,也許你的代碼將來就不必定有用了,所以須要不斷修改,「推陳出新」。

  2. 不過正所謂「生命在於折騰」,而這也是咱們喜歡Android系統的緣由。這個萌萌的機器人能夠經得起咱們隨意折騰,在嵌入式領域的應用前途也是很不錯的。

  3. 你們在平時學習編程的時候,也能夠把本身的代碼或經驗彙總到Github項目,一來惠己利人,二來提升本身的業界知名度。


人世間,
萬千情感皆有溫度,
千萬代碼似有性格。
這裏有原創教程,IT叢林......
和你一塊兒探索程序人生。
微信公衆號「程序員聯盟」ProgrammerLeague
我是謝恩銘,在巴黎奮鬥的嵌入式軟件工程師。
我的簡介熱愛生活,喜歡游泳,略懂烹飪。人生格言:「向着標杆直跑」

相關文章
相關標籤/搜索