之前有被問到過Android普通廣播和本地廣播的區(qū)別,所以打算分析下本地廣播的實現(xiàn)原理以及簡單結(jié)束兩者的區(qū)別豁鲤,算是對自我的一此源碼學(xué)習(xí)總結(jié)茎杂。
目前官網(wǎng)文檔上是說LocalBroadcastManager
被廢棄了兜材,如果想使用的話需要自行依賴或者使用LiveData
(后面會簡單介紹下其用法)來代替它理澎。
基本使用
首先我們在項目添加本地廣播的依賴
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
界面是Activity
包裹兩個Fragment
逞力,一個負責(zé)接受廣播刷新界面,另一個負責(zé)發(fā)送廣播糠爬。
布局比較簡單寇荧,貼個大概就曉得了。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.localbroad.LocalBroadActivity">
<fragment
android:id="@+id/fragmentOne"
android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadOneFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_local_broad_one" />
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="@android:color/black" />
<fragment
android:id="@+id/fragmentTwo"
android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadTwoFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_local_broad_two" />
</LinearLayout>
從上圖可以看到执隧,除本地廣播之外我還增加了普通廣播發(fā)送的功能揩抡,目的也是為了下文的跨應(yīng)用接收做驗證。
來看看代碼的實現(xiàn)部分:
class LocalBroadOneFragment : Fragment() {
companion object {
const val LOCAL = "com.vico.livedatademo_local"
const val EXPORT = "com.vico.livedatademo_export"
}
private lateinit var localBroadcast: LocalBroadcast
private lateinit var exportBroadcast: ExportBroadcast
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_local_broad_one, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//注冊本地廣播
localBroadcast = LocalBroadcast()
//指定action
val localIntentFilter = IntentFilter(LOCAL)
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(localBroadcast, localIntentFilter)
//注冊普通廣播
exportBroadcast = ExportBroadcast()
val exportIntentFilter = IntentFilter(EXPORT)
requireContext().registerReceiver(exportBroadcast, exportIntentFilter)
}
override fun onDetach() {
//別忘了最后要進行注銷的操作
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(localBroadcast)
requireContext().unregisterReceiver(exportBroadcast)
super.onDetach()
}
//自定義廣播镀琉,用來接收本地廣播發(fā)來的數(shù)據(jù)
inner class LocalBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == LOCAL) {
tv1.text = intent.getStringExtra(LocalBroadTwoFragment.LOCAL_EVENT) ?: ""
}
}
}
inner class ExportBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == EXPORT) {
tv2.text = intent.getStringExtra(LocalBroadTwoFragment.EXPORT_EVENT) ?: ""
}
}
}
}
class LocalBroadTwoFragment : Fragment() {
companion object {
const val LOCAL_EVENT = "local_event"
const val EXPORT_EVENT = "export_event"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_local_broad_two, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//發(fā)送一條包含時間戳內(nèi)容的本地廣播峦嗤,指定action
btnLocal.setOnClickListener {
val localIntent = Intent(LocalBroadOneFragment.LOCAL)
localIntent.putExtra(LOCAL_EVENT, System.currentTimeMillis().toString())
LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(localIntent)
}
//發(fā)送一條包含時間戳內(nèi)容的普通廣播,指定action
btnNormal.setOnClickListener {
val exportIntent = Intent(LocalBroadOneFragment.EXPORT)
exportIntent.putExtra(EXPORT_EVENT, (System.currentTimeMillis() / 100).toString())
requireContext().sendBroadcast(exportIntent)
}
}
}
來看下運行效果:
em........這樣好像也沒什么區(qū)別嘛屋摔,沒事我們在新啟一個應(yīng)用來看看能否接收當(dāng)前應(yīng)用發(fā)出的廣播烁设。
新建的Phone Module代碼跟上面接收廣播的類似
OtherActivity:
class OtherActivity : AppCompatActivity() {
companion object {
const val LOCAL = "com.vico.livedatademo_local"
const val EXPORT = "com.vico.livedatademo_export"
}
private lateinit var localBroadcast: LocalBroadcast
private lateinit var exportBroadcast: ExportBroadcast
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_other)
localBroadcast = LocalBroadcast()
val localIntentFilter = IntentFilter(LOCAL)
LocalBroadcastManager.getInstance(this).registerReceiver(localBroadcast, localIntentFilter)
exportBroadcast = ExportBroadcast()
val exportIntentFilter = IntentFilter(EXPORT)
registerReceiver(exportBroadcast, exportIntentFilter)
}
override fun onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(localBroadcast)
unregisterReceiver(exportBroadcast)
super.onDestroy()
}
inner class LocalBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == LOCAL) {
tv1.text = intent.getStringExtra("local_event") ?: ""
}
}
}
inner class ExportBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == EXPORT) {
tv2.text = intent.getStringExtra("export_event") ?: ""
}
}
}
}
activity_other.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".OtherActivity">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="來自本地廣播的值" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="來自普通廣播的值" />
</LinearLayout>
運行一下,看下這個新建的應(yīng)用能否接收到另一個應(yīng)用發(fā)送出來的廣播钓试。
確實本地廣播無法做到跨應(yīng)用發(fā)送装黑,那么其內(nèi)部的實現(xiàn)原理是什么呢?下面我們一起來看看
LocalBroadcastManager原理
通過對LocalBroadcastManager
的使用我們可以得知弓熏,LocalBroadcastManger
采用了單例設(shè)計模式恋谭,將其構(gòu)造函數(shù)私有化⊥炀希看下構(gòu)造函數(shù)里做了些上什么:
private final Context mAppContext;
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
private static LocalBroadcastManager mInstance;
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
//初始化一個在主線程運行的Handler
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
//執(zhí)行待處理的廣播
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
嗯疚颊,在構(gòu)造函數(shù)里初始化了一個在主線程運行的Handler,在接收到消息后就會執(zhí)行待處理的廣播。
好的滞谢,接下去看看注冊registerReceiver
函數(shù)里做了什么:
//維護不同BroadcastReceiver的ReceiverRecord集合的HashMap
private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
= new HashMap<>();
//維護不同Action的ReceiverRecord集合的HashMap
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
private static final class ReceiverRecord {
final IntentFilter filter;
final BroadcastReceiver receiver;
boolean broadcasting;
//是否被注銷
boolean dead;
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
filter = _filter;
receiver = _receiver;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(128);
builder.append("Receiver{");
builder.append(receiver);
builder.append(" filter=");
builder.append(filter);
if (dead) {
builder.append(" DEAD");
}
builder.append("}");
return builder.toString();
}
}
/**
* 注冊一個接收匹配給定的IntentFilter任何本地廣播
*
* @param receiver 處理廣播的BroadCastReceiver
* @param filter
*/
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
//將filter和receiver包裝成ReceiverRecord對象
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
//查找mReceivers這個HashMap里是否有對應(yīng)的key串稀,有則在value里添加一條記錄除抛,無則put
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
//mActions同樣也是這個道理
for (int i=0; i<filter.countActions(); i++) {
String action = filter.getAction(i);
ArrayList<ReceiverRecord> entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
}
entries.add(entry);
}
}
}
可以看出registerReceiver
函數(shù)是對兩個HashMap的新增操作狮杨,那么unregisterReceiver
同理是對兩個HashMap的刪除操作:
public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
synchronized (mReceivers) {
final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
for (int i=filters.size()-1; i>=0; i--) {
final ReceiverRecord filter = filters.get(i);
//將ReceiverRecord標記為已注銷
filter.dead = true;
for (int j=0; j<filter.filter.countActions(); j++) {
final String action = filter.filter.getAction(j);
final ArrayList<ReceiverRecord> receivers = mActions.get(action);
if (receivers != null) {
for (int k=receivers.size()-1; k>=0; k--) {
final ReceiverRecord rec = receivers.get(k);
if (rec.receiver == receiver) {
//將ReceiverRecord標記為已注銷
rec.dead = true;
receivers.remove(k);
}
}
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
好的,看完注冊和注銷到忽,我們看看發(fā)送廣播sendBroadcast(Intent intent)
是怎么實現(xiàn)的:
private static final String TAG = "LocalBroadcastManager";
private static final boolean DEBUG = false;
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
private static final class BroadcastRecord {
final Intent intent;
final ArrayList<ReceiverRecord> receivers;
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
intent = _intent;
receivers = _receivers;
}
}
public boolean sendBroadcast(@NonNull Intent intent) {
synchronized (mReceivers) {
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();
final boolean debug = DEBUG ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
if (debug) Log.v(
TAG, "Resolving type " + type + " scheme " + scheme
+ " of intent " + intent);
//查詢是否有action匹配的記錄橄教,無則結(jié)束
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null) {
if (debug) Log.v(TAG, "Action list: " + entries);
//初始化一個要發(fā)送的記錄
ArrayList<ReceiverRecord> receivers = null;
for (int i=0; i<entries.size(); i++) {
ReceiverRecord receiver = entries.get(i);
if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
//跳過正在執(zhí)行的記錄
if (receiver.broadcasting) {
if (debug) {
Log.v(TAG, " Filter's target already added");
}
continue;
}
//看IntentFilter是否匹配,不匹配就結(jié)束
int match = receiver.filter.match(action, type, scheme, data,
categories, "LocalBroadcastManager");
if (match >= 0) {
if (debug) Log.v(TAG, " Filter matched! match=0x" +
Integer.toHexString(match));
if (receivers == null) {
receivers = new ArrayList<ReceiverRecord>();
}
//往要發(fā)送的集合里添加
receivers.add(receiver);
receiver.broadcasting = true;
} else {
if (debug) {
String reason;
switch (match) {
case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
default: reason = "unknown reason"; break;
}
Log.v(TAG, " Filter did not match: " + reason);
}
}
}
if (receivers != null) {
for (int i=0; i<receivers.size(); i++) {
receivers.get(i).broadcasting = false;
}
//往待執(zhí)行廣播的集合填充
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
//Handler發(fā)送一條消息喘漏,最終在handleMessage里會執(zhí)行處理廣播的操作executePendingBroadcasts
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
最后看看executePendingBroadcasts
:
void executePendingBroadcasts() {
while (true) {
final BroadcastRecord[] brs;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
final BroadcastRecord br = brs[i];
final int nbr = br.receivers.size();
for (int j=0; j<nbr; j++) {
final ReceiverRecord rec = br.receivers.get(j);
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent);
}
}
}
}
}
將mPendingBroadcasts里元素進行復(fù)制并清空护蝶,復(fù)制后的數(shù)組遍歷執(zhí)行receiver
。本地廣播的流程到這里就結(jié)束了翩迈,下面講講它和普通的廣播有什么區(qū)別持灰。
和普通廣播的區(qū)別在哪?
兩個不同的應(yīng)用在Android系統(tǒng)中相當(dāng)于兩個不同的進程负饲。當(dāng)前應(yīng)用發(fā)出的普通廣播能夠被其他應(yīng)用所接收到堤魁,那么這勢必是一個進程間通信的過程(IPC
)喂链。
廣播的注冊分為靜態(tài)和動態(tài)兩種注冊方式,其中靜態(tài)注冊的廣播在應(yīng)用安裝時由系統(tǒng)自動完成注冊妥泉,具體點是PMS(PackageMangerService)完成的椭微。這里就只簡單分析下廣播的動態(tài)注冊過程。動態(tài)注冊的過程從ContextWrapper
的registerReceiver
方法開始盲链。ContextWrapper
并沒有做什么實際工作蝇率,將過程交給了ContextImpl
來完成。
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
ContextImpl
的registerReceiver
方法調(diào)用了自己的registerReceiverInterval
方法刽沾,實現(xiàn)如下:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
系統(tǒng)首先從mPackageInfo
獲取IIntentReceiver
對象本慕,然后再采用跨進程的方式向AMS發(fā)送廣播注冊的請求。IIntentReceiver
是一個Binder
接口侧漓,它的具體實現(xiàn)是LoadedApk.ReceiverDispatcher.InnerRecevier
,ReceiverDispatcher
的內(nèi)部同時保存了BroadcastReceiver
和InnerReceiver
间狂,這樣當(dāng)接收到廣播時,ReceiverDispatcher
可以很方便地調(diào)用BroadcastReceiver
的onReceive
方法火架。
相比而言鉴象,使用LocalBroadcastManager
來收發(fā)本地廣播效率更高(無需進行進程間通信),并且無需考慮其他應(yīng)用在收發(fā)廣播時帶來的任何安全問題何鸡。
使用LiveData替換纺弊?
開頭說到LocalBroadcastManager
已經(jīng)被廢棄,需要引入依賴支持骡男∠危可以使用LiveData
來代替實現(xiàn)相關(guān)功能。這里就簡單舉例一下:
界面如上圖所示隔盛,F(xiàn)ragment1犹菱、Fragment2以及包裹它們的Activity都共同持有一個ViewModel
。點擊Fragment2中的按鈕吮炕,F(xiàn)ragment1中的TextView和中間的分割線會發(fā)生變化腊脱。
創(chuàng)建一個需要用到的ViewModel
:
class LiveDataViewModel : ViewModel() {
/**
* 只暴露不可變的LiveData給外部。
*/
private val _value = MutableLiveData<String>()
val value: LiveData<String> get() = _value
fun setValue() {
_value.value = System.currentTimeMillis().toString()
}
}
在Fragment2中更改value的值:
class LiveDataTwoFragment : Fragment() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_live_data_two, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)
btnLiveData.setOnClickListener {
viewModel.setValue()
}
}
}
在Fragment1和Activity中觀察value的變化刷新UI:
class LiveDataActivity : AppCompatActivity() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
viewModel = ViewModelProvider(this).get(LiveDataViewModel::class.java)
viewModel.value.observe(this, Observer {
val random = Random()
val r = random.nextInt(255)
val g = random.nextInt(255)
val b = random.nextInt(255)
viewLine.setBackgroundColor(Color.rgb(r, g, b))
})
}
}
class LiveDataOneFragment : Fragment() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_live_data_one, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)
viewModel.value.observe(requireActivity(), Observer { value ->
tvLiveData.text = value
})
}
}
最終的效果如下:
END
文章到這里就已經(jīng)結(jié)束了龙亲,這一次就LocalBroadcastManager的實現(xiàn)原理和與普通廣播的區(qū)別做了介紹陕凹,以及相關(guān)的其他方案。文中的LiveData
+ViewModel
的實現(xiàn)只是其中一種方式而已鳄炉,還有其他諸多優(yōu)秀的方案可以選擇杜耙,比如:EventBus
,RxBus
,LiveEventBus
等等。
由于個人水平的原因拂盯,可能無法更深層次的去介紹佑女,希望能多多包涵。我也同廣大的開發(fā)者一樣,一步步地在慢慢成長团驱。