天天看點

BroadcastReceiver詳解BroadcastReceiver詳解

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合法性
  1. 包名、版本号
  2. 簽名(V2+V1)sudo
  3. 其他應用驗證package-verifier
  4. 應用申請的權限,需要使用者确認
  5. 拷貝安裝包到data/data/{包名}/目錄下,并且複制Native庫到lib目錄下
解析Apk檔案
  1. 添加apk檔案路徑到AssetManager管理中
  2. 解析Manifest.xml檔案資訊到Package類中,區分是多個apk檔案安裝,還是單個apk檔案安裝
  3. 驗證該安裝包是否隻用于測試
  4. 為Package類填充證書和簽名資訊
  5. 是否覆寫安裝,需要驗證簽名是否一緻、包配置是否一緻等。還要替換應用資源
//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做了兩件事:

  1. 前面在靜态注冊流程中提高,從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;
       }
}
           
  1. 第二件事會調用通過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();
   }
}