自動讀取短信驗證碼LoaderManager和CursorLoader的使用

已開通簡書博客,歡迎拍磚。

Cursor用來直接讀取安卓手機裏的數據庫記錄,如何獲取到Cursor?

  • 可以通過SQLiteOpenHelper,打開SQLiteDatabase
  • 通過ContentProvider/ContentResolver獲取

自動讀取驗證碼實現思路:

  1. 當驗證碼的短信到來,能有個監聽回調或者廣播之類,告知開發人員
  2. 讀取短信是危險權限,android6.0權限適配
  3. 獲取驗證碼的短信內容
  4. 從短信內容裏刷選出驗證碼
  5. 顯示驗證碼

接下來針對每一個步驟,做些分析和代碼分享

監聽接收到新短信

使用ContentResolver,引入內容觀察者,監聽短信內容的變化,代碼如下:

public void register(){
    mReadSmsContentObserver = new ReadSmsContentObserver(new Handler());
    mContext.getContentResolver().registerContentObserver(URI_SMS,true,mReadSmsContentObserver);
}


class ReadSmsContentObserver extends ContentObserver{

    public ReadSmsContentObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        LOG.e(TAG,"selfChange = " + selfChange+" uri = " + uri);
        if(PermissionHelper.checkReadSmsPermission(mContext)){
            initReadSmsLoader();
        }else{
            takeReadSmsPermission();
        }
    }
}

短信驗證碼,權限適配

PermissionHelper是自己封裝的工具類

PermissionHelper.takeReadSmsPermission(this,PermissionHelper.REQUEST_CODE_READ_SMS);   

獲取驗證碼的短信內容

這裏有多個方案,網上相當多技術文章使用contentResolver.query()方法。我認爲這個方法不夠好。

  • 該方法在UI線程裏操作的,當查詢的數據庫的記錄很多時,會出現卡頓現象。
  • 需要手動關閉Cursor,Cursor維護操作交給開發人員。

好在,安卓已經提供了異步的(子線程)數據庫查詢方式,使用LoaderManager和CursorLoader這兩個類,不存在上述兩個問題。代碼如下
更詳細的LoaderManager.LoaderCallbacks各個回調方法的使用,可以參考android CursorLoader用法介紹

/**
 * 註冊內容觀察者
 */
public void register(){
    mReadSmsContentObserver = new ReadSmsContentObserver(new Handler());
    mContext.getContentResolver().registerContentObserver(URI_SMS,true,mReadSmsContentObserver);
}

public class ReadSmsLoaderListener implements LoaderManager.LoaderCallbacks<Cursor>{

private static Uri URI_SMS_INBOX = Uri.parse("content://sms/inbox");
// "_id", "address", "person", "date", "type"
private static final String[] READ_SMS_PROJECTION = new String[]{"body"};
private static final String READ_SMS_SORT_ORDER = "date desc";

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        //兩分鐘內,收到的短信,date降序排列
        String readSmsWhere = "date >" + (System.currentTimeMillis() - 2*60*1000);
        return new CursorLoader(mContext,URI_SMS_INBOX,READ_SMS_PROJECTION,readSmsWhere,null,READ_SMS_SORT_ORDER);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        fetchSmsCodeFromCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    }
}

/**
 * 從數據庫獲取短信並過濾出驗證碼
 */
private void fetchSmsCodeFromCursor(Cursor cursor) {
    if(cursor != null) {
        while (cursor.moveToNext()) {
            String body = cursor.getString(cursor.getColumnIndex("body"));
            if (!TextUtils.isEmpty(body)) {
                //判斷該短信是否是該App發送的
                if (body.contains("走麼")) {
                    String verifyCode = getSmsCode(body);
                    displayVerifyCode(verifyCode);
                    break;
                }
            }
        }
    }
}

從短信內容裏刷選出驗證碼

使用正則表達式篩選,按正則的使用套路走起

/**
 * 從短信中獲取驗證碼
 * @param smsContent 短信內容
 * @return
 */
private String getSmsCode(String smsContent){
    Pattern pattern = Pattern.compile("[0-9]{4,}");
    Matcher m = pattern.matcher(smsContent);
    while (m.find()) {
        String group = m.group();
        if(group != null && group.length() >= VERIFY_CODE_LENGTH){
            return group;
        }
    }
    return "";
}

顯示驗證碼

/**
 * 顯示驗證碼
 * @param verifyCode 驗證碼
 */
private void displayVerifyCode(String verifyCode) {
    mSmsCodeEt.setText(verifyCode);
    //獲取焦點
    mSmsCodeEt.setFocusable(true);
    mSmsCodeEt.setFocusableInTouchMode(true);
    mSmsCodeEt.requestFocus();
    //設置光標位置
    mSmsCodeEt.setSelection(verifyCode.length());
}

總結

至此,成功實現讀取短信驗證碼。近來主流的App,並沒有實現自動讀取短信驗證碼,比如滴滴出行,釘釘。


參考資料

android CursorLoader用法介紹

Google Android API 查詢(可直接訪問)

CursorLoader

LoaderManager

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章