Android利用NotificationListenerService实现消息盒子功能
背景
现在市面上很多垃圾清理软件基本上都有消息盒子这个功能,就是把用户收到的推送消息全部收集起来集中管理,避免太多推送给用户带来烦恼。这个功能就是基于Android 提供的NotificationListenerService来实现的。
使用
在api18(Android 4.3) Google加入了NotificationListenerService,下面我们来看看NotificationListenerService的具体使用。
新建一个MyNotificationListenerService继承自NotificationListenerService,实现其中的三个方法onListenerConnected()、onNotificationPosted(StatusBarNotification sbn)和onNotificationRemoved(StatusBarNotification sbn)。
-
public class MyNotificationListenerService extends NotificationListenerService {
-
@Override
-
public void onListenerConnected() {
-
//当连接成功时调用,一般在开启监听后会回调一次该方法
-
}
-
@Override
-
public void onNotificationPosted(StatusBarNotification sbn) {
-
//当收到一条消息时回调,sbn里面带有这条消息的具体信息
-
}
-
@Override
-
public void onNotificationRemoved(StatusBarNotification sbn) {
-
//当移除一条消息的时候回调,sbn是被移除的消息
-
}
当然,还需要在Manifest中声明这个服务
-
<service
-
android:name=".MyNotificationListenerService"
-
android:label="@string/app_name"
-
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
-
<intent-filter>
-
<action android:name="android.service.notification.NotificationListenerService" />
-
</intent-filter>
-
</service>
每个方法是主要作用在上面的注释都写的比较清楚了。
NotificationListenerService中还有一些方法可供我们使用,下面是一些经常使用的方法:
cancelAllNotifications(); //移除所有可移除的通知
cancelNotification(String key); //移除指定key的通知,要求api21以上
cancelNotifications(String[] keys); //移除指定数组内的所有key的通知,要求api21以上
getActiveNotifications(); //获取通知栏上的所有通知,返回一个StatusBarNotification[]
StatusBarNotification
StatusBarNotification是通知栏的通知对象,里面包含了一条通知的所有信息。具体的如下:
getPackageName(); //获取发送通知的应用程序包名
isClearable(); //通知是否可被清除
getId(); //获取通知id
getKey(); //获取通知的key
getPostTime(); //通知的发送时间
getNotification(); //获取Notification
通过上面我们看到,StatusBarNotification能直接获取到到信息很少,但是其中有一个getNotification()方法可以获取到一个Notification对象,通过Notification可以获取到通知到标题,内容,图标以及通知的意图PendingIntent等信息。
如下:
-
@Override
-
public void onNotificationPosted(StatusBarNotification sbn) {
-
Bundle extras = sbn.getNotification().extras;
-
String title = extras.getString(Notification.EXTRA_TITLE); //通知title
-
String content = extras.getString(Notification.EXTRA_TEXT); //通知内容
-
int smallIconId = extras.getInt(Notification.EXTRA_SMALL_ICON); //通知小图标id
-
Bitmap largeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON); //通知的大图标,注意和获取小图标的区别
-
PendingIntent pendingIntent = sbn.getNotification().contentIntent; //获取通知的PendingIntent
-
}
注意
在上面的代码中,注意获取通知小图标和大图标时的区别,小图标获取到的是一个int型的资源id,而大图标是一个序列化后的bitmap,这点需要特别注意,还有,一些通知的大图标获取可能为null,需要注意空指针异常。
获取到的小图标id怎么转为图标呢?可以使用下面的方法:
-
/**
-
*
-
* @param context
-
* @param id 图标id
-
* @param pkgName 图标所在的包名
-
* @return bitmap
-
*/
-
public static Bitmap getSmallIcon(ApplicationContext context,String pkgName, int id) {
-
Bitmap smallIcon = null;
-
Context remotePkgContext;
-
try {
-
remotePkgContext = context.createPackageContext(pkgName, 0);
-
Drawable drawable = remotePkgContext.getResources().getDrawable(id);
-
if (drawable != null) {
-
smallIcon = ((BitmapDrawable) drawable).getBitmap();
-
}
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return smallIcon;
-
}
通过上面的介绍,现在就可以监听到通知的产生和清除事件了,并且可以获取到通知的具体内容进行一些自己的处理。
NotificationListenerService是一个系统级别的服务,它不需要我们去start或者bind来开启,也不能关闭它,会自动监听消息,但是前提是你的App拥有获取通知权限,下面介绍怎么判断应用是否拥有获取通知权限以及怎么打开获取通知权限。
获取通知权限的判断及打开通知权限设置页面
判断是否拥有通知权限
-
public boolean isNotificationListenersEnabled() {
-
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;
-
}
打开通知权限设置页面
-
protected boolean gotoNotificationAccessSetting() {
-
try {
-
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
startActivity(intent);
-
return true;
-
} catch (ActivityNotFoundException e) {//普通情况下找不到的时候需要再特殊处理找一次
-
try {
-
Intent intent = new Intent();
-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
ComponentName cn = new ComponentName("com.android.settings", "com.android.settings.Settings$NotificationAccessSettingsActivity");
-
intent.setComponent(cn);
-
intent.putExtra(":settings:show_fragment", "NotificationAccessSettings");
-
startActivity(intent);
-
return true;
-
} catch (Exception e1) {
-
e1.printStackTrace();
-
}
-
Toast.makeText(this, "对不起,您的手机暂不支持", Toast.LENGTH_SHORT).show();
-
e.printStackTrace();
-
return false;
-
}
-
}
最后
在使用过程中,我发现了一个问题,就是在退出app后,再次打开,监听不生效,这个时候我们需要做一些处理。在app启动时,我们去重新关闭打开一次监听服务,让它正常工作。
-
private void toggleNotificationListenerService(Context context) {
-
PackageManager pm = context.getPackageManager();
-
pm.setComponentEnabledSetting(new ComponentName(context, MyNotificationListenerService .class),
-
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
-
pm.setComponentEnabledSetting(new ComponentName(context, MyNotificationListenerService .class),
-
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
-
}
总结
总体来说,NotificationListenerService的使用还是比较简单,主要就是onListenerConnected(),onNotificationPosted(StatusBarNotification sbn),onNotificationRemoved(StatusBarNotification sbn)这三个方法和通过StatusBarNotification,Notification获取消息的具体内容。