Android短信發送,監聽,及其工具類封裝

這幾天在讀ViewPager及相關控件的源碼和相關開源的代碼,還沒構思好比較系統容易理解記憶的文章內容概述,就分享個封裝的工具類供同仁們參考一下吧

一,封裝 :發送短信

發送短信比較簡單,主要有兩種實現方式,一種是使用Intent通過系統短信應用,一種是直接調用短信接口發送短信;根據使用場景不同我一共封裝了三個方法:

  • 調用系統發短信界面 不需要用戶自己輸入接收方的電話號碼
/**
     * 調用系統發短信界面 不需要用戶自己輸入接收方的電話號碼
     *
     * @param context    Activity
     * @param phoneNumber 手機號碼
     * @param smsContent  短信內容
     */
    public static void sendMessageByIntent(Context context, String phoneNumber, String smsContent) {
        if (phoneNumber == null || phoneNumber.length() < 4) {
            return;
        }
        Uri uri = Uri.parse("smsto:" + phoneNumber);
        Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
        intent.putExtra("sms_body", smsContent);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (intent.resolveActivity(context.getPackageManager()) != null) {
            context.startActivity(intent);
        }

    }
  • 調用系統發短信界面,需要用戶自己輸入接收方的電話號碼
/** 調用系統發短信界面,需要用戶自己輸入接收方的電話號碼
     *
     * 示例:SMSUtil.sendMessageByIntent(MainActivity.this,"你好");
     *
     * @param context
     * @param message
     */
    public static void sendMessageByIntent(Context context, String message) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.putExtra("sms_body", message);
        intent.setType("vnd.android-dir/mms-sms");
        if (intent.resolveActivity(context.getPackageManager()) != null) {
            context.startActivity(intent);
        }
    }
  • 直接調用短信接口發短信
/**
     * 直接調用短信接口發短信
     * @param ActivityOrSetvice    Activity
     * @param phoneNumber
     * @param smsContent
     */
    public static void sendMessageBySysterm(Context ActivityOrSetvice, String phoneNumber, String smsContent) {
        if (phoneNumber == null || phoneNumber.length() < 4) {
            return;
        }
        //獲取短信管理器
        android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();
        //拆分短信內容(手機短信長度限制)
        List<String> divideContents = smsManager.divideMessage(smsContent);
        for (String text : divideContents) {
            smsManager.sendTextMessage(phoneNumber, null, text, null, null);
        }
    }

別忘記了添加權限:

<uses-permission android:name="android.permission.SEND_SMS" />

二,通過BroadcastReceiver監聽短信的代碼

此段代碼實現的是通過監聽sms廣播,把或得到指定號碼的短信內容傳給聲明的TextView,爲什麼是TextView呢,因爲幾乎所有有setText()或android:setText=”“屬性的View都是TextView的子類(或間接子類)。

/**
 * 原理:
 * Android收到短信後系統會發送一個android.provider.Telephony.SMS_RECEIVED廣播。
 * 把它放在Bundle(intent.Extras)中,Bundle可以理解爲一個Map,短信採用"pdus"作爲鍵,
 * pdus應該是protocol description units的簡寫,也就是一組短信。
 * Android不是一接收到短信就立刻發出廣播,會有一定的延遲,
 * 所以就有可能有多條短信,所以纔會用數組來存放。
 */

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.TextView;

/**
 * 作者:liuyaowei ;日期:2016-07-16.
 * QQ:1054185214
 * 類作用:監聽短信廣播, 獲取短信
 */

public class SmsBroadcastReceiver extends BroadcastReceiver {

    private String address=null; //需要監聽的號碼
    private TextView text ;

    public SmsBroadcastReceiver(String address, TextView text) {
        this.address=address;
         this.text=text;
    }

    @Override
    public void onReceive(Context context, Intent intent) {


        Bundle bundle = intent.getExtras();
        SmsMessage[] smsMessages = null;
        Object[] pdus = null;
        if (bundle != null) {
            pdus = (Object[]) bundle.get("pdus");
        }
        if (pdus !=null){
            smsMessages = new SmsMessage[pdus.length];
            String sender = null;
            String content = null;


            for (int i=0; i<pdus.length; i++){
                smsMessages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                sender = smsMessages[i].getOriginatingAddress(); // 獲取短信的發送者
                content = smsMessages[i].getMessageBody(); // 獲取短信的內容

                if (sender.equals(address)){          //如果收到信息的號碼和指定的號碼相同
                    text.setText(content);                 //返回信息內容
                    break;
                }
            }
        }
    }


}

三,通過ContentObserver監聽短信的代碼

ContentObserver,是Android實現的內容觀察者其中一個實現(與另一類DataSetObservable都是抽象類ContentObserver的實現),目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理。這個是我在曾經看到並收集的認爲較好代碼實現,和上一個功能類似,更進一步處理短信內容得到驗證碼中的數字,稍加修改,把得到的驗證碼同樣傳入TextView中。

0){
            ContentValues values=new ContentValues();
            if (Build.VERSION.SDK_INT < 21){
                values.put("type",1);//修改短信爲已讀短信   5.0後以不能修改
            }
            cursor.moveToNext();
            int smsbodyColumn=cursor.getColumnIndex("body");
            String smsbody=cursor.getString(smsbodyColumn);

            text.setText(getDynamicPassword(smsbody)); //調用下面的截取短信中六位數字驗證碼的方法
        }

        // 在用managedQuery的時候,不能主動調用close()方法, 否則在Android 4.0+的系統上, 會發生崩潰
        if (Build.VERSION.SDK_INT < 14) {
            cursor.close();
        }
    }

    /**
     * 從字符串中截取連續6位數字組合 ([0-9]{" + 6 + "})截取六位數字 進行前後斷言不能出現數字 用於從短信中獲取動態密碼
     *
     * @param st 短信內容
     * @return 截取得到的6位動態密碼
     */
    public String getDynamicPassword(String st){
        //  6是驗證碼的位數一般爲六位   如果驗證碼的位數變化只要將6修改爲想要的位數,
        // 過驗證如果不止爲數字,直接修改正則爲想要的內容即可

        //Pattern是java.util.regex(一個用正則表達式所訂製的模式來對字符串進行匹配工作的類庫包)中的一個類。
        // 一個Pattern是一個正則表達式經編譯後的表現模式
        Pattern pattern=Pattern.compile("(?/**
 * 爲了減少用戶的操作步驟,在獲得短信驗證碼的時候,我們可以監聽特殊手機號碼的短信,
 * 截取信息當中的短信驗證碼(其實有很多應用都監聽短信例如360短信,一些信用卡或者是記賬類的應用)。
 *
 * 原理:可以使用一個自定義的BroadcastReceiver來監聽短信,在監聽結果當中過濾手機號,
 * 在需要回填的activity當中實現實例化廣播並且實現其回調接口,在接口當中進行回填驗證碼,
 * 在銷燬activity時銷燬鏈接。但是這樣操作會出現一些問題,
 * 由於一些其他的應用也會使用廣播監聽手機例如QQ通訊錄或者是360通訊錄等有的時候會被其攔截,
 * 即使修改優先級也會出現不能進行回填的問題。所有這裏可以採用另外一種的解決方法:
 * 使用ContentProvider來監聽短信數據庫的變化,
 * 在自定義的ContentObserver當中實現onChange的方法進行監聽特定手機號的短信,
 * 然後進行信息截取在填充到需要填充的位置。
 *
 * “ContentObserver,內容觀察者,目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理,
 * 它類似於數據庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。
 * 觸發器分爲表觸發器、行觸發器,相應地ContentObserver也分爲“表“ContentObserver、“行”ContentObserver,
 * 當然這是與它所監聽的Uri MIME Type有關的。”
 *
 * 摘自:自動填充短信驗證碼(使用ContentObserver):http://www.tuicool.com/articles/bMVRru
 */

import android.app.Activity;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.widget.EditText;
import android.widget.TextView;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 作者:liuyaowei ;日期:2016-07-16.
 * QQ:1054185214
 * 類作用:監聽短信數據庫, 獲取短信 驗證碼 
 */

public class SmsContentObserver extends ContentObserver {
    private Activity activity;
    private String address;
    private TextView text;

    public SmsContentObserver(Handler handler, Activity activity, String number, TextView text) {
        super(handler);
        this.activity=activity;
         this.address=number;
        this.text=text;
    }


    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);


        // 讀取收件箱中指定號碼的短信
//        content://sms/inbox 收件箱
//        content://sms/sent 已發送
//        content://sms/draft 草稿
//        content://sms/outbox 發件箱
//        content://sms/failed 發送失敗
//        content://sms/queued 待發送列表
        Cursor cursor= activity.managedQuery(Uri.parse("content://sms/inbox"),
                new String[]{"_id","address","body","read"},
                " address=? and read=?",
                new String[] { address, "0" }, "_id desc");

        // 按id排序,如果按date排序的話,修改手機時間後,讀取的短信就不準了
        if (cursor!=null&&cursor.getCount()>0){
            ContentValues values=new ContentValues();
            if (Build.VERSION.SDK_INT < 21){
                values.put("type",1);//修改短信爲已讀短信   5.0後以不能修改
            }
            cursor.moveToNext();
            int smsbodyColumn=cursor.getColumnIndex("body");
            String smsbody=cursor.getString(smsbodyColumn);

            text.setText(getDynamicPassword(smsbody)); //調用下面的截取短信中六位數字驗證碼的方法
        }

        // 在用managedQuery的時候,不能主動調用close()方法, 否則在Android 4.0+的系統上, 會發生崩潰
        if (Build.VERSION.SDK_INT < 14) {
            cursor.close();
        }
    }

    /**
     * 從字符串中截取連續6位數字組合 ([0-9]{" + 6 + "})截取六位數字 進行前後斷言不能出現數字 用於從短信中獲取動態密碼
     *
     * @param st 短信內容
     * @return 截取得到的6位動態密碼
     */
    public String getDynamicPassword(String st){
        //  6是驗證碼的位數一般爲六位   如果驗證碼的位數變化只要將6修改爲想要的位數,
        // 過驗證如果不止爲數字,直接修改正則爲想要的內容即可

        //Pattern是java.util.regex(一個用正則表達式所訂製的模式來對字符串進行匹配工作的類庫包)中的一個類。
        // 一個Pattern是一個正則表達式經編譯後的表現模式
        Pattern pattern=Pattern.compile("(?<![0-9])([0-9]{" + 6 + "})(?![0-9])") ;
        Matcher matcher=pattern.matcher(st);
        String dynamicPassword=null;
        while (matcher.find()){
            System.out.println(matcher.group());
            dynamicPassword=matcher.group();
        }
        return dynamicPassword;
    }
}

四,對上面BroadcastReceiver,ContentObserver註冊取消監聽的封裝

對BroadcastReceiver註冊和取消監聽的封裝:

static BroadcastReceiver receiver;
    /**
     * 供其它組件調用 註冊短信變化監聽
     * @param context
     */
    public static void registerSmsBroadcastReceiver(Activity context,String number,TextView text){
        if (receiver!=null){
            return;
        }
        receiver= new SmsBroadcastReceiver(number,text);
        //註冊短信變化監聽
        context.registerReceiver(receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));

    }

    /**
     * 供其它組件調用 關閉數據庫監聽
     * @param context
     */
    public static void unregisterSmsBroadcastReceiver(Activity context){
        if (receiver!=null) {
            //關閉數據庫監聽
            context.unregisterReceiver(receiver);
        }
    }

對ContentObserver註冊和取消監聽的封裝:

static SmsContentObserver content;
    /**
     * <uses-permission android:name="android.permission.RECEIVE_SMS" />
     * 供其它組件調用 註冊短信變化監聽
     * @param context
     */
    public static void registerSmsContentObserver(Activity context,String number,TextView text){
        if (content!=null){
            return;
        }
        content= new SmsContentObserver(new Handler(),context,number,text);
        //註冊短信變化監聽
        context.getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, content);
    }

    /**
     * 供其它組件調用 關閉數據庫監聽
     * @param context
     */
    public static void unregisterSmsContentObserver(Activity context){
        if (content!=null) {
            //關閉數據庫監聽
            context.getContentResolver().unregisterContentObserver(content);
        }
    }

五,不要忘記添加權限

我把這些封裝的代碼都放到SmsPhoneUtil.java工具類中,在使用中直接通過SmsPhoneUtil調用相關方法就可以了,很方便吧,不過在發送短信和監聽短信的時候千萬不要忘記添加相應權限。
還有一個通過反射修改短信數據庫的示例,還沒比較滿意的封裝,就暫不貼出來了,留待以後仔細研究之後再說。

/**
 * 作者:liuyaowei ;日期:2016-07-14.
 * QQ:1054185214
 * 類作用:Sms and Phone工具類
 */

public class SmsPhoneUtil {
    private BitmapUtil() {
        throw new Error("Do not instantiate it!/不要實例化");
    }
}
    <!-- 發送短信 -->
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <!-- 讀取短信 -->
    <uses-permission android:name="android.permission.READ_SMS"/>
    <!-- 接收短信 -->
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
發佈了38 篇原創文章 · 獲贊 71 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章