Android的BroadcastReceiver 廣播 短信攔截*


    如何去理解BroadcastReceiver(廣播)?其實可以這樣想,首先我們要有一個發送廣播的“媒體”,在這個例子中,我們暫且用activity組件作為這個媒體,當然以后會用到service,或者隨機啟動方式來發送廣播,這看業務需求來決定。在這個例子中,當點擊按鈕的時候,一條廣播就發送了出去,同樣用到了意圖對象Intent。和啟動activity和service一樣,我們需要為意圖對象設置“標記”和“包裹”,它就像個基站,向世界發送信號。而對於廣播自身而言,他就像一個監聽器,一個系統級別的監聽器,當然他也有他所監聽的內容,不是什么都監聽的,那么如何確定廣播到底在監聽什么呢?

    上面提到,意圖這個基站被激活后向外發送信號,這個信號是帶着“標記(action)”的,比如,基站發送了一條標記為 setAction("wuchen.broadcastReceiver.MY_RECEIVER")的信號,而在世界中監聽的廣播君,如果想要監聽到這條信號,就需要讓世界知道自己的存在,並且告訴世界自己要監聽的信號的標記是什么,都要記錄在案。這就需要在Manifest清單文件中進行注冊: 

    <receiver android:name=".MyBroadcastReceiver" >
        <intent-filter>
            <action android:name="wuchen.broadcastReceiver.MY_RECEIVER" />
        </intent-filter>
    </receiver>

    一但注冊完成功后,每當有基站發送的信號同廣播君向世界注冊的標記匹配時,廣播君就會啟動並截獲信息。其實廣播接聽的這個過程,上面的比喻需要調整一下,更嚴謹的說法是廣播並不是隨時都保持在一個監聽的狀態,而是在接收到Intent發出的廣播和自己本身注冊的信息匹配時,才會被觸發,而且由於自身屬於系統級別監聽和程序級別監聽不同的是:程序級別監聽在程序終止后就沒有監聽作用了,而系統級別監聽在被激活后會有自己的進程,比如當一個程序打開,並激活了一個系統級別監聽,當這個程序關閉后,系統級別監聽並不會隨着這個激活他的程序的關閉而停止處理業務的作用,還會繼續運行,除非他自己本身處理完業務需求后生命周期結束。

    嚴格上講,系統級別監聽的broadcastReceiver激活后也只能最多運行10秒鍾,如果10秒內還沒有處理完業務,Android會認為該程序無響應,並彈出ANR(application No Response)對話框。對於這種”限制“,我們處理這種限制的方式,也是項目中用到的最多一種方法:啟動service。最可靠的方式是在廣播中啟動一個service,讓service在后台去處理數據,這樣就算廣播生命周期結束,service也不會被銷毀,因為用startService這種方式啟動的service,就算訪問者(廣播)銷毀自己也不會銷毀,而且本來在廣播中處理數據的耗時操作,交給了service去做,同時避免了ANR問題的出現。用啟動新線程的方式也不可靠,廣播的生命周期,就是內部onReceive方法的周期,當onReceiver方法結束,也就意味着廣播的結束。

    雖然我們可以在onReceiver內執行啟動一個新線程執行耗時的業務,由於onReceiver方法周期很短暫,可能出現的情況是:子線程還沒有結束廣播就關閉了,廣播一旦關閉,雖然子線程還可以運行,但系統會認為廣播處於無組件活動的狀態,在內存緊張的時候優先結束該進程,這樣可能會導致子線程不能執行完任務。所以最好的方式是啟動service。  

    需要注意的是,世界中接收同一個標記信號的廣播君不局限只能存在一個,也許廣播君A和廣播君B監聽的是同一個信號這是決不沖突的。但究竟是廣播君A先接收到信號還是廣播君B先接收到,這就涉及到了有序廣播,和有序廣播相對應的是普通廣播。


    普通廣播:普通廣播可以讓所有的廣播君一起接收到信號(邏輯上),如果有10個廣播君,那么是個都可以接收到,無法停止信號的傳播。


    有序廣播:有序廣播傳播的方式如同傳遞,比如有10個廣播君,A傳到B,B再到C,以此類推,如果傳到D后,D想終止信號的傳播,那么D以后的廣播君就接收不到信號了。雖然這10個廣播君都在等待接收同一個信號,但在D這里,就有權利終止信號的傳輸。 有序廣播還有一個特點,廣播君可以將自己的內容傳給下一位唯一需要確定的是,到底是廣播君A先接收到還是廣播君B先接收到,這就需要在廣播君們在注冊時,考慮到”優先級“的問題了。在配置文件的每個receiver中的intent-filter標簽中添加一個屬性android:priority="100",數字越高優先級越大。

    <receiver android:name=".MyBroadcastReceiver" >
        <intent-filter android:priority="11" >
            <action android:name="wuchen.broadcastReceiver.MY_RECEIVER" />
        </intent-filter>
    </receiver>

    <receiver android:name=".MyBroadcastReceiver_B" >
        <intent-filter android:priority="10" >
            <action android:name="wuchen.broadcastReceiver.MY_RECEIVER" />
        </intent-filter>
    </receiver>

普通廣播(Normal Broadcast):

一,優缺點:和有序廣播的優缺點相反!

二,發送廣播的方法:sendBroadcast()

 

有序廣播(Ordered Broadcast):

一,優缺點

優點:1,按優先級的不同,優先Receiver可對數據進行處理,並傳給下一個Receiver

             2,通過abortBroadcast可終止廣播的傳播  

缺點:效率低  

二,發送廣播的方法:sendOrderedBroadcast()   

三,優先接收到Broadcast的Receiver可通過setResultExtras(Bundle)方法將處理結果存入Broadcast中,下一個Receiver 通過 Bundle bundle=getResultExtras(true)方法獲取上一個 Receiver傳來的數據     

上面講了很多關於廣播的很多基礎概念,那么BroadcastReceiver在實際的應用中到底會起到樣的作用呢?

因為廣播是一個系統級別的監聽器,所以它可以將不同進程間通信的一個“紐帶”。舉個簡單的例子,我們知道在我們啟動service是如果用startService這種方式啟動時,訪問者和service之間是不能進行數據的“互動”的,但是有了BroadcastReceiver就一樣可以進行互動,方法我大致說一下,在activity端創建一個broadcast,同樣在service端也創建一個broadcast,相互監聽對方的動作,這樣就可以達到進程間通信的目的了。

短信攔截

BroadcastReceiver還可以監聽系統進程,比如android的收短信,電量低,電量改變,系統啟動,等……只要BroadcastReceiver監聽了這些進程,就可以實現很多有趣的功能,比如,接收短信的這條廣播是一條有序廣播,所以我們可以監聽這條信號,在傳遞給真正的接收程序時,我們將自定義的廣播接收程序的優先級大於它,並且取消廣播的傳播,這樣就可以實現攔截短信的功能了。

我們只需要一個BroadcastReceiver類,就可以實現這個功能,不需要activity,也不需要布局文件。當然如果需要更加的商業化,需要改進的地方還有很多。比如我們可以做一個界面讓用戶從聯系人中指定一個號碼進行短信攔截,也可以自定義。

public class SmsReceiver extends BroadcastReceiver {
        // 當接收到短信時被觸發
 @Override public void onReceive(Context context, Intent intent) { // 如果是接收到短信 if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { // 取消廣播(這行代碼將會讓系統收不到短信)  abortBroadcast(); StringBuilder sb = new StringBuilder(); // 接收由SMS傳過來的數據 Bundle bundle = intent.getExtras(); // 判斷是否有數據 if (bundle != null) { // 通過pdus可以獲得接收到的所有短信消息 Object[] pdus = (Object[]) bundle.get("pdus"); // 構建短信對象array,並依據收到的對象長度來創建array的大小 SmsMessage[] messages = new SmsMessage[pdus.length]; for (int i = 0; i < pdus.length; i++) { messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); } // 將送來的短信合並自定義信息於StringBuilder當中 for (SmsMessage message : messages) { sb.append("短信來源:"); // 獲得接收短信的電話號碼  sb.append(message.getDisplayOriginatingAddress()); sb.append("\n------短信內容------\n"); // 獲得短信的內容  sb.append(message.getDisplayMessageBody()); } } Toast.makeText(context, sb.toString(), 5000).show(); } } }
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="wuchen.broadcastReceiver"
    android:versionCode="1"
    android:versionName="1.0" >

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >

        <receiver android:name=".SmsReceiver" >

            <intent-filter android:priority="800" >

                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
    </application>
    <!-- 授予程序接收短信的權限 -->

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

</manifest>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM