Android簡訊監聽實現,及Android4.4之後簡訊機制變更
阿新 • • 發佈:2019-01-07
前陣子公司有一個專案,簡單的監聽簡訊應用,功能只有如下兩個:
1.監聽簡訊並獲取簡訊內容上傳伺服器;
2.從伺服器獲取簡訊內容,傳送出去
按照傳統的思路,監聽簡訊我們有兩種方式;第一種是使用廣播方式監聽簡訊廣播到來,第二種則是通過觀察者,監聽資料庫資料變化。
其中,指的注意的是Android4.4之後版本,新引入了預設簡訊應用的概念,系統可以設定由某個應用來處理簡訊;
本文我們將帶人們分析以下幾個問題: 1.監聽資料庫變化方式監聽簡訊 2.通過廣播監聽簡訊內容 3.Android 4.4以上版本簡訊許可權問題 4.Android4.4版本以上設定預設簡訊應用
1.
既然是監聽資料庫變化 那我們就應該清楚簡訊的資料庫表結構:
sms主要結構:
_id: 簡訊序號,如100
thread_id:對話的序號,如100,與同一個手機號互發的簡訊,其序號是相同的
address: 發件人地址,即手機號,如+86138138000
person: 發件人,如果發件人在通訊錄中則為具體姓名,陌生人為null
date: 日期,long型,如1346988516,可以對日期顯示格式進行設定
protocol: 協議0SMS_RPOTO簡訊,1MMS_PROTO彩信
read: 是否閱讀0未讀,1已讀
status: 簡訊狀態-1接收,0complete,64pending,128failed
type: 簡訊型別1是接收到的,2是已發出
body: 簡訊具體內容
service_center:簡訊服務中心號碼編號,如+8613800755500 既然需要操作資料庫,便少不了使用ContentResolver,所以我們應該還需要了解,簡訊的content uri : 全部簡訊:content://sms/ 收件箱:content://sms/inbox 發件箱:content://sms/sent 草稿箱:content://sms/draft
在這裡我們只是寫了一個方法查詢資料庫,但是還有一個問題就是我們應該在什麼時候去查資料庫,總不能起個執行緒去輪訓,這樣太耗費資源了,這裡我們可以是用觀察者模式;
針對Android4.4版本的,Google提供了 SMS_DELIVER_ACTION(sms)和 WAP_PUSH_DELIVER_ACTION(MMS)這兩個intent給預設的簡訊使用,也就是說只有預設簡訊才可以收到這兩個廣播,也只有收到這兩個廣播的簡訊應用才可以對簡訊資料庫機型操作,其他的簡訊應用可以使用SMS_RECEIVED_ACTION對簡訊進行監聽,但僅僅只能讀取(理論上可以監聽,但是在一臺6.0系統的三星機器上並不能監聽到,具體什麼原因沒查出來,當然這是在我所寫的應用沒有成為手機預設簡訊應用的情況下,當設定為預設簡訊應用後監聽也是正常的) 另外,值得一提的是,在Android4.4版本之前SMS_RECEIVED_ACTION是一個有序廣播,這意味著在Android4.4版本之前,應用在接受廣播之後可以對廣播進行攔截;但是在Android4.4之後,這個攔截動作不會生效,這就意味著Android4.4之後,非預設簡訊應用對簡訊除了讀操作外,沒有更多的許可權了
ComposeSmsActivity.java
HeadlessSmsSendService.java
最後別忘了新增相應的許可權:
下面貼出參考的相關部落格,供大家全面瞭解Android簡訊機制:
本文我們將帶人們分析以下幾個問題: 1.監聽資料庫變化方式監聽簡訊 2.通過廣播監聽簡訊內容 3.Android 4.4以上版本簡訊許可權問題 4.Android4.4版本以上設定預設簡訊應用
1. 監聽資料庫變化方式監聽簡訊內容
既然是監聽資料庫變化 那我們就應該清楚簡訊的資料庫表結構:
sms主要結構:_id: 簡訊序號,如100
thread_id:對話的序號,如100,與同一個手機號互發的簡訊,其序號是相同的
address: 發件人地址,即手機號,如+86138138000
person: 發件人,如果發件人在通訊錄中則為具體姓名,陌生人為null
date: 日期,long型,如1346988516,可以對日期顯示格式進行設定
protocol: 協議0SMS_RPOTO簡訊,1MMS_PROTO彩信
read: 是否閱讀0未讀,1已讀
status: 簡訊狀態-1接收,0complete,64pending,128failed
type: 簡訊型別1是接收到的,2是已發出
body: 簡訊具體內容
service_center:簡訊服務中心號碼編號,如+8613800755500 既然需要操作資料庫,便少不了使用ContentResolver,所以我們應該還需要了解,簡訊的content uri : 全部簡訊:content://sms/ 收件箱:content://sms/inbox 發件箱:content://sms/sent 草稿箱:content://sms/draft
private Uri SMS_INBOX = Uri.parse("content://sms/inbox"); public void getSmsFromPhone() { ContentResolver cr = getContentResolver(); String[] projection = new String[] { "body","address" };//"_id", "address", "person",, "date", "type String where = " date > " + (System.currentTimeMillis() - 10 * 60 * 1000); Cursor cur = cr.query(SMS_INBOX, projection, where, null, "date desc"); if (null == cur) return; if (cur.moveToFirst()) { String number = cur.getString(cur.getColumnIndex("address"));//手機號 String body = cur.getString(cur.getColumnIndex("body")); //TODO 這裡是具體處理邏輯 } }
在這裡我們只是寫了一個方法查詢資料庫,但是還有一個問題就是我們應該在什麼時候去查資料庫,總不能起個執行緒去輪訓,這樣太耗費資源了,這裡我們可以是用觀察者模式;
private SmsObserver smsObserver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_login);
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();
}
}
在這裡我們註冊了一個觀察者,監聽收件箱的變化,一旦收件箱變化,我們就查詢資料庫,去除最新的一條資料
相應的許可權這裡就不貼出來了
至此我們就實現了通過監聽資料庫的方式來監聽簡訊內容
2.通過廣播監聽簡訊內容
public class SmsReceiver extends BroadcastReceiver {
public static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
private static final String TAG = "yjj";
public SmsReceiver() {
Log.i("yjj", "new SmsReceiver");
}
@Override
public void onReceive(final Context context, Intent intent) {
Log.i(TAG, "jie shou dao");
Cursor cursor = null;
try {
if (SMS_RECEIVED.equals(intent.getAction())) {
Log.d(TAG, "sms received!");
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
final SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
if (messages.length > 0) {
String content = messages[0].getMessageBody();
String sender = messages[0].getOriginatingAddress();
long msgDate = messages[0].getTimestampMillis();
String smsToast = "New SMS received from : "
+ sender + "\n'"
+ content + "'";
Toast.makeText(context, smsToast, Toast.LENGTH_LONG)
.show();
Log.d(TAG, "message from: " + sender + ", message body: " + content
+ ", message date: " + msgDate);
//自己的邏輯
}
}
cursor = context.getContentResolver().query(Uri.parse("content://sms"), new String[] { "_id", "address", "read", "body", "date" }, "read = ? ", new String[] { "0" }, "date desc");
if (null == cursor){
return;
}
Log.i(TAG,"m cursor count is "+cursor.getCount());
Log.i(TAG,"m first is "+cursor.moveToFirst());
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Exception : " + e);
} finally {
if (cursor != null) {
cursor.close();
cursor = null;
}
}
}
}
這個很簡單就是定義一個廣播接收者,並且在清單檔案中註冊(註冊有兩種方式,這裡就不展開了)
<receiver android:name=".message.SmsReceiver" android:permission="android.permission.BROADCAST_SMS">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_DELIVER" />
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
3.Android 4.4以上版本簡訊許可權問題
Android4.4版本之前,簡訊有著一個問題,任何應用只要想,就可以操作簡訊,著包括監聽簡訊、修改簡訊、刪除簡訊、攔截簡訊等,因而市面上有著成片的簡訊應用,著也使得Android系統簡訊的管理變得越發的混亂。 針對這個問題Google在Android4.4版本之後,引進了一個新的概念----預設簡訊應用。即使用者可以在系統中選擇由哪個應用預設對簡訊進行處理。針對Android4.4版本的,Google提供了 SMS_DELIVER_ACTION(sms)和 WAP_PUSH_DELIVER_ACTION(MMS)這兩個intent給預設的簡訊使用,也就是說只有預設簡訊才可以收到這兩個廣播,也只有收到這兩個廣播的簡訊應用才可以對簡訊資料庫機型操作,其他的簡訊應用可以使用SMS_RECEIVED_ACTION對簡訊進行監聽,但僅僅只能讀取(理論上可以監聽,但是在一臺6.0系統的三星機器上並不能監聽到,具體什麼原因沒查出來,當然這是在我所寫的應用沒有成為手機預設簡訊應用的情況下,當設定為預設簡訊應用後監聽也是正常的) 另外,值得一提的是,在Android4.4版本之前SMS_RECEIVED_ACTION是一個有序廣播,這意味著在Android4.4版本之前,應用在接受廣播之後可以對廣播進行攔截;但是在Android4.4之後,這個攔截動作不會生效,這就意味著Android4.4之後,非預設簡訊應用對簡訊除了讀操作外,沒有更多的許可權了
4.Android4.4版本以上設定預設簡訊應用
我們已經分析了Android4.4版本之後簡訊的改變----預設簡訊應用,但是並不是每個應用都可以被設定為預設簡訊應用,接下來我們來實現一下怎麼讓我們的簡訊應用可以被設定為預設短息應用。<!-- BroadcastReceiver that listens for incoming MMS messages -->
<receiver android:name=".message.MmsReceiver"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<!-- Activity that allows the user to send new SMS/MMS messages -->
<activity android:name=".message.ComposeSmsActivity" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</activity>
<!-- Service that delivers messages from the phone "quick response" -->
<service android:name=".message.HeadlessSmsSendService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
首先我們子在清單檔案中寫入如下資訊,可以看到我們定義了一個MmsReceiver、一個ComposeSmsActivity、一個HeadlessSmsSendService
很顯然我們需要這三個對應的java檔案,即一個receiver類、一個service類、一個Activity類;這三個了都可以不需要任何內容,具體程式碼如下
MmsReceiver.java
public class MmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
}
}
ComposeSmsActivity.java
public class ComposeSmsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
}
HeadlessSmsSendService.java
public class HeadlessSmsSendService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
通過以上步驟,我們所寫的應用就可以被設定為預設簡訊應用了最後別忘了新增相應的許可權:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
這裡貼出的是我整個專案的許可權,世紀應該只需要SMS相關的許可權,這裡就不做區分了下面貼出參考的相關部落格,供大家全面瞭解Android簡訊機制: