BroadcastReceiver詳解
使用方式
在Manifest.xml檔案中注冊
<receiver
android:name=".test.broadcast.MyTestReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
<action android:name="android.intent.action.CONFIGURATION_CHANGED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
在Manifesh.xml檔案中注冊自定義的BroadcastReceiver,當意圖過濾器中的動作發生時,會回調BroadcastReceiver中的onRecevie方法.
public class MyTestReceiver extends BroadcastReceiver {
private static final String TAG = "MyTestReceiver";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
String action = intent.getAction();
Log.d(TAG, "onReceive: receive action is " + action);
}
}
}
代碼中動态注冊
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent == null ? "" : intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equalsIgnoreCase(action)) {
Log.i(TAG, "onReceive: 螢幕關閉");
}
context.unregisterReceiver(this);
}
}, intentFilter);
在Activity上下文中調用registerReceiver方法,動态注冊一個廣播接收者,同時指定了意圖過濾器。
這裡要注意注冊的BroadcastReceiver最好是靜态類,防止記憶體洩漏。否則要及時取消注冊。
原理
注冊通知
靜态注冊
通過PackageManagerService安裝應用時,會解析安裝包中的Manifest.xml檔案,存儲解析到Receiver标簽對應的廣播接收器。
通過Unix Socket接收需要安裝的Apk檔案
//android/os/FileBridge.java
public class FileBridge extends Thread {
private static final int MSG_LENGTH = 8;
private final FileDescriptor mServer = new FileDescriptor();
private final FileDescriptor mClient = new FileDescriptor();
private FileDescriptor mTarget;
//構造函數
//建立Unix Domain Socket的用戶端和服務端,然後會将代表用戶端的檔案描述符發送給用戶端程序
public FileBridge() {
try {
Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
} catch (ErrnoException e) {
throw new RuntimeException("Failed to create bridge");
}
}
//mTarget檔案描述符用來存儲用戶端程序發送過來的Apk檔案資料
public void setTargetFile(FileDescriptor target) {
mTarget = target;
}
public FileDescriptor getClientSocket() {
return mClient;
}
//這是一個線程,不停監聽Socket的Server端
@Override
public void run() {
final byte[] temp = new byte[8192];//讀緩沖區
//讀取Socket的Server端,如果沒有資料會阻塞
//每次先讀取8位元組,前4個位元組代表資料類型,後4四個位元組代表資料長度
while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
//按照大端序讀取前4位元組
final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
//資料寫入,将apk檔案資料寫入到mTraget代表的檔案描述中
if (cmd == CMD_WRITE) {
int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
while (len > 0) {
int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
if (n == -1) {
throw new IOException(
"Unexpected EOF; still expected " + len + " bytes");
}
IoBridge.write(mTarget, temp, 0, n);
len -= n;
}
//資料同步,并回顯給用戶端确認
} else if (cmd == CMD_FSYNC) {
Os.fsync(mTarget);
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
//關閉消息通道,并回顯給用戶端确認
} else if (cmd == CMD_CLOSE) {
Os.fsync(mTarget);
Os.close(mTarget);
mClosed = true;
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
break;
}
}
}
}
驗證APK合法性
- 包名、版本号
- 簽名(V2+V1)sudo
- 其他應用驗證package-verifier
- 應用申請的權限,需要使用者确認
- 拷貝安裝包到data/data/{包名}/目錄下,并且複制Native庫到lib目錄下
解析Apk檔案
- 添加apk檔案路徑到AssetManager管理中
- 解析Manifest.xml檔案資訊到Package類中,區分是多個apk檔案安裝,還是單個apk檔案安裝
- 驗證該安裝包是否隻用于測試
- 為Package類填充證書和簽名資訊
- 是否覆寫安裝,需要驗證簽名是否一緻、包配置是否一緻等。還要替換應用資源
//frameworks/base/core/java/android/content/pm/PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
...
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth))
{
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError,
false,owner.baseHardwareAccelerated);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
...
}
}
從上面解析過程可以看出,Android系統把Activity和BroadcastReceiver都當做了Activity類型來存儲的。
parseActivity方法中,還會繼續解析裡面的intent-filter标簽,以及其action/category/data等子标簽。
所有從Manifest.xml檔案中解析出來的資料,會存儲到Package類中。
public final static class Package {
public String packageName;
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
...
}
儲存元件資訊
将Package類中的四大元件資訊以及整個Package資訊儲存到PackageManagerService中。同時還會儲存到Settings類中,Settings類代表了系統的動态設定資訊。
//PackageManagerService.java
//系統中所有程式資訊
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
// 系統中所有程式的四大元件資訊
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
final ServiceIntentResolver mServices = new ServiceIntentResolver();
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
//掃描包資訊
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
...
//儲存Package中的BroadcastReceiver資訊
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
//這裡把Receiver當做Activity來處理
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
}
動态注冊
通過ActivityManagerServer注冊
//frameworks/base/core/java/android/app/ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
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 = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我們在Context上下文中調用registerReceiver(BroadcastReceiver, IntentFilter)方法動态注冊一個廣播時,會先儲存到ActivityManagerServer中。
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
//如果調用者傳入BroadcastReceiver是null,代表想要獲得一個粘性廣播
//粘性廣播直接通過registerReceiver方法的傳回值傳回給調用者。
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (receiver == null) {
return sticky;
}
//将BroadcastReceiver存儲到一個HashMap中
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
//ReceiverList是一個ArrayList子類,用來存儲BroadcastReceiver和對應的過濾器
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
//儲存過濾器到IntentResolver中
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);
}
發送、接收通知
當應用程式或系統程式發出一個廣播時,會調用到ActivityManagerService中,ActivityManagerService會從自己儲存的BroadcastReceiver清單和PackageManagerService儲存的BroadcastReceiver清單中找出符合意圖過濾器的廣播接收者,然後向其發送廣播。
//ActivityManagerService.java
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
//1. 預設不給已經stopped停止的應用發送廣播
//2. 如果沒有完成開機操作,例如正在更新系統,隻允許注冊廣播,不能發送
//3. 如果廣播指定的目的使用者,那麼這個使用者必須處于運作狀态
//4. 驗證是否是系統應用發出的受保護的廣播。受保護的廣播隻能由系統應用發出。
//系統應用的判斷标準是使用者id:ROOT_UID/SYSTEM_UID/PHONE_UID/BLUETOOTH_UID/NFC_UID,或者是常駐應用
//5. 視窗小部件的配置和更新廣播也不能由應用發出。由于曆史原因,它們并不是受保護的廣播,需要單獨判斷
//6. 對特殊廣播的處理:由PackageManager發出的應用被删除或變更的廣播,
//需要ActivityManagerService作出對應的操作,例如從最近使用應用中删除它們的Activity。
//特殊類型的廣播處理完成後,就直接傳回了
//7. 從PackageManagerService中查詢符合過濾器條件的廣播接受者List<ResolveInfo>,這裡就是應用安裝時,解析出來的靜态注冊的廣播接受者。
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags,
user).getList();
//8. 從ActivityManagerService的ContentResolver中查詢符合條件的廣播接受者,這裡代表通過代碼動态注冊的廣播接受者
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
//9. 将兩個清單按照優先級合并成一個清單
//10. 構造一個BroadcastRecord,添加到隊列BroadcastQueue中,然後開始排程執行。
}
//BroasdcastQueue.java
public final class BroadcastQueue {
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
//11. BroadcastQueue中的廣播被分為并發廣播和順序廣播。并發廣播會立即被執行,而不需要等待其他的廣播執行完畢。順序廣播則會逐一執行。
//我們在注冊廣播時可以指定是否是順序廣播,或者粘性廣播被認為是并發廣播
final void processNextBroadcast(boolean fromMsg) {
BroadcastRecord r;
//12. 開始處理清單中的廣播。先處理并發廣播
//13. 如果處理廣播的程序不存在,還需要等待程序的建立
//14. 處理動态注冊的廣播,在BroadcastQueue中以BroadcastFilter形式存儲
performReceiveLocked(filter.receiverList.app,
filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
//15. 處理靜态注冊的廣播,如果需要,還要提前喚起對應程序。如果目标程序開始運作後,再分發對應的廣播
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode,
mService.mProcessStats);
processCurBroadcastLocked(r, app);
}
}
performReceiveLocked方法代碼如下:
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
if (app != null) {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser,
app.repProcState);
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
processCurBroadcastLocked方法如下:
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
app.thread.scheduleReceiver(new Intent(r.intent),
r.curReceiver,
mService.compatibilityInfoForPackageLocked(
r.curReceiver.applicationInfo),
r.resultCode,
r.resultData,
r.resultExtras,
r.ordered,
r.userId,
app.repProcState);
}
取消注冊
取消動态注冊的廣播接收器
前面說過動态注冊的廣播接收器是存儲在ActivityManagerService中的,那麼取消注冊的時候,也是通過跨程序通信,告知ActivityManagerService移除指定的廣播接收器。
//ActivityManagerService.java
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
public void unregisterReceiver(IIntentReceiver receiver) {
final long origId = Binder.clearCallingIdentity();
try {
boolean doTrim = false;
synchronized(this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl != null) {
final BroadcastRecord r = rl.curBroadcast;
if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
final boolean doNext = r.queue.finishReceiverLocked(
r, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
r.queue.processNextBroadcast(false);
}
}
if (rl.app != null) {
rl.app.receivers.remove(rl);
}
//從HashMap中移除BroadcastReceiver
//從IntentResolver中移除對應的過濾器
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
}
// If we actually concluded any broadcasts, we might now be able
// to trim the recipients' apps from our working set
if (doTrim) {
trimApplications();
return;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void removeReceiverLocked(ReceiverList rl) {
mRegisteredReceivers.remove(rl.receiver.asBinder());
for (int i = rl.size() - 1; i >= 0; i--) {
mReceiverResolver.removeFilter(rl.get(i));
}
}
取消靜态注冊的廣播接收器
靜态注冊的廣播接收者理論上是無法取消注冊的,因為它們是寫死在Manifest.xml檔案中,并在應用安裝時就被記錄到PackageManagerService中的,并且PackageManagerService類并沒有提供移除Receiver資訊的接口。
那麼靜态注冊的廣播接收器就不能取消了麼?
當然不是,這裡我們可以采用迂回的方式達到目的。
在從PackageManagerService中查詢符合條件的廣播接收器清單時,IntentResolver會檢查每個過濾器對應的BroadcastReceiver是否被禁用了,如果被禁用了就會忽略掉這個BroadcastReceiver,進而達到和取消注冊同樣的效果。
下面是禁用或啟動某個元件的代碼:
//禁用xxx.class代表的元件,如果是廣播接收器,則不會再接收到廣播
ComponentName componentName = new ComponentName(this, xxx.class);
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
//啟用xxx.class代表的元件
ComponentName componentName = new ComponentName(this, xxx.class);
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
當我們通過PackageManagerService禁用某個元件時,PackageManagerService做了兩件事:
- 前面在靜态注冊流程中提高,從Manifest.xml解析出來的元件也會存儲到Setting中一份,辨別了元件的狀态。是以第一個要先修改Setting中的元件狀态。如下面代碼所示:
//PackageManagerService.java
private void setEnabledSetting(final String packageName, String className, int newState,
final int flags, int userId, String callingPackage) {
PackageSetting pkgSetting;
pkgSetting = mSettings.mPackages.get(packageName);
pkgSetting.setEnabled(newState, userId, callingPackage);
}
将某個元件的狀态改為禁用後,當再次向PackageManagerService查詢靜态注冊的元件時就會把禁用掉的元件過濾掉,不會傳回給ActivityManagerService。
//PackageManagerService.java
//ActivityIntentResolver類的方法
//根據ActivityIntentInfo建立一個ResolveInfo
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
//向Setting類查詢對應的元件是否被啟用了
if (!mSettings.isEnabledAndMatchLPr(info.activity.info, mFlags, userId)) {
return null;
}
}
- 第二件事會調用通過ActivityManagerService發送一個Intent.ACTION_PACKAGE_CHANGED類型的廣播,告知ActivityManagerService有個元件被禁用了,如果有正在排隊發送的廣播需要移除掉。
這個流程和普通應用發送廣播的流程是一樣的,差別是ActivityManagerService在檢查到此類型的廣播時,會自己處理掉,而不會真正的發送出去,為你Intent.ACTION_PACKAGE_CHANGED廣播屬于受保護的廣播,隻能系統代碼能發送。
ActivityManagerService的處理邏輯如下:
//ActivityManagerService.java
private void cleanupDisabledPackageComponentsLocked(
String packageName, int userId, boolean killProcess, String[] changedClasses) {
//1. 周遊禁用的元件,從PackageManagerService中查詢對應元件是否已經被禁用
//找出還沒有被禁用的元件,如果已經禁用了就不管了
try {
enabled = pm.getComponentEnabledSetting(
new ComponentName(packageName, changedClass),
(userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// As above, probably racing with uninstall.
return;
}
if (enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED
&& enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
if (disabledClasses == null) {
disabledClasses = new ArraySet<>(changedClasses.length);
}
//disabledClasses代表了實際将要被禁用的元件
disabledClasses.add(changedClass);
}
//周遊ActivityManagerService存儲的廣播接收器,檢視是否有需要禁用的
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
packageName, disabledClasses, userId, true);
}
}
通過以上兩個步驟就實作了禁用靜态注冊的廣播接收器的目的。
與LocalBroadcastReceiver差別
LocalBroadcastReceiver就比較簡單了,就是一個單例類+消息轉發。不需要與ActivityManagerService或PackageManagerService服務進行跨程序通信。
LocalBroadcastReceiver是線程安全的,注冊或發送廣播時都是用Synchronized關鍵字包裹起來。
擷取LocalBroadcastReceiver單例
@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;
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);
}
}
};
}
在構造LocalBroadcastReceiver執行個體時,會建立一個Handler,用于在主線程發送通知。
注冊廣播
private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
= new HashMap<>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
//使用ReceiverRecord記錄一個BroadcastReceiver和IntentFilter
//使用HashMap記錄一個BroadcastReceiver與多個ReceiverRecord關系
//因為對同一個BroadcastReceiver允許注冊多次
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
for (int i=0; i<filter.countActions(); i++) {
String action = filter.getAction(i);
//使用HashMap記錄每個Action和多個ReceiverRecord的關系
//便于發送廣播時查找對應的Receiver
ArrayList<ReceiverRecord> entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
}
entries.add(entry);
}
}
}
發送廣播
在主線程接收廣播
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
public boolean sendBroadcast(@NonNull Intent intent) {
...
//找出接受者,存儲到清單mPendingBroadcasts中,等待被發送
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
//向主線程發送一個消息,開始分發mPendingBroadcasts中的廣播
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
}
在目前調用線程接收廣播
public void sendBroadcastSync(@NonNull Intent intent) {
if (sendBroadcast(intent)) {
//找到符合條件的接受者後,立即在目前線程去執行分發邏輯,而不等待主線程分發
//也就是此方法會BroadcastReceiver的onReceive方法執行後,才會傳回
executePendingBroadcasts();
}
}