Android Notification 補充 - NotificationListenerService

版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載罕邀。
微博:厲圣杰
源碼:AndroidDemo/Notification
文中如有紕漏,歡迎大家留言指出。

此篇為 Android Notification 補充揩环,對 Notification 的基本操作可參考:Android Notification 詳解(一)——基本操作。最近在公司項目中幅虑,需要實現(xiàn)獲取通知欄相關(guān)通知數(shù)據(jù)的功能丰滑,對此進行了調(diào)研,此為 NotificationListenerService 的簡單總結(jié)倒庵。

實現(xiàn)此功能需要用到 NotificationListenerService 褒墨,該類主要用于獲取系統(tǒng)相關(guān)的通知信息,需要在 API 18(Android 4.3) 及更高版本中才能使用擎宝。如:新增或刪除通知郁妈、通知的數(shù)量、通知的相關(guān)信息等绍申。

NotificationListenerService 主要方法有:

  • onNotificationPosted(StatusBarNotification sbn)
  • onNotificationRemoved(StatusBarNotification sbn)
  • cancelAllNotifications()
  • cancelNotification(String pkg, String tag, int id)
  • getActiveNotifications()

當我們繼承 NotificationListenerService 時噩咪,主要復(fù)寫 onNotificationPosted(StatusBarNotification sbn)onNotificationRemoved(StatusBarNotification sbn) 锄奢,其余三個方法是 final 的,所以不能被復(fù)寫剧腻。

StatusBarNotification 則包含了 Notification 的相關(guān)信息拘央,如:通知的名稱、圖標书在、創(chuàng)建時間等灰伟。

實現(xiàn) NotificationListenerService 的使用可以分為以下幾步:

  1. 新建一個繼承自 NotificationListenerService 的子類,實現(xiàn) onNotificationPosted()onNotificationRemoved() 方法

    public class MyNotificationListenerService extends NotificationListenerService {
    
        private static final String TAG = MyNotificationListenerService.class.getSimpleName();
    
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
            super.onNotificationPosted(sbn);
            Log.d(TAG, "onNotificationPosted=" + sbn.toString());
        }
    
        @Override
        public void onNotificationRemoved(StatusBarNotification sbn) {
            super.onNotificationRemoved(sbn);
            Log.d(TAG, "onNotificationRemoved=" + sbn.toString());
        }
    
    }
    
  2. 在 AndroidManifest.xml 中申明相關(guān)的權(quán)限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.littlejie.notification">
    
        <uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            
            <!-- something else -->
            <service
                android:name=".MyNotificationListenerService"
                android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
                <intent-filter>
                    <action android:name="android.service.notification.NotificationListenerService" />
                </intent-filter>
            </service>
        </application>
    
    </manifest>
    
  3. 僅僅實現(xiàn)上述兩個步驟還是不夠儒旬,這里栏账,我們還需要去開啟 Notification access。默認是在 Settings > Security > Notification access 中去開啟栈源,我們也可以通過 Intent 直接跳轉(zhuǎn)到對應(yīng)頁面挡爵。

    public class NotificationListenerServiceActivity extends Activity {
    
        //此為 Settings 中的常量,不過是屬于隱藏字段
        private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
    
        private Button mBtnSetNotifyAccess;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_get_notification_bar);
    
            mBtnSetNotifyAccess = (Button) findViewById(R.id.btn_setting);
            mBtnSetNotifyAccess.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
                    startActivity(intent);
                }
            });
            findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startService(new Intent(NotificationListenerServiceActivity.this, MyNotificationListenerService.class));
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mBtnSetNotifyAccess.setText(String.format(getString(R.string.set_notification_access), isEnabled() ? "已開啟" : "未開啟"));
        }
    
        /**
         * 判斷 Notification access 是否開啟
         * @return
         */
        private boolean isEnabled() {
            String pkgName = getPackageName();
            final String flat = Settings.Secure.getString(getContentResolver(),
                    ENABLED_NOTIFICATION_LISTENERS);
            if (!TextUtils.isEmpty(flat)) {
                final String[] names = flat.split(":");
                for (int i = 0; i < names.length; i++) {
                    final ComponentName cn = ComponentName.unflattenFromString(names[i]);
                    if (cn != null) {
                        if (TextUtils.equals(pkgName, cn.getPackageName())) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
    

Crash

在 HTC Android 4.3 版本的手機上捕獲該 crash,原因不明甚垦,大意是應(yīng)該是系統(tǒng)沒有實現(xiàn) onNotificationPosted() 方法茶鹃,后在 Android 4.3 及 4.4 的模擬器上測試,均重現(xiàn)了該 crash艰亮,而 Android 5.0 及以上的系統(tǒng)都正常闭翩,推測是系統(tǒng)原因,因此迄埃,Android 5.0 以下使用 NotificationListenerService 需慎重疗韵。

12-07 19:35:55.350 1210-1236/com.littlejie.notification E/JavaBinder: *** Uncaught remote exception!  (Exceptions are not yet supported across processes.)
     java.lang.AbstractMethodError: abstract method not implemented
         at android.service.notification.NotificationListenerService.onNotificationPosted(NotificationListenerService.java)
         at com.littlejie.notification.MyNotificationListenerService.onNotificationPosted(MyNotificationListenerService.java:17)
         at android.service.notification.NotificationListenerService$INotificationListenerWrapper.onNotificationPosted(NotificationListenerService.java:167)
         at android.service.notification.INotificationListener$Stub.onTransact(INotificationListener.java:56)
         at android.os.Binder.execTransact(Binder.java:388)
         at dalvik.system.NativeStart.run(Native Method)
12-07 19:35:55.350 1210-1236/com.littlejie.notification W/dalvikvm: threadid=9: thread exiting with uncaught exception (group=0x415f6970)
12-07 19:35:55.360 1210-1236/com.littlejie.notification E/AndroidRuntime: FATAL EXCEPTION: Binder_1
         java.lang.AbstractMethodError: abstract method not implemented
             at android.service.notification.NotificationListenerService.onNotificationPosted(NotificationListenerService.java)
             at com.littlejie.notification.MyNotificationListenerService.onNotificationPosted(MyNotificationListenerService.java:17)
             at android.service.notification.NotificationListenerService$INotificationListenerWrapper.onNotificationPosted(NotificationListenerService.java:167)
             at android.service.notification.INotificationListener$Stub.onTransact(INotificationListener.java:56)
             at android.os.Binder.execTransact(Binder.java:388)
             at dalvik.system.NativeStart.run(Native Method)

補充

后續(xù)需要實現(xiàn)當應(yīng)用被殺,但是 App 仍能接受到 Notification 通知侄非,即繼承自 NotificationListenerService 的 Service 仍然需要存活蕉汪,本以為這是一個浩大工程,需要 Service 之間相互喚起逞怨,結(jié)果測試的時候發(fā)現(xiàn) NotificationListenerService 不會被殺死者疤,實在是很不解,網(wǎng)上搜索之后才發(fā)現(xiàn)原因:繼承自 NotificationListenerService 的 Service 被殺死后骇钦,仍會被系統(tǒng)以各種方式重新調(diào)起宛渐,這不失為一個讓 App 永生不滅的方法竞漾,除了要讓用戶手動授權(quán)獲取 Notification access 權(quán)限眯搭。
具體分析詳情見:Android之使用NotificationListenerService使得自己的應(yīng)用不被殺及其源碼分析

參考:

  1. Android 4.4 KitKat NotificationManagerService使用詳解與原理分析(一)__使用詳解
  2. Android 4.4 KitKat NotificationManagerService使用詳解與原理分析(二)__原理分析
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市业岁,隨后出現(xiàn)的幾起案子鳞仙,更是在濱河造成了極大的恐慌,老刑警劉巖笔时,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍好,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機借笙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門扒怖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人业稼,你說我怎么就攤上這事盗痒。” “怎么了低散?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵俯邓,是天一觀的道長。 經(jīng)常有香客問我熔号,道長稽鞭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任引镊,我火速辦了婚禮朦蕴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弟头。我一直安慰自己梦重,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布亮瓷。 她就那樣靜靜地躺著琴拧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘱支。 梳的紋絲不亂的頭發(fā)上蚓胸,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音除师,去河邊找鬼沛膳。 笑死,一個胖子當著我的面吹牛汛聚,可吹牛的內(nèi)容都是我干的锹安。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼倚舀,長吁一口氣:“原來是場噩夢啊……” “哼叹哭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起痕貌,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤风罩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舵稠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體超升,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡入宦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了室琢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乾闰。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盈滴,靈堂內(nèi)的尸體忽然破棺而出汹忠,到底是詐尸還是另有隱情,我是刑警寧澤雹熬,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布宽菜,位于F島的核電站,受9級特大地震影響竿报,放射性物質(zhì)發(fā)生泄漏铅乡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一烈菌、第九天 我趴在偏房一處隱蔽的房頂上張望阵幸。 院中可真熱鬧,春花似錦芽世、人聲如沸挚赊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荠割。三九已至,卻和暖如春旺矾,著一層夾襖步出監(jiān)牢的瞬間蔑鹦,已是汗流浹背突雪。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工颊咬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留磷脯,地道東北人声怔。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像添瓷,于是被迫代替她去往敵國和親鹅髓。 傳聞我的和親對象是個殘疾皇子瑟押,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容