我试图通过让设备向自己发送短信并自动检查是否收到短信来验证 Android 设备的电话号码。我怎样才能做到这一点?
最佳答案
首先,这将需要两个权限;一个发送短信,一个接收短信。以下内容需要在您的 AndroidManifest.xml 中,在 <manifest>
标签之间,但在 <application>
标签之外。
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
这些都是危险的权限,因此如果您的应用程序要在 Marshmallow(API 级别 23)或更高版本上运行,并且
targetSdkVersion
为 23+,则您需要相应地处理它们。可以在 this developer page 上找到有关如何在运行时请求这些权限的信息。您需要的 Java 类位于
android.telephony
包中;特别是 android.telephony.SmsManager
和 android.telephony.SmsMessage
。请确保您已为两者导入了正确的类。要发送外发短信,您将使用
SmsManager
的 sendTextMessage()
方法,该方法具有以下签名:sendTextMessage(String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent)
在这个方法调用中只需要两个参数 -
destinationAddress
和 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"
。此接收器可以在 list 中静态注册,也可以在运行时在 Context
上动态注册。<application>
标签之间:<receiver
android:name=".SmsReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
PackageManager#setComponentEnabledSetting()
方法可用于根据需要启用和禁用此 <receiver>
。 Context
上动态注册 Receiver 实例在代码方面更容易管理,因为 Receiver 类可以成为注册它的任何组件的内部类,因此可以直接访问该组件的成员。但是,这种方法可能不如静态注册可靠,因为一些不同的事情可能会阻止 Receiver 获得广播;例如,您的应用程序的进程被终止,用户导航离开注册 Activity
等。SmsReceiver receiver = new SmsReceiver();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(receiver, filter);
请记住在适当的时候取消注册接收器。
在 Receiver 的
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()
进行比较。建议为此使用 sendMultipartTextMessage()
,因为在 Receiver 中检索到的数字可能与寻址的数字采用不同的格式。笔记:
SmsManager#divideMessage()
方法。您需要首先使用 ArrayList
拆分文本,并将生成的 String
传递给该方法,以代替消息 byte[]
。要在 Receiver 中重新组装完整的消息,您必须将每个 SmsMessage
解码为 sendDataMessage()
,并连接消息正文。 为此,使用
short
方法,这将需要一个额外的 byte[]
参数作为(任意)端口号,并且消息作为 String
传递,而不是 "android.intent.action.DATA_SMS_RECEIVED"
。要过滤的操作是 IntentFilter
,过滤器需要设置数据方案和权限(主机和端口)。在 list 中,它看起来像:<intent-filter>
<action android:name="android.intent.action.DATA_SMS_RECEIVED" />
<data
android:scheme="sms"
android:host="localhost"
android:port="1234" />
</intent-filter>
SmsMessage
类中有相应的方法可以动态设置这些方法。解码
byte[]
是相同的,但消息 getUserData()
是用 getMessageBody()
检索的,而不是 abortBroadcast()
。 传入的消息可能会被拦截,并且它们的广播在主消息应用程序可以接收和写入它们之前中止。为此,将过滤器的优先级设置为最大值,并在接收器中调用
android:priority="999"
。在静态选项中,<intent-filter>
属性被添加到打开的 IntentFilter#setPriority()
标签中。动态地,android:permission="android.permission.BROADCAST_SMS"
方法可以做同样的事情。这一点都不可靠,因为其他应用程序的优先级总是可能高于您的应用程序。
<receiver>
属性添加到静态选项的开头 registerReceiver()
标记中。对于动态,使用 String
方法的四参数重载,将该权限 null
作为第三个参数传递,将 ojit_code 作为第四个参数传递。 关于android - 使用 SMS 验证设备的电话号码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20133774/