【问题标题】:Android – Listen For Incoming SMS MessagesAndroid – 监听传入的 SMS 消息
【发布时间】:2011-10-28 16:38:24
【问题描述】:

我正在尝试创建一个用于监控传入 SMS 消息的应用程序,并通过传入 SMS 启动一个程序,它也应该从 SMS 中读取内容。

工作流程:

  • 短信发送到安卓设备
  • 自可执行应用程序
  • 读取短信信息

【问题讨论】:

  • 我知道要创建一个应用程序来发送短信,但在这里我需要创建一个短信应用程序,它从短信中获取信息并将其保存到 SQLite 数据库......我该如何开发这样的应用
  • @iShader 我希望您成功创建应用程序,只是想知道您是如何设法同步设备和服务器的消息

标签: android sms android-service


【解决方案1】:
public class SmsListener extends BroadcastReceiver{

    private SharedPreferences preferences;

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub

        if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
            Bundle bundle = intent.getExtras();           //---get the SMS message passed in---
            SmsMessage[] msgs = null;
            String msg_from;
            if (bundle != null){
                //---retrieve the SMS message received---
                try{
                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];
                    for(int i=0; i<msgs.length; i++){
                        msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                        msg_from = msgs[i].getOriginatingAddress();
                        String msgBody = msgs[i].getMessageBody();
                    }
                }catch(Exception e){
//                            Log.d("Exception caught",e.getMessage());
                }
            }
        }
    }
}

注意:在清单文件中添加 BroadcastReceiver-

<receiver android:name=".listener.SmsListener">
    <intent-filter>
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

添加此权限:

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

【讨论】:

  • 您能解释一下为什么使用辅助接收器吗?
  • @VineetShukla 你能解释一下什么是 pdus 吗??
  • 使用 Intents.SMS_RECEIVED_ACTION 而不是硬编码的。
  • 以上评论不正确。任何应用程序在 4.4+ 中仍然可以获取 SMS_RECEIVED 广播,并且由于该广播无法中止,它比以前的版本更加确定。
  • @RuchirBaronia 多部分消息。单个 SMS 消息有字符限制(取决于您使用的字符集,但常见限制为 70、140、160 个字符)。如果一条消息超过该限制,则可以将其拆分为多个消息、多个部分。该数组是您需要连接以获得完整消息的部分数组。您的接收者一次只能收到一条完整的消息;它可能分为多个部分。
【解决方案2】:

请注意,在某些设备上,如果没有意图过滤器中的 android:priority="1000",您的代码将无法工作:

<receiver android:name=".listener.SmsListener">
    <intent-filter android:priority="1000">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

这里有一些优化:

public class SmsListener extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                String messageBody = smsMessage.getMessageBody();
            }
        }
    }
}

注意
该值必须是整数,例如“100”。数字越大优先级越高。默认值为 0。该值必须大于 -1000 且小于 1000。

Here's a link.

【讨论】:

  • 这个答案可能更优雅,但需要 API 19。仅供参考。
  • 根据thisandroid:priority不能高于1000(或低于-1000)。
  • 它不适用于Android 5.1 的小米红米Note 3 Pro。每个人都在提供这个解决方案,但它似乎对我不起作用。
  • 清单文件中插入的
  • @Sermilion 您必须在手机的应用程序管理器中手动允许读取短信的权限。
【解决方案3】:

@Mike M. 我发现接受的答案存在问题(请参阅我们的 cmets):

基本上,如果我们不每次都连接多部分消息,那么遍历 for 循环是没有意义的:

for (int i = 0; i < msgs.length; i++) {
    msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
    msg_from = msgs[i].getOriginatingAddress();
    String msgBody = msgs[i].getMessageBody();
}

请注意,我们只是将msgBody 设置为消息相应部分的字符串值,无论我们在什么索引上,这使得循环通过 SMS 消息的不同部分的整个点无用,因为它会只需设置为最后一个索引值。相反,我们应该使用+=,或者正如迈克所说,StringBuilder

总而言之,我的短信接收代码如下所示:

if (myBundle != null) {
    Object[] pdus = (Object[]) myBundle.get("pdus"); // pdus is key for SMS in bundle

    //Object [] pdus now contains array of bytes
    messages = new SmsMessage[pdus.length];
    for (int i = 0; i < messages.length; i++) {
         messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); //Returns one message, in array because multipart message due to sms max char
         Message += messages[i].getMessageBody(); // Using +=, because need to add multipart from before also
    }

    contactNumber = messages[0].getOriginatingAddress(); //This could also be inside the loop, but there is no need
}

只是把这个答案放在那里,以防其他人有同样的困惑。

【讨论】:

    【解决方案4】:

    这是我用的!

    public class SMSListener extends BroadcastReceiver {
    
        // Get the object of SmsManager
        final SmsManager sms = SmsManager.getDefault();
    String mobile,body;
    
        public void onReceive(Context context, Intent intent) {
    
            // Retrieves a map of extended data from the intent.
            final Bundle bundle = intent.getExtras();
    
            try {
    
                if (bundle != null) {
    
                    final Object[] pdusObj = (Object[]) bundle.get("pdus");
    
                    for (int i = 0; i < pdusObj.length; i++) {
    
                        SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                        String phoneNumber = currentMessage.getDisplayOriginatingAddress();
    
                        String senderNum = phoneNumber;
                        String message = currentMessage.getDisplayMessageBody();
                         mobile=senderNum.replaceAll("\\s","");
                         body=message.replaceAll("\\s","+");
    
    
                        Log.i("SmsReceiver", "senderNum: "+ senderNum + "; message: " + body);
    
    
                        // Show Alert
                        int duration = Toast.LENGTH_LONG;
                        Toast toast = Toast.makeText(context,
                                "senderNum: "+ mobile+ ", message: " + message, duration);
                        toast.show();
    
                    } // end for loop
                } // bundle is null
    
            } catch (Exception e) {
                Log.e("SmsReceiver", "Exception smsReceiver" +e);
    
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      如果有人像我一样提到如何在 Xamarin Android 上执行相同的功能(使用收到的 SMS 读取 OTP):

      1. 将此代码添加到您的 AndroidManifest.xml 文件中:

        <receiver android:name=".listener.BroadcastReveiverOTP">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
        </receiver>
        <uses-permission android:name="android.permission.RECEIVE_SMS" />
        <uses-permission android:name="android.permission.BROADCAST_SMS" />
        <uses-permission android:name="android.permission.READ_SMS" />
        
      2. 然后在您的 Android 项目中创建您的 BroadcastReveiver 类。

        [BroadcastReceiver(Enabled = true)] [IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)] 
        public class BroadcastReveiverOTP : BroadcastReceiver {
                public static readonly string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
        
                protected string message, address = string.Empty;
        
                public override void OnReceive(Context context, Intent intent)
                {
                    if (intent.HasExtra("pdus"))
                    {
                        var smsArray = (Java.Lang.Object[])intent.Extras.Get("pdus");
                        foreach (var item in smsArray)
                        {
                            var sms = SmsMessage.CreateFromPdu((byte[])item);
                            address = sms.OriginatingAddress;
                            if (address.Equals("NotifyDEMO"))
                            {
                                message = sms.MessageBody;
                                string[] pin = message.Split(' ');
                                if (!string.IsNullOrWhiteSpace(pin[0]))
                                { 
                                        // NOTE : Here I'm passing received OTP to Portable Project using MessagingCenter. So I can display the OTP in the relevant entry field.
                                        MessagingCenter.Send<object, string>(this,MessengerKeys.OnBroadcastReceived, pin[0]);
                                }
                                }
                        }
                    }
                }
        }
        
      3. 在 Android 项目的 MainActivity 类中注册此 BroadcastReceiver 类:

        public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
        
                // Initialize your class
                private BroadcastReveiverOTP _receiver = new BroadcastReveiverOTP ();
        
                protected override void OnCreate(Bundle bundle) { 
                        base.OnCreate(bundle);
        
                        global::Xamarin.Forms.Forms.Init(this, bundle);
                        LoadApplication(new App());
        
                        // Register your receiver :  RegisterReceiver(_receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
        
                }
        }
        

      【讨论】:

      • 编译器错误提示“android.permission.BROADCAST_SMS”只授予系统应用程序。
      【解决方案6】:

      接受的答案是正确的,适用于旧版本的 Android,其中 Android 操作系统会在应用安装时请求权限,但是在较新版本的 Android 上,它不能立即工作,因为较新的 Android 操作系统会在应用程序运行时请求权限需要该功能。因此,为了使用已接受答案中提到的技术在较新版本的 Android 上接收 SMS,程序员还必须实现将在运行时检查并请求用户权限的代码。在这种情况下,权限检查功能/代码可以在应用程序的第一个活动的 onCreate() 中实现。只需在您的第一个活动中复制并粘贴以下两个方法,然后在 onCreate() 结束时调用 checkForSmsReceivePermissions() 方法。

          void checkForSmsReceivePermissions(){
          // Check if App already has permissions for receiving SMS
          if(ContextCompat.checkSelfPermission(getBaseContext(), "android.permission.RECEIVE_SMS") == PackageManager.PERMISSION_GRANTED) {
              // App has permissions to listen incoming SMS messages
              Log.d("adnan", "checkForSmsReceivePermissions: Allowed");
          } else {
              // App don't have permissions to listen incoming SMS messages
              Log.d("adnan", "checkForSmsReceivePermissions: Denied");
      
              // Request permissions from user 
              ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECEIVE_SMS}, 43391);
          }
      }
      
      @Override
      public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          if(requestCode == 43391){
              if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                  Log.d("adnan", "Sms Receive Permissions granted");
              } else {
                  Log.d("adnan", "Sms Receive Permissions denied");
              }
          }
      }
      

      【讨论】:

        【解决方案7】:

        如果您想处理打开的活动的意图,您可以使用 PendintIntent(完成以下步骤):

        public class SMSReciver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                final Bundle bundle = intent.getExtras();
                try {
                    if (bundle != null) {
                        final Object[] pdusObj = (Object[]) bundle.get("pdus");
                        for (int i = 0; i < pdusObj.length; i++) {
                            SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
                            String phoneNumber = currentMessage.getDisplayOriginatingAddress();
                            String senderNum = phoneNumber;
                            String message = currentMessage.getDisplayMessageBody();
                            try {
                                if (senderNum.contains("MOB_NUMBER")) {
                                    Toast.makeText(context,"",Toast.LENGTH_SHORT).show();
        
                                    Intent intentCall = new Intent(context, MainActivity.class);
                                    intentCall.putExtra("message", currentMessage.getMessageBody());
        
                                    PendingIntent pendingIntent= PendingIntent.getActivity(context, 0, intentCall, PendingIntent.FLAG_UPDATE_CURRENT);
                                    pendingIntent.send();
                                }
                            } catch (Exception e) {
                            }
                        }
                    }
                } catch (Exception e) {
                }
            }
        } 
        

        清单:

        <activity android:name=".MainActivity"
                    android:launchMode="singleTask"/>
        <receiver android:name=".SMSReciver">
                    <intent-filter android:priority="1000">
                        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
                    </intent-filter>
                </receiver>
        

        onNewIntent:

         @Override
                 protected void onNewIntent(Intent intent) {
                        super.onNewIntent(intent);
                        Toast.makeText(this, "onNewIntent", Toast.LENGTH_SHORT).show();
        
                        onSMSReceived(intent.getStringExtra("message"));
        
                    }
        

        权限:

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

        【讨论】:

        • Google Play 商店的 Google 管理员认为 RECEIVE_SMS 权限(在您提到的教程中)是危险的。因此,包含该权限的应用程序将被拒绝。然后,开发人员必须向 Google Play 管理员提交表单以供批准。其他开发人员提到这个过程很糟糕,反馈需要数周时间,并且在没有任何解释或一般反馈的情况下被彻底拒绝。关于如何避免的任何想法?
        【解决方案8】:

        感谢@Vineet Shukla(接受的答案)和@Ruchir Baronia(在接受的答案中找到问题),下面是Kotlin 版本:

        添加权限:

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

        在 AndroidManifest 中注册 BroadcastReceiver:

        <receiver
            android:name=".receiver.SmsReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="2332412">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
        

        添加广播接收器的实现:

        class SmsReceiver : BroadcastReceiver() {
            private var mLastTimeReceived = System.currentTimeMillis()
        
            override fun onReceive(p0: Context?, intent: Intent?) {
                val currentTimeMillis = System.currentTimeMillis()
                if (currentTimeMillis - mLastTimeReceived > 200) {
                    mLastTimeReceived = currentTimeMillis
        
                    val pdus: Array<*>
                    val msgs: Array<SmsMessage?>
                    var msgFrom: String?
                    var msgText: String?
                    val strBuilder = StringBuilder()
                    intent?.extras?.let {
                        try {
                            pdus = it.get("pdus") as Array<*>
                            msgs = arrayOfNulls(pdus.size)
                            for (i in msgs.indices) {
                                msgs[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray)
                                strBuilder.append(msgs[i]?.messageBody)
                            }
        
                            msgText = strBuilder.toString()
                            msgFrom = msgs[0]?.originatingAddress
        
                            if (!msgFrom.isNullOrBlank() && !msgText.isNullOrBlank()) {
                                //
                                // Do some thing here
                                //
                            }
                        } catch (e: Exception) {
                        }
                    }
                }
            }
        }
        

        有时事件会触发两次,所以我添加了mLastTimeReceived = System.currentTimeMillis()

        【讨论】:

          【解决方案9】:

          在 Kotlin 上的广播实现:

           private class SmsListener : BroadcastReceiver() {
              override fun onReceive(context: Context?, intent: Intent?) {
                  Log.d(TAG, "SMS Received!")
          
                  val txt = getTextFromSms(intent?.extras)
                  Log.d(TAG, "message=" + txt)
              }
          
              private fun getTextFromSms(extras: Bundle?): String {
                  val pdus = extras?.get("pdus") as Array<*>
                  val format = extras.getString("format")
                  var txt = ""
                  for (pdu in pdus) {
                      val smsmsg = getSmsMsg(pdu as ByteArray?, format)
                      val submsg = smsmsg?.displayMessageBody
                      submsg?.let { txt = "$txt$it" }
                  }
                  return txt
              }
          
              private fun getSmsMsg(pdu: ByteArray?, format: String?): SmsMessage? {
                  return when {
                      SDK_INT >= Build.VERSION_CODES.M -> SmsMessage.createFromPdu(pdu, format)
                      else -> SmsMessage.createFromPdu(pdu)
                  }
              }
          
              companion object {
                  private val TAG = SmsListener::class.java.simpleName
              }
          }
          

          注意:在清单文件中添加 BroadcastReceiver-

          <receiver android:name=".listener.SmsListener">
              <intent-filter>
                  <action android:name="android.provider.Telephony.SMS_RECEIVED" />
              </intent-filter>
          </receiver>
          

          添加此权限:

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

          【讨论】:

            【解决方案10】:

            从现在开始,如果您不是默认的短信应用,几乎不可能发布具有 android.permission.RECEIVE_SMS 权限的应用。 Google 提供了一个新的短信捕获工具 ==> Automatic SMS Verification with the SMS Retriever API

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-04-19
              • 2014-06-05
              • 1970-01-01
              • 1970-01-01
              • 2019-04-22
              • 2020-03-06
              • 1970-01-01
              相关资源
              最近更新 更多