使用SMS验证设备的电话号码

Soh*_*lam 26 sms android

我试图通过让设备向自己发送短信来验证Android设备的电话号码,并自动检查是否已收到短信.我怎样才能做到这一点?

Mik*_* M. 65

首先,这需要两个权限; 一个发送短信,一个发送短信.以下内容需要在您的AndroidManifest.xml中,在<manifest>标记之间,但在<application>标记之外.

<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
Run Code Online (Sandbox Code Playgroud)

这些都是危险的权限,因此如果您的应用程序要在Marshmallow(API级别23)或更高版本上运行,并且具有targetSdkVersion23+ 以上,则需要相应地处理它们.有关如何在运行时请求这些权限的信息可以在此开发人员页面上找到.


您需要的Java类在android.telephony包中; 特别是android.telephony.SmsManagerandroid.telephony.SmsMessage.确保您已为两者导入了正确的类.

要发送的短信外发,你会使用SmsManagersendTextMessage()方法,该方法具有以下特征:

sendTextMessage(String destinationAddress, String scAddress, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent)
Run Code Online (Sandbox Code Playgroud)

此方法调用只需要两个参数 - destinationAddresstext; 第一个是电话号码,第二个是电话号码.null可以通过其余的.例如:

String number = "1234567890";
String message = "Verification message.";
SmsManager sm = SmsManager.getDefault();
sm.sendTextMessage(number, null, message, null, null);
Run Code Online (Sandbox Code Playgroud)

保持消息文本相对较短很重要,sendTextMessage()如果文本长度超过单个消息的字符限制,通常会无声地失败.


要接收和读取传入消息,您需要注册a BroadcastReceiverIntentFilter执行"android.provider.Telephony.SMS_RECEIVED"操作.此Receiver可以在清单中静态注册,也可以Context在运行时动态注册.

  • 在清单中静态注册Receiver类将允许您的应用程序接收传入消息,即使您的应用程序恰好在收到之前被杀死.但是,可能需要一些额外的工作才能将结果输出到您想要的位置.<application>标签之间:

    <receiver
        android:name=".SmsReceiver"
        android:enabled="false">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
    
    Run Code Online (Sandbox Code Playgroud)

    PackageManager#setComponentEnabledSetting()方法可用于<receiver>根据需要启用和禁用此方法.

  • 在代码上动态注册Receiver实例Context可以更容易管理,因为Receiver类可以在任何组件注册它时成为内部类,因此可以直接访问该组件的成员.但是,这种方法可能不如静态注册那么可靠,因为一些不同的东西可能会阻止接收方进行广播; 例如,您的应用程序被杀死,用户导航远离注册Activity等.

    SmsReceiver receiver = new SmsReceiver();
    IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
    registerReceiver(receiver, filter);
    
    Run Code Online (Sandbox Code Playgroud)

    请记得在适当的时候注销Receiver.


在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();
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

此时,您只需将number此处与传递给sendTextMessage()调用的那个进行比较即可.建议使用PhoneNumberUtils.compare()此方法,因为在Receiver中检索的数字可能与寻址的格式不同.


笔记:

  • 这里演示的示例是使用一个单部分消息,因此消息文本应限制为相对较短的长度.如果您确实想要发送更长的消息,出于某种原因,sendMultipartTextMessage()可以使用该方法.您需要首先拆分文本,使用SmsManager#divideMessage()并将结果传递ArrayList给该方法,以代替消息String.要在Receiver中重新组合完整的消息,您必须将每个消息解码byte[]为一个SmsMessage,并连接消息体.

  • 从KitKat(API级别19)开始,如果您的应用程序不是默认的消息传递应用程序,则此处使用的消息将由系统和默认应用程序保存到SMS提供程序,因此可供使用该应用程序的任何其他应用程序使用.供应商.你无能为力,但如果你真的想避免它,这种技术可以用于数据短信,它不会触发默认应用,也不会保存到提供商.

    为此,使用该sendDataMessage()方法,该方法需要short(任意)端口号的附加参数,并且消息作为a byte[]而不是a 传递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>
    
    Run Code Online (Sandbox Code Playgroud)

    IntentFilter类中有相应的方法来动态设置它们.

    解码SmsMessage是相同的,但是消息byte[]是用getUserData(),而不是getMessageBody().

  • 在KitKat之前,应用程序负责编写自己的外发邮件,因此如果您不想要任何记录,那么您就不能在这些版本上执行此操作.

    可以拦截传入的消息,并且在主消息传递应用程序可以接收和写入它们之前中止它们的广播.为此,过滤器的优先级设置为最大值,并abortBroadcast()在接收器中调用.在静态选项中,该android:priority="999"属性将添加到开始<intent-filter>标记中.动态地,该IntentFilter#setPriority()方法可以做同样的事情.

    这根本不可靠,因为其他应用程序总是可以拥有比您更高的优先级.

  • 在这些例子中,我省略了获得广播公司许可的接收者,部分原因是为了简单和清晰,部分原因是因为事物的性质不会让你接受任何可能造成伤害的欺骗行为.但是,如果您想要包含它,那么您只需要将android:permission="android.permission.BROADCAST_SMS"属性添加到<receiver>static选项的开始标记中.对于动态,使用方法的四参数重载,将该registerReceiver()权限String作为第三个参数传递,并null作为第四个参数.