小米(MIUI)手机监听短信的两种解决方案

测试的系统为MIUI10和模拟器(Pixel 2 API 26)

应用场景:短信验证码自动填写等

方法1为监听系统短信数据库,但是存在一个问题,就是收到短信之后需要点击短信进入短信详情页面,才会触发广播

方法2通过监听通知栏,根据发通知的应用包名锁定对应的短信应用,从而可以在收到短信的同时获取短信内容

两种方法在获取短信的时候都是只获取收到回调时最新的一条短信,因此不推荐方法1。


首先需要获取对应的权限,高版本的系统需要写代码动态获取权限

<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

  1. 使用ContentObserver监听系统短信数据库更新
    1.1 实现代码

    • 监听短信数据库变化
      class SmsObserver extends ContentObserver {
          private Context context;
          public SmsObserver(Context context, Handler handler) {
              super(handler);
              this.context = context;
          }
          @Override
          public void onChange(boolean selfChange) {
              super.onChange(selfChange);
              //每当有新短信到来时,获取短消息的方法
              getSmsFromPhone();
          }
          private void getSmsFromPhone() {
              ContentResolver cr = context.getContentResolver();
              String[] projection = new String[]{"_id", "body", "address"};//"_id", "address", "person",, "date", "type
              String where = " date >  "
                      + (System.currentTimeMillis() - 10 * 60 * 1000);//10分钟之内的短信
              Cursor cur = cr.query(Uri.parse("content://sms/inbox"), projection, where, null, "date desc");
              if (null == cur)
                  return;
              try {
                  if (cur.moveToFirst()) {
                      String number = cur.getString(cur.getColumnIndex("address"));//手机号
                      String body = cur.getString(cur.getColumnIndex("body"));//内容
                  }
              } finally {
                  cur.close();
              }
          }
      }
      
    • 初始化监听
       val smsHandler: Handler = @SuppressLint("HandlerLeak")
        object : Handler() { //这里可以进行回调的操作
            //TODO
        }
        smsObserver = SmsObserver(this, smsHandler)
        //监听短信
        applicationContext.contentResolver.registerContentObserver(
            Uri.parse("content://sms/inbox"), true,
            smsObserver!!
        )
    
  2. 使用NotificationListenerService监听系统通知栏数据更新
    2.1 实现代码

    • 继承NotificationListenerService(高版本的安卓系统需要在Service里面调用函数startForeground设置通知栏,否则会报错)

      class NotificationMonitor : NotificationListenerService() {
      
          private val match = arrayOf("com.android.mms", "com.google.android.apps.messaging") //暂时只有小米短信和谷歌短信的包名
      	// 有新的通知
          override fun onNotificationPosted(sbn: StatusBarNotification) {
              super.onNotificationPosted(sbn)
              Log.i(getString(R.string.app_name), sbn.packageName)
              if (sbn.packageName in match) {
                  Log.i(getString(R.string.app_name), "有新的短信")
                  getSmsFromPhone()
              }
          }
          
          private fun getSmsFromPhone() {
              val cr: ContentResolver = contentResolver
              val projection =
                  arrayOf("_id", "body", "address") //"_id", "address", "person",, "date", "type
              val where = (" date >  "
                      + (System.currentTimeMillis() - 10 * 60 * 1000))
              val cur =
                  cr.query(Uri.parse("content://sms"), projection, where, null, "date desc") ?: return
              cur.use { cur ->
                  if (cur.moveToFirst()) {
                      val number = cur.getString(cur.getColumnIndex("address")) //手机号
                      val body = cur.getString(cur.getColumnIndex("body")) //内容
                  }
              }
          }
          
          override fun onCreate() {
              super.onCreate()
              //设置通知栏
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                  val channelId =
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                          createNotificationChannel(
                              "message_deliver_notification_service",
                              "MessageDeliver Background Service"
                          )
                      } else {
                          // If earlier version channel ID is not used
                          // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
                          ""
                      }
      
                  val notificationBuilder = NotificationCompat.Builder(this, channelId)
                  val notification = notificationBuilder.setOngoing(true)
                      .setSmallIcon(R.mipmap.ic_launcher)
                      .setPriority(NotificationCompat.PRIORITY_MIN)
                      .setCategory(Notification.CATEGORY_SERVICE)
                      .build()
                  startForeground(1, notification)
              }
          }
      
          @RequiresApi(Build.VERSION_CODES.O)
          private fun createNotificationChannel(channelId: String, channelName: String): String {
              val chan = NotificationChannel(
                  channelId,
                  channelName, NotificationManager.IMPORTANCE_NONE
              )
              chan.lightColor = Color.BLUE
              chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
              val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
              service.createNotificationChannel(chan)
              return channelId
          }
      
      }
      
    • 在AndroidManifest.xml的application标签里面注册Service

          <service
              android:name=".NotificationMonitor"
              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>
      
    • 启动Service(这里由于安卓版本的不同,启动后台服务可以选择调用不同的函数)

      val notification = Intent(this, NotificationMonitor::class.java)
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          this.startForegroundService(notification)
      } else {
          this.startService(notification)
      }
      

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值