BroadcastReceiver + SMS_RECEIVED

我希望我的应用能够收到传入的短信。 这里有几个例子。 看起来我们只需要这样做:

// AndroidManifest.xml <receiver android:name=".SMSReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> // SMSReceiver.java public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i(TAG, "SMS received."); .... } } 

它是否正确? 我正在给我的手机发送一些短信,但是日志logging永远不会被打印出来。 我在手机上安装了一些其他短信应用程序,当收到短信时会显示一个popup窗口 – 是否阻止了传递到我的应用程序的意图,他们只是完全消耗它?

谢谢

您还需要在清单文件中指定一个uses-permission:

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

以下教程应该有所帮助:

响应传入的短信
Android中的短信

还有一件事,这些答案没有提到 – 你应该要求的权限android.permission.BROADCAST_SMS。 如果你不这样做,任何应用程序都可以欺骗你的应用程序的消息。

 <receiver android:name=".SMSReceiver" android:exported="true" android:permission="android.permission.BROADCAST_SMS"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> 

路上有几个陷阱。 你可以find所有需要的信息在计算器上。 为了方便,我收集了这个答案中的所有信息。

事情要注意

  1. 我假设android kitkat和以上。
  2. 收集短信的意图是"android.provider.Telephony.SMS_RECEIVED"
  3. 您可以更改意向filter的优先级,但不是必需的。
  4. 您需要清单xml中的此权限"android.permission.RECEIVE_SMS" ,以便接收短信。 在Android 6及以上版本中,您还需要在运行时询问权限。
  5. 您不需要在意向filter中设置MIMEtypes的数据。 如果没有设置MIMEtypes,意向filter应该只传递空数据,但幸运的是它仍然可以在没有MIME的情况下工作。
  6. adb shell am broadcast将无法正常工作。 使用telnet连接模拟器来testing短信接收。
  7. 长短信息被分成小短信块。 我们需要连接它们。

如何发送短信到模拟器

最重要的是有可能发送假的短消息到设备,所以我们可以testing代码。

为此,我们将使用一个虚拟设备和一个telnet连接到它。

  1. 在android studio中创build一个虚拟设备并运行模拟器
  2. 看看模拟器窗口中的标题栏。 有设备名称和端口号 。 我们需要在接下来的步骤中知道这个端口号。
  3. 现在用telnet连接到模拟器标题栏中显示的端口号

      $ telnet localhost 5554 
  4. 如果您看到: Android Console: Authentication required ,那么您需要使用此命令validation连接:

      auth xxxxxx 

    用从~/.emulator_console_auth_token文件中读取的标记replace上面的xxxxxx

  5. 现在你应该可以运行所有的命令。 要发送短信,请input以下命令:

      sms send 555 "This is a message" 

    在哪里可以用发件人的电话号码和自己的留言来代替555。

如何收听SMS_RECEIVED广播

要获得广播,您需要注册一个BroadcastReceiver对象。 您可以在manifest.xml中执行此操作, 或者只调用registerReceiver函数。 我会告诉你后者,因为它更容易推理,更灵活。

将广播接收机连接到主要活动

数据stream是一种方式。 从广播接收机到主要活动。 所以最简单的方法就是使用一个函数接口。 该活动将实现这样的function,广播接收者将具有作为参数在构造函数中传递的活动实例。

文件SmsHandler.java:

 package ... interface SmsHandler { void handleSms(String sender, String message); } 

实现广播接收机

广播接收器将获得callback的意图。 我们将使用Telephony.Sms.Intents.getMessagesFromIntent(intent)函数来获取短信息。 注意构造函数中的SmsHandler参数。 这将是我们将发送收到的短信的活动。

文件SmsInterceptor.java:

 package ... import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.provider.Telephony; import android.telephony.SmsMessage; public class SmsInterceptor extends BroadcastReceiver { private SmsHandler handler; /* Constructor. Handler is the activity * * which will show the messages to user. */ public SmsInterceptor(SmsHandler handler) { this.handler = handler; } @Override public void onReceive(Context context, Intent intent) { /* Retrieve the sms message chunks from the intent */ SmsMessage[] rawSmsChunks; try { rawSmsChunks = Telephony.Sms.Intents.getMessagesFromIntent(intent); } catch (NullPointerException ignored) { return; } /* Gather all sms chunks for each sender separately */ Map<String, StringBuilder> sendersMap = new HashMap<>(); for (SmsMessage rawSmsChunk : rawSmsChunks) { if (rawSmsChunk != null) { String sender = rawSmsChunk.getDisplayOriginatingAddress(); String smsChunk = rawSmsChunk.getDisplayMessageBody(); StringBuilder smsBuilder; if ( ! sendersMap.containsKey(sender) ) { /* For each new sender create a separate StringBuilder */ smsBuilder = new StringBuilder(); sendersMap.put(sender, smsBuilder); } else { /* Sender already in map. Retrieve the StringBuilder */ smsBuilder = sendersMap.get(sender); } /* Add the sms chunk to the string builder */ smsBuilder.append(smsChunk); } } /* Loop over every sms thread and concatenate the sms chunks to one piece */ for ( Map.Entry<String, StringBuilder> smsThread : sendersMap.entrySet() ) { String sender = smsThread.getKey(); StringBuilder smsBuilder = smsThread.getValue(); String message = smsBuilder.toString(); handler.handleSms(sender, message); } } } 

主要活动

最后,我们需要在主活动中实现SmsHandler接口,并在onCreate函数中添加注册广播接收者和权限检查。

文件MainActivity.java:

 package ... import ... public class MainActivity extends AppCompatActivity implements SmsHandler { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /* Register the broadcast receiver */ registerSmsListener(); /* Make sure, we have the permissions */ requestSmsPermission(); } /* This function will be called by the broadcast receiver */ @Override public void handleSms(String sender, String message) { /* Here you can display the message to the user */ } private void registerSmsListener() { IntentFilter filter = new IntentFilter(); filter.addAction("android.provider.Telephony.SMS_RECEIVED"); /* filter.setPriority(999); This is optional. */ SmsInterceptor receiver = new SmsInterceptor(this); registerReceiver(receiver, filter); } private void requestSmsPermission() { String permission = Manifest.permission.RECEIVE_SMS; int grant = ContextCompat.checkSelfPermission(this, permission); if ( grant != PackageManager.PERMISSION_GRANTED) { String[] permission_list = new String[1]; permission_list[0] = permission; ActivityCompat.requestPermissions(this, permission_list, 1); } } } 

最后记得给你的manifest xml添加RECEIVE_SMS权限

 <?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <application> ... </application> </manifest> 

另外请注意,环聊应用程序目前会阻止我的BroadcastReceiver接收短信。 我必须在环聊应用程序(设置 – >短信 – >打开短信)中禁用短信function,然后再开始短信广播接收。

编辑:它看起来好像有些应用程序将abortBroadcast()的意图,这将阻止其他应用程序接收意图。 解决方法是增加intent-filter标记中的android:priority属性:

  <receiver android:name="com.company.application.SMSBroadcastReceiver" > <intent-filter android:priority="500"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> 

在此处查看更多详情: 在环聊2.0中启用短信支持会在我的应用中打破SMS_RECEIVED的BroadcastReceiver

你用模拟器试过了吗?

在模拟器中部署应用程序之后,可以通过DDMS或者通过连接telnet的命令行发送类似SMS的事件:

 telnet localhost <port_emulator> send sms <incoming_tel_number> <sms_content> 

port_emulator通常是5554

你应该阅读这个关于发送和接收短信的程序。 http://mobiforge.com/developing/story/sms-messaging-android