Android必知必會的四大元件 -- Broadcast Receiver篇

ClericYi發表於2020-02-25

Android必知必會的四大元件 -- Broadcast Receiver篇

前言

廣播,在我們的應用中起著一個非常重要的角色。就比如說我們經常使用的IntentIntentFilter,就有著廣播的作用。

在我的Android工具包專案中就整合了網路廣播的動態註冊。

思維導圖

Android必知必會的四大元件 -- Broadcast Receiver篇

生命週期

因為沒有直接的圖示可以上,而且Broadcast中並沒有onCreateonDestroy這樣方法,只能通過官方文件驗證。

Android必知必會的四大元件 -- Broadcast Receiver篇
圖中的圈紅框的加粗文字大概意思就是,使用靜態廣播進行註冊,那麼每接受到一次資訊,他就不復存在了,也就是需要重建。其它方式構建的生命週期,與關聯的Activity中的具體操作相關。

在Android 8.0以後已經不在支援靜態廣播了

兩種廣播

public class NetworkReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
       if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION) && App.getInstance() != null) {
           App.getInstance().notifyObservers(isNetConnected(context));
       }
   }
}
複製程式碼

雖然是兩種廣播形式,但是他們同樣要幹一件事情,就是繼承BroadcastReceiver,並重寫onReceive()方法。

全域性廣播

這個廣播同樣可以使用在應用內,但是這種廣播的安全性有待質疑。

// 訊息傳遞
sendBroadcast(Intent);
複製程式碼
  • 靜態廣播註冊
 <receiver android:name="com.clericyi.basehelper.network.NetworkReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BATTERY_LOW"/>
    </intent-filter>
</receiver>
複製程式碼
  • 動態廣播註冊

和靜態廣播不同的地方,動態廣播註冊完以後需要進行登出操作。

// 註冊
networkReceiver = new NetworkReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(networkReceiver, intentFilter);

// 登出(如果沒有登出,將會發生記憶體洩漏)
unregisterReceiver(networkReceiver);
複製程式碼

應用內廣播

  1. 優點:
    • 傳送的廣播只會在自己的App內傳播,不會洩漏給其他App,保障了資料的安全性。
    • 無法接受到其他App的廣播,也就省去各種麻煩事。
    • 相較於全域性廣播效率更高。
  2. 使用方法
//註冊
networkReceiver = new NetworkReceiver();
localBroadcastManager = LocalBroadcastManager.getInstance(this); // --> 以單例模式進行建立
localBroadcastManager.registerReceiver(networkReceiver, new IntentFilter("需要去過濾的資訊"));

// 傳送訊息
localBroadcastManager.sendBroadcast(Intent);

// 登出
localBroadcastManager.unregisterReceiver(networkReceiver);
複製程式碼

LocalBroadcastManager原始碼導讀

為什麼要導讀LocalBroadcastManager原始碼呢?

其實是想讓讀者們知道LocalBroadcastManager使用並不是Binder機制來完成通訊的。

getInstance()

public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext()); // 1 -->
            }
            return mInstance;
        }
    }
// 由註釋1直接呼叫的方法    
private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }
複製程式碼

那這裡就是很清楚的知道,這是一個以DCL的方式,來直接完成對單例的建立,而在建構函式中,定義了一個Handler

那我們就來做一個猜測,我們在應用內的廣播本質其實是基於一個Handler的一非同步傳輸機制。為了驗證!!我們就需要去了解他的sendBroadcast(Intent)方法。

sendBroadcast(Intent)

public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            // 拿到傳遞過來的Intent中儲存的資料
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            // 。。。。。
            
            // 獲取配置的Action
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                // 。。。。。 對變數receivers的一些列操作。

                // 存在接受物件時,將資料通過Handler的方式傳遞出去。
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }
複製程式碼

通過程式碼已經成功驗證了,其實LocalBroadcast最終基於的資料傳輸機制就是我們的Handler,這就是和應用間廣播最大的不同之處了。

總結

  1. 動態廣播和靜態廣播的區別?
    • 靜態廣播:廣播一直存在,消耗資源較大,耗電量大。
    • 動態廣播:廣播的生命週期較為靈活,資源消耗少。響應速度快於靜態廣播。
  2. 廣播同樣會引發ANR的慘狀,廣播的耗時操作時長不允許超過10s。而且廣播內一般也不會像ServiceActivity一樣會使用Thread來完成我們的耗時操作。
  3. 全域性和應用內的廣播兩者的註冊方式其實相似,但是針對的場景不同。如果需要網路、電池等服務,你就需要全域性廣播;如果你只需要應用內通訊,那麼你只需要應用內廣播。
  4. 應用內廣播(LocalBroadcast)使用的Handler的訊息傳輸機制;應用間廣播或者說是程式間廣播(Broadcast)使用的則是Binder的機制。

以上就是我的學習成果,如果有什麼我沒有思考到的地方或是文章記憶體在錯誤,歡迎與我分享。


相關文章推薦:

Android必知必會的四大元件 -- Service篇

Glide都在用的LruCache,你會幾分?

Android事件分發機制,你瞭解過嗎?

相關文章