相信技术的力量

Android监听手机短信

有两种实现方式,监听短信和监听短信数据库.

方式一:短信监听

以下情况可能会导致短信拦截失败:

  • 小米,360等品牌手机拦截短信,短信的优先级给了系统
  • 用户禁用短信权限
  • 手机连接电脑,被电脑端的手机助手类软件截获
  • 手机内装有QQ通讯录之类的管理联系人,短信的应用,被截获.

定义短信广播接收者


public class SMSBroadcastReceiver extends BroadcastReceiver {

    private static OnReceivedMessageListener mOnReceivedMessageListener;
    public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";

    public SMSBroadcastReceiver() {
        super();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(SMS_RECEIVED_ACTION)) {
            Object[] pdus = (Object[]) intent.getExtras().get("pdus");
            for(Object pdu:pdus) {
                SmsMessage smsMessage = SmsMessage.createFromPdu((byte [])pdu);
                String sender = smsMessage.getDisplayOriginatingAddress();
                String content = smsMessage.getDisplayMessageBody();
                long date = smsMessage.getTimestampMillis();
                Date tiemDate = new Date(date);
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String time = simpleDateFormat.format(tiemDate);
                //过滤不需要读取的短信的发送号码
                if ("106902780116481".equals(sender)) {
                    mOnReceivedMessageListener.onReceived(getDynamicPassword(content));
                    abortBroadcast();
                }
            }
        }
    }

    public interface OnReceivedMessageListener {
        void onReceived(String message);
    }

    public void setOnReceivedMessageListener(OnReceivedMessageListener onReceivedMessageListener) {
        this.mOnReceivedMessageListener = onReceivedMessageListener;
    }

    /**
     * 从字符串中截取连续6位数字组合 ([0-9]{" + 6 + "})截取六位数字 进行前后断言不能出现数字 用于从短信中获取动态密码
     * @param str 短信内容
     * @return 截取得到的6位动态密码
     */
    public String getDynamicPassword(String str) {
        // 6是验证码的位数,一般为六位
        Pattern continuousNumberPattern = Pattern.compile("(?<![0-9])([0-9]{" + 6 + "})(?![0-9])");
        Matcher m = continuousNumberPattern.matcher(str);
        String dynamicPassword = "";
        while (m.find()) {
            dynamicPassword = m.group();
        }
        return dynamicPassword;
    }
}

注册广播

SMSBroadcastReceiver = mSMSBroadcastReceiver = new SMSBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter(SMSBroadcastReceiver.SMS_RECEIVED_ACTION);
intentFilter.setPriority(Integer.MAX_VALUE);
this.registerReceiver(mSMSBroadcastReceiver, intentFilter);
ToastManager.getInstance().showLongToast("注册短信监听");

mSMSBroadcastReceiver.setOnReceivedMessageListener(new SMSBroadcastReceiver.OnReceivedMessageListener() {
    @Override
    public void onReceived(String message) {
        //do something
    }
});

方式二:监听短信数据库

内容提供者中对外通信方式有3种:

方式1:定义为Activity的内部类,直接在onChang()方法中处理业务逻辑,比如收到短信之后设置给EditText

  • 优点:方便
  • 缺点:耦合度高,复用性差

方式2:定义接口,Activity实现这个接口,拿到回调,处理业务逻辑

  • 优点:耦合度低,复用性高
  • 缺点没用充分利用Handler资源

代码如下:

/**
 * Created by wxw on 2016/9/24 10:34
 * function: 监听短信数据库
 * e-mail:wangxw725@163.com
 */
public class SmsContentObserver extends ContentObserver {
    private Cursor cursor = null;
    private Context mContext;
    private String[] projection = new String[]{"_id","body"};
    private OnMessageObservedListener mOnMessageObservedListener;

    public SmsContentObserver(Context context,Handler handler) {
        super(handler);
        this.mContext = context;
    }

    public interface OnMessageObservedListener {
        void onObservedMessage(String message);
    }
    public void setOnMessageObservedListener(OnMessageObservedListener onMessageObservedListener) {
        this.mOnMessageObservedListener = onMessageObservedListener;
    }

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

        cursor = mContext.getContentResolver().query(Uri.parse("content://sms/inbox"), projection,null, null, "_id desc");

        if (cursor != null && cursor.getCount() > 0) {
            cursor.moveToNext();
            int smsbodyColumn = cursor.getColumnIndex("body");
            String smsBody = cursor.getString(smsbodyColumn);
            mOnMessageObservedListener.onObservedMessage(getDynamicPassword(smsBody));
        }
        cursor.close();
    }

    private String getDynamicPassword(String str) {
        Pattern continuousNumberPattern = Pattern.compile("(?<![0-9])([0-9]{" + 6 + "})(?![0-9])");
        Matcher m = continuousNumberPattern.matcher(str);
        String dynamicPassword = "";
        while (m.find()) {
            dynamicPassword = m.group();
        }
        return dynamicPassword;
    }
}

方式3:通过Handler发送消息,在Activity中进行业务逻辑处理

代码如下:

/**
 * Created by wxw on 2016/9/24 10:34
 * function: 监听短信数据库
 * e-mail:wangxw725@163.com
 */
public class SmsContentObserver extends ContentObserver {
    private Cursor cursor = null;
    private Context mContext;
    private Handler mHandler;
    private String[] projection = new String[]{"_id","body"};

    public SmsContentObserver(Context context,Handler handler) {
        super(handler);
        this.mContext = context;
        this.mHandler = handler;
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        cursor = mContext.getContentResolver().query(Uri.parse("content://sms/inbox"), projection,null, null, "_id desc");

        if (cursor != null && cursor.getCount() > 0) {
            cursor.moveToNext();
            int smsbodyColumn = cursor.getColumnIndex("body");
            String smsBody = cursor.getString(smsbodyColumn);
            mHandler.obtainMessage(1,getDynamicPassword(smsBody)).sendToTarget();
        }
        cursor.close();
    }

    private String getDynamicPassword(String str) {
        Pattern continuousNumberPattern = Pattern.compile("(?<![0-9])([0-9]{" + 6 + "})(?![0-9])");
        Matcher m = continuousNumberPattern.matcher(str);
        String dynamicPassword = "";
        while (m.find()) {
            dynamicPassword = m.group();
        }
        return dynamicPassword;
    }
}

注册观察者

smsContentObserver = new SmsContentObserver(this, smsHandler);
getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, smsContentObserver);

handler中处理消息

private Handler smsHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {

        switch (msg.what){
            case 0:
                ......
                break;
            case 1:
                LogUtils.x("Activity收到了短息码:"+(String)msg.obj);
                lf.mEtPassword.setText((String)msg.obj);
                break;
        }
    }
};

注销观察者

@Override
protected void onDestroy() {
    super.onDestroy();
    ......
    if(smsContentObserver!=null){
        getContentResolver().unregisterContentObserver(smsContentObserver);
        smsContentObserver = null;
    }
}

遗留问题

观察者会多次回调onChang方法()

参考博客:

http://blog.csdn.net/qinjuning/article/details/7047607

http://blog.csdn.net/u014452224/article/details/53910230

⬆️