Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android 四大組件之再論BroadCast,androidbroadcast

Android 四大組件之再論BroadCast,androidbroadcast

編輯:關於android開發

Android 四大組件之再論BroadCast,androidbroadcast


BroadCast 是android提供的跨進程通訊的有一利器。

1.異步執行onReceiver

    @Nullable
    public abstract Intent registerReceiver(BroadcastReceiver receiver,
            IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler);

這是context裡面注冊廣播的API,duplex2個我們不常用的東東。

我們分別來討論這2個東西。

先討論異步handler。

如果我們傳入一個handler,會怎樣?我們所有的onReceiver是運行在主線程嗎?

package com.joyfulmath.samples.broadcastsample; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import com.joyfulmath.servicesample.R; import com.joyfulmath.servicesample.TraceLog; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Click; import org.androidannotations.annotations.EActivity; /** * Created by Administrator on 2016/10/23 0023. */ @EActivity(R.layout.activity_main) public class BcSampleActivity extends Activity { HandlerThread mThread = null; Handler nHandler = null; BroadCastAAA aReceiver = new BroadCastAAA(); @Override protected void onResume() { super.onResume(); TraceLog.i(); } @Override protected void onPause() { super.onPause(); TraceLog.i(); unregisterReceiver(aReceiver); } @Override protected void onDestroy() { super.onDestroy(); aReceiver = null; } @AfterViews void initViews() { TraceLog.i(); mThread = new HandlerThread("samples") { @Override protected void onLooperPrepared() { nHandler = new Handler(getLooper()) { @Override public void dispatchMessage(Message msg) { TraceLog.i(msg.toString()); super.dispatchMessage(msg); } @Override public void handleMessage(Message msg) { TraceLog.i(); dispatchMsg(msg); } }; IntentFilter filter = new IntentFilter("com.joyfulmath.sample.broadcast.action"); registerReceiver(aReceiver, filter, null, nHandler); } }; mThread.start(); } @Click(R.id.btn_connect) void onBtnClick() { TraceLog.i(); // nHandler.sendEmptyMessage(0x01); sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action")); } void dispatchMsg(Message msg) { switch (msg.what) { case 0x1: TraceLog.i(); break; } TraceLog.i(); } public class BroadCastAAA extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { TraceLog.i(); } } } broadcastsample

通過上述代碼,我們添加了一個運行在handlerThread裡面的nHandler。

log如下:

10-22 22:32:21.332 16889-16889/com.joyfulmath.servicesample I/BcSampleActivity$override: onBtnClick:  [at (BcSampleActivity.java:79)]
10-22 22:32:21.347 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$1$1$override: dispatchMessage: { when=-2ms callback=android.app.LoadedApk$ReceiverDispatcher$Args target=com.joyfulmath.samples.broadcastsample.BcSampleActivity$1$1 } [at (BcSampleActivity.java:59)]
10-22 22:32:21.348 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$BroadCastAAA: onReceive:  [at (BcSampleActivity.java:100)]

可以看到,onReceive沒有運行在主線程,運行在哪裡?就是nHandler的線程。

還有一個:callback=android.app.LoadedApk$ReceiverDispatcher$Args 這個是什麼,看名字可以猜測,這個就是分發Receiver的東西。

可以看這個registerReceiver的注釋:

Handler identifying the thread that will receive
     *      the Intent.  If null, the main thread of the process will be used.

所以onReceiver 是可以運行在子線程的。這裡有個問題:public class BroadCastAAA extends BroadcastReceiver

是內部類,如果運行在子線程,會不會導致內存洩露?

這些問題後續在分析.

2.Permission

 權限管理分2塊:

public abstract void sendBroadcast(Intent intent,
            @Nullable String receiverPermission);

這裡有個permission

還有就是上一節提到的permission。

第一種:

        sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action"), Contants.BROADCAST_ACTION);
Permission Denial: receiving Intent { act=com.joyfulmath.sample.broadcast.action flg=0x10 } to ProcessRecord{39c0edf1 2286:com.joyfulmath.servicesample/u0a62} (pid=2286, uid=10062) requires com.joyfulmath.samples.broadcast.action due to sender com.joyfulmath.servicesample (uid 10062)

可以看到報permission denial。就是沒有權限。報的錯誤是基於進程的。

permission定義在發送app,接受app需要userpermission。

當然解決這個錯誤就是在manifest裡面use這條permission。

第二種:

registerReceiver(aReceiver, filter, "com.joyfulmath.samples.broadcast.permission", nHandler);

靜態注冊也一樣。

這條語句的意思是:只有含有permisson的progress發送的廣播才能被收到。

permission定義在receiver中,發送app需要userpermission才能收到。

3.Local Broadcast:App應用內廣播(此處的App應用以App應用進程為界)

相比於全局廣播,App應用內廣播優勢體現在:

1.安全性更高;

2.更加高效。

為此,Android v4兼容包中給出了封裝好的LocalBroadcastManager類,用於統一處理App應用內的廣播問題,使用方式上與通常的全局廣播幾乎相同,只是注冊/取消注冊廣播接收器和發送廣播時將主調context變成了LocalBroadcastManager的單一實例。

4.不同注冊方式的廣播接收器回調onReceive(context, intent)中的context具體類型

1).對於靜態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是ReceiverRestrictedContext;

2).對於全局廣播的動態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Activity Context;

3).對於通過LocalBroadcastManager動態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Application Context。

注:對於LocalBroadcastManager方式發送的應用內廣播,只能通過LocalBroadcastManager動態注冊的ContextReceiver才有可能接收到(靜態注冊或其他方式動態注冊的ContextReceiver是接收不到的)。

 

5.不同Android API版本中廣播機制相關API重要變遷

1).Android5.0/API level 21開始粘滯廣播和有序粘滯廣播過期,以後不再建議使用;

2).”靜態注冊的廣播接收器即使app已經退出,主要有相應的廣播發出,依然可以接收到,但此種描述自Android 3.1開始有可能不再成立“

Android 3.1開始系統在Intent與廣播相關的flag增加了參數,分別是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

FLAG_INCLUDE_STOPPED_PACKAGES:包含已經停止的包(停止:即包所在的進程已經退出)

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已經停止的包

主要原因如下:

自Android3.1開始,系統本身則增加了對所有app當前是否處於運行狀態的跟蹤。在發送廣播時,不管是什麼廣播類型,系統默認直接增加了值為FLAG_EXCLUDE_STOPPED_PACKAGES的flag,導致即使是靜態注冊的廣播接收器,對於其所在進程已經退出的app,同樣無法接收到廣播。

詳情參加Android官方文檔:http://developer.android.com/about/versions/android-3.1.html#launchcontrols

由此,對於系統廣播,由於是系統內部直接發出,無法更改此intent flag值,因此,3.1開始對於靜態注冊的接收系統廣播的BroadcastReceiver,如果App進程已經退出,將不能接收到廣播。

但是對於自定義的廣播,可以通過復寫此flag為FLAG_INCLUDE_STOPPED_PACKAGES,使得靜態注冊的BroadcastReceiver,即使所在App進程已經退出,也能能接收到廣播,並會啟動應用進程,但此時的BroadcastReceiver是重新新建的。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
4 intent.putExtra("name", "qqyumidi");
5 sendBroadcast(intent);

注1:對於動態注冊類型的BroadcastReceiver,由於此注冊和取消注冊實在其他組件(如Activity)中進行,因此,不受此改變影響。

注2:在3.1以前,相信不少app可能通過靜態注冊方式監聽各種系統廣播,以此進行一些業務上的處理(如即時app已經退出,仍然能接收到,可以啟動service等..),3.1後,靜態注冊接受廣播方式的改變,將直接導致此類方案不再可行。於是,通過將Service與App本身設置成不同的進程已經成為實現此類需求的可行替代方案。

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved