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

标签 android sms

我试图通过让设备向自己发送短信并自动检查是否收到短信来验证 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.SmsManagerandroid.telephony.SmsMessage 。请确保您已为两者导入了正确的类。

要发送外发短信,您将使用 SmsManagersendTextMessage() 方法,该方法具有以下签名:
sendTextMessage(String destinationAddress, String scAddress, String text,
                PendingIntent sentIntent, PendingIntent deliveryIntent)

在这个方法调用中只需要两个参数 - destinationAddresstext ;第一个是电话号码,第二个是消息内容。 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 上动态注册。
  • 在 list 中静态注册 Receiver 类将允许您的应用程序接收传入的消息,即使您的应用程序在接收之前碰巧被终止。但是,可能需要做一些额外的工作才能在您想要的地方获得结果。在 <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() 方法中,实际的消息是作为额外的附加到 byteIntent 数组的数组。解码细节因 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() ,并连接消息正文。
  • 自 KitKat(API 级别 19)以来,如果您的应用程序不是默认消息应用程序,则此处使用的消息将由系统和默认应用程序保存到 SMS Provider,因此可用于任何其他使用提供者。对此您无能为力,但如果您真的想避免它,同样的技术可以用于数据 SMS,它不会触发默认应用程序,也不会保存到 Provider。

    为此,使用 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()
  • 在 KitKat 之前,应用程序负责编写自己的传出消息,因此如果您不想要任何记录,则不能在这些版本上这样做。

    传入的消息可能会被拦截,并且它们的广播在主消息应用程序可以接收和写入它们之前中止。为此,将过滤器的优先级设置为最大值,并在接收器中调用 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/

    相关文章:

    android - 如何关闭抽屉导航以使用“返回主页”图标按钮?

    安卓 Logcat 错误

    c# - 使用 SmsManager 打开默认的短信应用

    android - 如何在 Qt for Android 中发送短信?

    android - Intent 向多个联系人发送短信

    java - onPostexecute 中的字符串比较不起作用

    java - OnLongClickListener 不适用于 onTouchListener

    android - Adb emu 命令有时不起作用

    vb.net - 串口读取包括vb.net中已经写入的内容

    Android 布局未按预期显示