安卓權威編程指南-筆記(第27章 broadcast intent)

來源:互聯網
上載者:User

標籤:visible   缺陷   style   manifest   mission   不同的   運行   nal   狀態   

本章需求:首先,讓應用輪詢新結果並在有所發現時及時通知使用者,即使使用者重啟裝置後還沒有開啟過應用。其次,保證使用者在使用應用時不出現新結果通知。

 

1. 一般intent和broadcast intent

  許多系統組件需要知道某些事件的發生(WIFI訊號時有時無,電話的呼入等),為滿足這樣的需求,Andorid提供了broadcast intent 組件。

  broadcast intent的工作原理類似於之前學過的intent,但不同的是broadcast intent可以被多個叫做broadcast receiver的組件接收、

2. 接受系統broadcast : 重啟後喚醒

 

2.1 standalone receiver

  standalone receiver 是一個在manifest設定檔中聲明的broadcast receiver。即使應用進程已消滅,standalone receiver也可以被啟用。(還有一種是可以同fragemt或activity的生命週期綁定的dynamic receiver)

  broadcast receiver必須在系統中登記後才能發揮作用,如果不登記,系統就不知道該向哪裡發送intent,broadcast receiver的onReceiver()方法也就得不到預定的調用了。

     要登記broad receiver,首先要建立它:

public class StartupReceiver extends BroadcastReceiver {    private static final String TAG = "StartupReceiver";    @Override    public void onReceive(Context context, Intent intent) {  //onReceiver是在主線程中執行的        Log.i(TAG, "Received broadcast intent: " + intent.getAction());        boolean isOn = QueryPreferences.isAlarmOn(context);        PollService.setServiceAlarm(context, isOn);    }}

  broadcast receiver是接受intent的組件,當有intent發送給StartupReceiver時,它 的onReceive()方法會被調用。

  然後在AndroidManifest.xml檔案中聲明:

  

<usespermissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" /><receiver android:name=".StartupReceiver"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>

  完成聲明後,即使app並未運行,只要有匹配的broadcast intent發來,broadcast receiver就會醒來接受,一收到intent,broadcast receiver的onReceive(Context. Intent)方法即開始執行,隨後會被銷毀。

 

 

3. 過濾前台通知訊息

  PhotoGallery應用的另一缺陷,通知訊息雖然很有用,但應用開著的時候不應該受到通知訊息。

  解決方式:

    首先,我們發送(或接收)定製版broadcast intent(最後會鎖定它,只允許PhotoGallery應用組件接收它)。其次,不再使用manifest檔案,改用代碼為broadcast intent動態登記receiver。(動態註冊的receiver與fragment進行綁定,收到廣播時說明是在app中) 最後,發送一個有序broadcast在一組receiver中傳遞資料,藉此保證最後才運行某個receiver(最後的receiver決定顯不顯示通知,這個receiver是靜態註冊的)。

  3.1 發送broadcast intent

    要發送broadcast intent,需要建立一個intent,並傳入sendBroadcast(intent)方法即可。

public static final String ACTION_SHOW_NOTIFICATION = "com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));

  3.2 動態broadcast receiver

    動態broadcast receiver是在代碼中,而不是在設定檔中完成登記聲明。要在代碼中登記,可調用registerReceiver(BroadcastReceiver, IntentFliter)方法,取消登錄時,則調用unregiseterReceiver

(BroadcastReceiver)方法。receiver自身通常被定義為一個內部類執行個體,如同一個按鈕點擊監聽器。在registerReceiver()和unregisterReceiver()方法的BroadcastReceiver需要的是同一個執行個體、

    我們要只在應用開啟的時候接受發過來的廣播過濾,就不能在 manifest 中聲明一個過濾器,而是要動態地建立一個廣播接收器。我們在這裡建立一個用於隱藏前台通知的通用 fragment 子類:

    

public abstract class VisibleFragment extends Fragment {    private static final String TAG = "VisibleFragment";    @Override    public void onStart() {        super.onStart();        IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);        getActivity().registerReceiver(mOnShowNotification, filter,                PollService.PERM_PRIVATE, null);    }    @Override    public void onStop() {        super.onStop();        getActivity().unregisterReceiver(mOnShowNotification);    }    private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            // 如果接收到廣播,說明應用正在前台,所以把 ResultCode 更改掉            Log.i(TAG, "canceling notification");            setResultCode(Activity.RESULT_CANCELED);        }    };}

 

  3.3 使用私人許可權

    使用動態broadcast receiver存在一個問題,即系統中的任何應用均可監聽並觸發我們的receiver。

    有兩種辦法可以阻止應用闖入我們的私人領域,一種辦法是在mainfest設定檔裡給receiver標籤添加一個android:exported= “false”屬性,聲明它僅限應用內部使用。

    

    另外,也可以建立自己的使用許可權,可以通過在AndroidManifest.xml中添加一個permission標籤來完成:

<permission android:name="com.bignerdranch.android.photogallery.PRIVATE"android:protectionLevel="signature" /><uses-permission android:name="com.bignerdranch.android.photogallery.PRIVATE" />

    要使用許可權,須將其作為參數傳入sendBroadcast(),有了這個許可權,所有應用都必須使用同樣的許可權才能接受我們發送的intent。

    要怎麼保護我們的broad receiver呢?其他應用可通過建立自己的broadcast intent來觸發它。同樣,在 registerReceiver(...) 方法中傳入自訂許可權就能解決該問題:

public abstract class VisibleFragment extends Fragment {    ...    @Override    public void onStart() {        super.onStart();        IntentFilter filter = newIntentFilter(PollService.ACTION_SHOW_NOTIFICATION);        getActivity().registerReceiver(mOnShowNotification, filter,        PollService.PERM_PRIVATE, null);    }    ...}    

   

 

     3.3.1 深入學習安全層級          

        自訂許可權必須指定 android:protectionLevel 屬性值。Android根據 protectionLevel 屬性值確定自訂許可權的使用方式。在PhotoGallery應用中,我們使用的 protectionLevel 是signature 。signature 安全層級表明,如果其他應用需要使用我們的自訂許可權,則必須使用和當前應用相同的key做簽名認證。對於僅限應用內部使用的許可權,選擇 signature 安全層級比較合適。既然其他開發人員沒有相同的key,自然也就無法接觸到許可權保護的東西。此外,有了自己的key,將來還可用於我們開發的其他應用中。

 

 

 

 

    3.4 使用有序broadcast

    如果想讓程式在開啟時不發送出通知,就不能再讓服務來發出通知了,因為它無法知道前台的運行狀態。所以我們讓 PollService 發送一個有序廣播。

     

public static final String REQUEST_CODE = "REQUEST_CODE";public static final String NOTIFICATION = "NOTIFICATION";private void showBackgroundNotification(int requestCode, Notification notification) {    Intent i = new Intent(ACTION_SHOW_NOTIFICATION);    i.putExtra(REQUEST_CODE, requestCode);    i.putExtra(NOTIFICATION, notification);    sendOrderedBroadcast(i, PERM_PRIVATE, null, null,                                Activity.RESULT_OK, null, null);}      

 

    有序廣播是按照優先順序發送的,先發送給優先順序高的接收器,再發給優先順序低的接收器。因為在應用結束後也要發出通知,顯然我們發出通知的廣播接收器是需要聲明在 manifest 檔案中的。

    內部實現如下:

    

public class NotificationReceiver extends BroadcastReceiver {    private static final String TAG = "NotificaitonReceiver";    @Override    public void onReceive(Context context, Intent intent) {        Log.i(TAG, "received result: " + getResultCode());        if (getResultCode() != Activity.RESULT_OK) {            // PollService 發出的 intent 帶的結果碼是 RESULT_OK            // 如果接到的不是,說明應用在前台,將結果碼修改了            return;        }            // 如果沒有 return,說明應用不在前台,就可以發出通知了。        int requestCode = intent.getIntExtra(PollService.REQUEST_CODE, 0);        Notification notification = (Notification)                intent.getParcelableExtra(PollService.NOTIFICATION);        NotificationManagerCompat notificationManager =                NotificationManagerCompat.from(context);        notificationManager.notify(requestCode, notification);    }}

 

  

<receiver android:name=".NotificationReceiver"          android:exported="false">    <!-- 在這裡將優先順序設為最低,即 -999 -->    <intent-filter        android:priority="-999">        <action android:name="com.kniost.photogallery.SHOW_NOTIFICATION" />    </intent-filter></receiver>

 

    3.5 receiver與長時間運行任務

    如不想受限與主線程的時間限制,希望broadcast intent觸發一個長時間運行任務,該如何做呢?

  •      將任務交給服務處理,然後通過broadcast receiver啟動服務。
  •         使用BroadcastReceiver.getAsync()方法。該方法返回一個 BroadcastReceiver.PendingResult 對象,隨後可使用該對象提供結果。因此,可將 PendingResult 交給AsyncTask 去執行長時啟動並執行任務,然後再調用 PendingResult 的方法響應broadcast。
  •       goAsync() 方法的弊端是不夠靈活。我們仍需快速響應broadcast(10秒內),並且與使用服務相比,沒什麼架構模式好選擇。當然, goAsync() 方法也有明顯的優勢:可調用該方法設定有序broadcast的結果。

 

      3.6 使用EventBus

broadcast intent可實現系統內全域性的訊息傳遞。如果僅需要應用內的訊息事件廣播,該怎
麼做呢?答案是使用事件匯流排(event bus)。

事件匯流排的設計思路就是,提供一個應用內的組件可以訂閱的共用匯流排或資料流。事件一旦
發布到匯流排上,各訂閱組件就會被啟用並執行相應的回調代碼。

由greenrobot出品的EventBus是目前廣為人知的一個第三方事件匯流排庫。

為實現在應用內發送broadcast intent,Android自己也提供了一個叫作 LocalBroadcast-
Manager 的廣播管理類;但上述第三方類庫用起來更為靈活和方便。

    

 

安卓權威編程指南-筆記(第27章 broadcast intent)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.