首先,这需要两个权限;一种用于发送短信,另一种用于接收短信。以下内容需要在您的 AndroidManifest.xml 中,在<manifest>
标签,但在<application>
tags.
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
这些都是危险的权限,因此如果您的应用程序要在 Marshmallow(API 级别 23)或更高版本上运行,并且具有targetSdkVersion
23+。有关如何在运行时请求这些权限的信息可以在此开发者页面 https://developer.android.com/training/permissions/requesting.html.
您需要的 Java 类位于android.telephony
包裹;具体来说android.telephony.SmsManager
and android.telephony.SmsMessage
。请确保您已为两者导入了正确的类。
要发送外发短信,您将使用SmsManager
's sendTextMessage()
方法,其签名如下:
sendTextMessage(String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent)
此方法调用中只需要两个参数 -destinationAddress
and text
;第一个是电话号码,第二个是消息内容。null
其余的都可以通过。例如:
String number = "1234567890";
String message = "Verification message.";
SmsManager sm = SmsManager.getDefault();
sm.sendTextMessage(number, null, message, null, null);
保持消息文本相对较短很重要,因为sendTextMessage()
如果文本长度超过单个消息的字符限制,通常会失败。
要接收和阅读传入消息,您需要注册BroadcastReceiver
与IntentFilter
为了"android.provider.Telephony.SMS_RECEIVED"
行动。该接收器可以在清单中静态注册,也可以在清单上动态注册Context
在运行时。
-
在清单中静态注册 Receiver 类将允许您的应用程序接收传入消息,即使您的应用程序在接收之前碰巧被终止。然而,可能需要一些额外的工作才能获得您想要的结果。在。。之间<application>
tags:
<receiver
android:name=".SmsReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
The PackageManager#setComponentEnabledSetting()
方法可用于启用和禁用此功能<receiver>
如所须。
-
动态注册 Receiver 实例Context
从代码角度来说,管理起来会更容易一些,因为 Receiver 类可以成为注册它的任何组件上的内部类,因此可以直接访问该组件的成员。然而,这种方法可能不如静态注册那么可靠,因为一些不同的事情可能会阻止接收者获取广播;例如,您的应用程序的进程被终止,用户离开注册Activity
, etc.
SmsReceiver receiver = new SmsReceiver();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(receiver, filter);
请记住在适当的时候取消注册接收器。
在接收者的onReceive()
方法,实际消息以数组形式出现byte
数组附加到Intent
作为额外的。解码细节因Android版本而异,但这里的结果是单个SmsMessage
包含您要查找的电话号码和消息的对象。
class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SmsMessage msg;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
SmsMessage[] msgs = Telephony.Sms.Intents.getMessagesFromIntent(intent);
msg = msgs[0];
} else {
Object pdus[] = (Object[]) intent.getExtras().get("pdus");
msg = SmsMessage.createFromPdu((byte[]) pdus[0]);
}
String number = msg.getOriginatingAddress();
String message = msg.getMessageBody();
...
}
}
此时,您只需比较number
这里是传递给的那个sendTextMessage()
称呼。建议使用PhoneNumberUtils.compare()
为此,因为接收器中检索到的号码可能采用与寻址的格式不同的格式。
Notes:
此处演示的示例使用单部分消息,因此应将消息文本限制为相对较短的长度。如果您确实想发送更长的消息,出于某种原因,sendMultipartTextMessage()
可以用方法代替。您需要首先使用分割文本SmsManager#divideMessage()
,并传递结果ArrayList
到该方法,代替消息String
。要在接收器中重新组装完整的消息,您必须对每个消息进行解码byte[]
进入一个SmsMessage
,并连接消息体。
-
从 KitKat(API 级别 19)开始,如果您的应用程序不是默认消息应用程序,则此处使用的消息将由系统和默认应用程序保存到 SMS 提供程序,因此可供使用该消息的任何其他应用程序使用。提供者。您对此无能为力,但如果您确实想避免这种情况,可以将相同的技术用于数据短信,它不会触发默认应用程序,也不会保存到提供程序。
为此,sendDataMessage()
使用方法,这需要额外的short
(任意)端口号的参数,并且消息作为byte[]
,而不是一个String
。要过滤的操作是"android.intent.action.DATA_SMS_RECEIVED"
,并且过滤器将需要数据方案和权限(主机和端口)集。在清单中,它看起来像:
<intent-filter>
<action android:name="android.intent.action.DATA_SMS_RECEIVED" />
<data
android:scheme="sms"
android:host="localhost"
android:port="1234" />
</intent-filter>
并且里面有相应的方法IntentFilter
类来动态设置它们。
解码SmsMessage
是一样的,但是消息byte[]
检索到getUserData()
, 而不是getMessageBody()
.
-
在 KitKat 之前,应用程序负责编写自己的传出消息,因此如果您不希望有任何记录,则可以不在这些版本上执行此操作。
传入的消息可能会被拦截,并且在主消息传递应用程序可以接收和写入它们之前,它们的广播就会中止。为了实现这一点,过滤器的优先级设置为最大,并且abortBroadcast()
在接收器中被调用。在静态选项中,android:priority="999"
属性被添加到开头<intent-filter>
标签。动态地,IntentFilter#setPriority()
方法也可以做同样的事情。
这一点也不可靠,因为另一个应用程序总是有可能比您的应用程序具有更高的优先级。
在这些示例中,我省略了在获得广播公司许可的情况下保护接收器的安全,部分是为了简单明了,部分是因为事情的本质不会让您受到任何可能造成伤害的欺骗。但是,如果您想包含此内容,那么您只需添加android:permission="android.permission.BROADCAST_SMS"
归因于开口<receiver>
静态选项的标签。对于动态,使用四参数过载registerReceiver()
方法,通过该许可String
作为第三个参数,并且null
作为第四个。