前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android NotificationListenerService使用方式(详细步骤+源码)

Android NotificationListenerService使用方式(详细步骤+源码)

作者头像
晨曦_LLW
发布2021-08-25 16:29:44
4.3K0
发布2021-08-25 16:29:44
举报
文章被收录于专栏:Android、鸿蒙开发

前言

  今天是七夕,深圳又在下雨,庆幸的是单身狗不用出门,苦涩。NotificationListenerService 通知监听服务,就是监听手机上的广播通知,这个在纯App开发中用的比较少,但是在智能穿戴领域用的很多,比如我有一个智能手表,然后有一个配套的App应用,有时候在上班路上,手机放在口袋里,要接受到手机上的通知消息,例如QQ、微信、来电、短信等。智能手表上显示有新消息,如果手表上有屏幕的话,还能显示消息的内容。这就很Nice了不是吗?

先看看效果图:

正文

  下面进入正式的使用,我先说一下使用的思路,NotificationListenerService 是一个服务,服务更多的是在后台运行,其次由于这个需要进行通知监听,会涉及到一个动态权限请求。第三就是手机上是否安装了微信、QQ等应用。

一、配置项目

  新建项目NotifyListenerDemo。

主要是项目依赖库的添加,打开工程的build.gradle的repositories{}闭包下添加依赖库:

代码语言:javascript
复制
maven {url "https://jitpack.io"}

然后在app模块的build.gradle中添加依赖:

代码语言:javascript
复制
//简单工具类库
implementation 'com.github.lilongweidev:EasyLibrary:1.0.4'

然后Sync,同步项目添加依赖库。

二、通知监听服务

  创建一个服务,然后将服务注册在AndroidManifest.xml中。创建一个NotifyService类,里面的代码如下:

代码语言:javascript
复制
public class NotifyService extends NotificationListenerService {

    public static final String TAG = "NotifyService";

    public static final String QQ = "com.tencent.mobileqq";//qq信息
    public static final String WX = "com.tencent.mm";//微信信息
    public static final String MMS = "com.android.mms";//短信
    public static final String HONOR_MMS= "com.hihonor.mms";//荣耀短信
    public static final String MESSAGES = "com.google.android.apps.messaging";//信息
    public static final String IN_CALL = "com.android.incallui";//来电 - 
    /**
     * 发布通知
     * @param sbn 状态栏通知
     */
    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        switch (sbn.getPackageName()){
            case MESSAGES:
            case MMS:
            case HONOR_MMS:
                Log.d(TAG,"收到短信");
                break;
            case QQ:
                Log.d(TAG,"收到QQ消息");
                break;
            case WX:
                Log.d(TAG,"收到微信消息");
                break;
            case IN_CALL:
                Log.d(TAG,"收到来电");
                break;
            default:break;
        }
    }

    /**
     * 通知已删除
     * @param sbn 状态栏通知
     */
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
        switch (sbn.getPackageName()){
            case MESSAGES:
            case MMS:
            case HONOR_MMS:
                Log.d(TAG,"移除短信");
                break;
            case QQ:
                Log.d(TAG,"移除QQ消息");
                break;
            case WX:
                Log.d(TAG,"移除微信消息");
                break;
            case IN_CALL:
                Log.d(TAG,"移除来电");
                break;
            default:break;
        }
    }

    /**
     * 监听断开
     */
    @Override
    public void onListenerDisconnected() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 通知侦听器断开连接 - 请求重新绑定
            requestRebind(new ComponentName(this, NotificationListenerService.class));
        }
    }


}

这个类继承了NotificationListenerService,里面里面几个方法,都已经注释过了,这里设置通知栏上应用的包名,由于国产厂商对于Android做了定制化,因此各个厂商的系统App软件的包名就不会是一致的,例如我用的是荣耀,那么我监听到的通知短信是com.hihonor.mms,来电是com.android.incallui。这个地方需要开发者自行去适配。不过QQ和微信这种App的包名是固定的,可以放心使用。

下面将这个服务注册在AndroidManifest.xml中。

代码语言:javascript
复制
        <service
            android:name=".NotifyService"
            android:enabled="true"
            android:label="测试通知服务"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            intent-filter>
        service>

服务配置完成了,下面进行具体的打开服务操作。

三、打开通知服务监听

  使用这个通知服务其实就是打开一个手机上应用的开关,效果上和打开蓝牙差不多,下面先写一个方法检查当前应用是否开启这个服务。方法代码如下:

代码语言:javascript
复制
	/**
     * 是否启用通知监听服务
     * @return
     */
    public boolean isNLServiceEnabled() {
        Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(this);
        if (packageNames.contains(getPackageName())) {
            return true;
        }
        return false;
    }

这里还对应一个方法就是设置服务是否运行,如下:

代码语言:javascript
复制
	/**
     * 切换通知监听器服务
     *
     * @param enable
     */
    public void toggleNotificationListenerService() {
        PackageManager pm = getPackageManager();
        pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

        pm.setComponentEnabledSetting(new ComponentName(getApplicationContext(), NotifyService.class),
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
    }

现在方法有了需要一个地方去触发,通过按钮来进行,在activity_main.xml添加一个按钮。

代码语言:javascript
复制
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请求权限"
        android:onClick="requestPermission"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

androidx.constraintlayout.widget.ConstraintLayout>

在MainActivity中添加一个方法:

代码语言:javascript
复制
	private static final int REQUEST_CODE = 9527;
	/**
     * 请求权限
     *
     * @param view
     */
    public void requestPermission(View view) {
        if (!isNLServiceEnabled()) {
            startActivityForResult(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"), REQUEST_CODE);
        } else {
            showMsg("通知服务已开启");
            toggleNotificationListenerService(true);
        }
    }

这个还有一个showMsg方法:

代码语言:javascript
复制
	private void showMsg(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

然后是页面返回:

代码语言:javascript
复制
	@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            if (isNLServiceEnabled()) {
                showMsg("通知服务已开启");
                toggleNotificationListenerService(true);
            } else {
                showMsg("通知服务未开启");
                toggleNotificationListenerService(false);
            }
        }
    }

下面就可以先运行一下:

现在就可以测试数据,这里建议你找一个朋友给你发消息,保持电脑上没有登录微信和QQ,然后手机上的微信和QQ保持后台运行就可以了,下面让你的朋友给你发QQ消息、微信消息。

这是我这里测试的数据,控制打印出来:

QQ、微信、短信、来电都监听到了。

四、页面显示消息类型

  现在只是在NotifyService中可以打印出来,那么Activity并不知道,但是实际的UI操作又是在Activity里面,因此需要将消息传递到Activity,这种方式很多,我这里使用接口回调的方式来进行。下面新建一个接口,代码如下:

代码语言:javascript
复制
public interface NotifyListener {

    /**
     * 接收到通知栏消息
     * @param type
     */
    void onReceiveMessage(int type);

    /**
     * 移除掉通知栏消息
     * @param type
     */
    void onRemovedMessage(int type);
}

然后再写一个NotifyHelper帮助类,用于实现接口的回调处理。

代码语言:javascript
复制
public class NotifyHelper {

    private static NotifyHelper instance;

    public static final int N_MESSAGE = 0;
    public static final int N_CALL = 1;
    public static final int N_QQ = 2;
    public static final int N_WX = 3;

    private NotifyListener notifyListener;

    public static NotifyHelper getInstance() {
        if (instance == null) {
            instance = new NotifyHelper();
        }
        return instance;
    }

    /**
     * 收到消息
     * @param type 消息类型
     */
    public void onReceive(int type) {
        if (notifyListener != null) {
            notifyListener.onReceiveMessage(type);
        }

    }

    /**
     * 移除消息
     * @param type 消息类型
     */
    public void onRemoved(int type) {
        if (notifyListener != null) {
            notifyListener.onRemovedMessage(type);
        }
    }

    /**
     * 设置回调方法
     *
     * @param notifyListener 通知监听
     */
    public void setNotifyListener(NotifyListener notifyListener) {
        this.notifyListener = notifyListener;
    }
}

最后收到服务中收到通知进行调用。

移除通知进行调用

最后只要在MainActivity中实现接口。

这里实现接口,然后将收到的结果显示在TextView上,实现接口中的两个回调方法。

代码语言:javascript
复制
	/**
     * 收到通知
     * @param type 通知类型
     */
    @Override
    public void onReceiveMessage(int type) {
        switch (type) {
            case N_MESSAGE:
                textView.setText("收到短信消息");
                break;
            case N_CALL:
                textView.setText("收到来电消息");
                break;
            case N_WX:
                textView.setText("收到微信消息");
                break;
            case N_QQ:
                textView.setText("收到QQ消息");
                break;
            default:
                break;
        }
    }

    /**
     * 移除通知
     * @param type 通知类型
     */
    @Override
    public void onRemovedMessage(int type) {
        switch (type) {
            case N_MESSAGE:
                textView.setText("移除短信消息");
                break;
            case N_CALL:
                textView.setText("移除来电消息");
                break;
            case N_WX:
                textView.setText("移除微信消息");
                break;
            case N_QQ:
                textView.setText("移除QQ消息");
                break;
            default:
                break;
        }
    }

下面再运行一下:

可以看到,现在通过接口想消息类型的结果显示到页面上来了。

五、页面显示消息内容、时间

  现在的很多带屏幕的手环都做到了这一点,可以显示消息内容,这个说起来很高大上,实际上很简单,还是之前的那个地方,那个位置,为了更够更好的显示内容,我还是用代码来说明一下。

首先在NotifyListener增加接口中的方法。

代码语言:javascript
复制
	/**
     * 接收到通知栏消息
     * @param sbn
     */
    void onReceiveMessage(StatusBarNotification sbn);

    /**
     * 移除掉通知栏消息
     * @param sbn
     */
    void onRemovedMessage(StatusBarNotification sbn);

与之前的方法中的参数不同,当然图方便也可以只用这个新增的,把之前的可以去掉,因为现在的这个拿到的信息要比之前多,只不过需要再对消息做进一步的处理。

接口方法写好了,在NotifyHelper中新增两个方法对刚才的接口方法进行调用。

代码语言:javascript
复制
	/**
     * 收到消息
     * @param sbn 状态栏通知
     */
    public void onReceive(StatusBarNotification sbn) {
        if(notifyListener != null) {
            notifyListener.onReceiveMessage(sbn);
        }
    }
	
	/**
     * 移除消息
     * @param sbn 状态栏通知
     */
    public void onRemoved(StatusBarNotification sbn) {
        if (notifyListener != null) {
            notifyListener.onRemovedMessage(sbn);
        }
    }

这里和之前的接口方法如出一辙,最后就是在NotifyService中调用NotifyHelper中的两个方法即可。

最后只要在MainActivity中实现接口回调方法就可以了。 代码如下:

代码语言:javascript
复制
	/**
     * 收到通知
     *
     * @param sbn 状态栏通知
     */
    @Override
    public void onReceiveMessage(StatusBarNotification sbn) {
        if (sbn.getNotification() == null) return;
        //消息内容
        String msgContent = "";
        if (sbn.getNotification().tickerText != null) {
            msgContent = sbn.getNotification().tickerText.toString();
        }

        //消息时间
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINESE).format(new Date(sbn.getPostTime()));
        textView.setText(String.format(Locale.getDefault(),
                "应用包名:%s\n消息内容:%s\n消息时间:%s\n",
                sbn.getPackageName(), msgContent, time));
    }

    /**
     * 移除通知
     *
     * @param sbn 状态栏通知
     */
    @Override
    public void onRemovedMessage(StatusBarNotification sbn) {
        textView.setText("通知移除");
    }

运行一下:

这个图就说明了消息的内容,在通知栏上并不是所有通知都有内容的,例如手机屏幕录制是没有通知内容的,然后后面我收到一条QQ消息就会显示出消息内容,那么本篇文章就到这里了,山高水长,后会有期~

六、源码

GitHub:NotifyListenerDemo

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/08/14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 一、配置项目
      • 二、通知监听服务
        • 三、打开通知服务监听
          • 四、页面显示消息类型
            • 五、页面显示消息内容、时间
              • 六、源码
              相关产品与服务
              短信
              腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档