android 监听短信并获取验证码

最近想给 app 添加自动获取短信验证码的功能,让注册流程更加友好,在网上搜索了一些资料,主要的实现方法有两种。
第一:实现广播 BroadCastReceiver 来监听收件箱,在需要监听的地方注册广播监听,然后再Activity 结束的地方unregist掉。
第二:利用 ContentObserver 来监听短信数据库,当有指定的新信息到来时调用相应的方法来实现信息内容的读取。以下仅给出第二种方法的实现代码(亲测可行哦!)。

  • 修改 AndroidManifest.xml 获取短信接收和读取权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
  • 新建一个Activity
package com.example.myapp;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.widget.EditText;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MyActivity extends Activity {
    /**
     * Called when the activity is first created.
     */
    private EditText verifyText;
    private SmsObserver smsObserver;
    private Uri SMS_INBOX = Uri.parse("content://sms/");

    public void getSmsFromPhone() {
        ContentResolver cr = getContentResolver();
        //这个projection参数初始化要尤其注意哦!由于我只想获取验证码内容,我就只填了“body”,如果给位还需要获取发件人信息的话,可以继续添加
        String[] projection = new String[] { "body"};
        String where = " address = '1069041810748872' AND date >  "
              + (System.currentTimeMillis() - 60 * 1000);
        //设置监听指定号码,10分钟内有效,如果您不需要指定号码的话,只需要去掉相应的条件就ok
        Cursor cur = cr.query(SMS_INBOX, projection, where, null, "date desc");
        if (null == cur)
            return;
        if (cur.moveToFirst()) {
            String body = cur.getString(cur.getColumnIndex("body"));
            //这里我是要获取自己短信服务号码中的验证码,务必记住这里获取的内容需要和projection字符串对应,否则短信数据库可能会找不到内容的。
            Pattern pattern = Pattern.compile("[0-9]{4}");
            //我获取的验证码是4位阿拉伯数字,正则匹配规则您可能需要修改
            Matcher matcher = pattern.matcher(body);
            if (matcher.find()) {
                String res = matcher.group();
                verifyText.setText(res);
            }
        }
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        verifyText = (EditText) findViewById(R.id.verifycode);
        smsObserver = new SmsObserver(this, smsHandler);
        getContentResolver().registerContentObserver(SMS_INBOX, true,
                smsObserver);
    }

    public Handler smsHandler = new Handler() {
        //这里可以进行回调的操作
        //TODO

    };

    class SmsObserver extends ContentObserver {
        public SmsObserver(Context context, Handler handler) {
            super(handler);
        }
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //每当有新短信到来时,使用我们获取短消息的方法
            getSmsFromPhone();
        }
    }
}
  • main.xml如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        >
    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Hello World, MyActivity"
            />
    <EditText
            android:id="@+id/verifycode"
            android:layout_width="200dp"
            android:layout_height="50dp"/>
LinearLayout>

是不是很简单呢? 可能会有朋友碰到类似下面这样的问题:

Failed to read row 0, column 1 from a CursorWindow which has 1 rows, 1 columns.
FATAL EXCEPTION: main
java.lang.IllegalStateException: Could not execute method of the activity
Caused by: java.lang.IllegalStateException: Couldn't read row 0, col 1 from CursorWindow.  
Make sure the Cursor is initialized correctly before accessing data from it.

这种情况就是说CursorWindow没有成功的初始化,不妨通过打 log 的方式,看看cur中到底有什么,本人出现这个问题就在于 projection 没有设置好,导致初始化出来的cur并不是我想要的。
附上短信数据库简单分析:

*address:短信发送者电话号码
person:联系人编号,如果电话薄里有联系人则显示编号,没有联系人则显示null
read:读取状态,0为未读,1为已读
body:短信内容*

好了,就这些了,感谢!

你可能感兴趣的:(android)