Android: NotificationListenerServiceとAccessibilityServiceの違い

Android 4.3から導入された NotificationListenerService で出来る機能は、AccessibilityServiceでも実現できる場合があります。

新しい NotificationListenerService を使わなくてもできるのであれば、昔ながらのやり方をすれば良いと考えられるかもしれませんが、新しいOSバージョンでは NotificationListenerService を利用し、どうしても古いバージョンでも対応する必要がある場合は AccessibilityService を併用することをおすすめします。

NotificationListenerService の利用を推奨する大きな理由は以下の通りです。

  • 通知管理のほとんどをシステム側に任せられ、またできることが増える
  • セキュリティメニューによって有効・無効が管理されるため、ユーザに通知管理をダウンロードしたアプリに任せることのセキュリティリスクを意識させやすい

業務用アプリであれば、対象端末をAndorid 4.3以上に限定して開発することで、工数を大幅に削減することも可能となるでしょう。システムを設計する際に、ソフトウェアの開発工数とAndroid 4.3端末の導入にかかる費用を合算して検討することで、お客様にとって最適なシステムを提案できるでしょう。 ((新型Nexus7など、Android 4.3を指定した端末の導入は現実的な選択肢になってきていると、弊社では考えております。))

Google Playで広いユーザに対して提供するアプリを開発しなければならない場合に、通知管理機能を開発しなければならない場合は、引き続きAccessibilityService も利用しての開発を頑張りましょう。

通知管理について

AccessibilityService を利用して、裏技的に通知を管理しようとする場合、通知が届く度にその情報をそのアプリ内で管理する情報として保持し、アプリが起動される度にそのアプリの通知情報を削除するといったことをアプリ内で行う必要があります。それをちゃんと設計・実装するには大きな工数が必要となります。

また残念ながら、管理のためにたくさんのコードを書いたとしても、通知バーに表示されている情報とアプリが管理している情報を正確に一致させることはできません。

NotificationListenerService を利用する場合、以下のことができるので、アプリ側で行う処理を非常にシンプルにすることが可能となりますし、アプリ内の情報を通知バーの情報と一致させることは容易です。

  • getActiveNotifications() で有効なすべての通知を取得する
  • cancelAllNotification() や cancelNotification() で通知を削除する
  • onNotificationPosted() で新しい通知の到着をトリガとして処理を実行する
  • onNotificationRemoved() で通知の削除をトリガとして処理を実行する

通知の即時実行と、実行後の自動削除を行うサンプルコード

以下のコードサンプルは、通知が届いたらすぐに通知をタップしたのと等価な処理を実行するものです。その時、通知が削除可能かどうか、タップした時に自動削除することを要求しているか確認して、どちらもtrueなら通知を削除する処理を呼び出しています。

@Override
public void onNotificationPosted(StatusBarNotification sbn) {
    Notification notification = sbn.getNotification();
    Intent intent = new Intent();
    PendingIntent pendingIntent = notification.contentIntent;
    try {
        pendingIntent.send(this, 0, intent);

        int id = sbn.getId();
        String tag = sbn.getTag();
        String packageName = sbn.getPackageName();
        int flags = sbn.getNotification().flags;

        // 通知がキャンセル可能、かつユーザによる通知タップ時に
        // 消える設定の場合に通知を消す
        if (sbn.isClearable() &&
            flags & Notification.FLAG_AUTO_CANCEL) {
            cancelNotification(packageName, id, tag);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}