Android BroadcastReceiver的注册

广播接收器的注册

广播接收器,是用来接收系统和应用发出的广播,常见的是开机广播,可以用于实现开机启动服务的功能,还有网络变化,电池电量变化等等均会发出相应的广播。Android系统中的广播设计的很好,对于开发者而言非常容易上手。

静态注册

不管该应用程序是否处于活动状态,都会进行监听,比如某个程序是监听内存的使用情况的,当在手机上安装好后,不管该应用程序是处于什么状态,都会执行该监听方法中的内容。

静态注册即在AndroidManifest中注册广播接收器
//TODO 解释各个属性的意义

<receiver android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver>

实例:

<!-- AndroidManifest.xml -->
<receiver  android:name=".MyReceiver" android:enabled="true" android:exported="true">
    <intent-filter>
        <action android:name="MLY" />
    </intent-filter>
</receiver>

动态注册

在代码中进行注册后,当应用程序关闭后,就不再进行监听,因此一般在Activity创建的时候注册,在Activity销毁的时候取消注册。

注册

IntentFilter filter = new IntentFilter("");
BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    }
};

registerReceiver(receiver, filter);

取消注册

unregisterReceiver(receiver);

静态广播接收器的注册流程

Android系统启动后,PackageManagerService会扫描各个APK的AndroidManifest.xml,并解析其中的Receiver标签,最后将Receiver信息保存在PackageManagerService的receivers中。AMS就可以调用PMS的queryIntentReceivers函数获取到ResolveInfo列表,一个ResolveInfo代表一个BroadcastReceiver,AMS就可以通过ResolveInfo启动未启动的BroadcastReceiver所在的进程,然后将广播分发给静态注册的BroadcastReceiver。

动态广播接收器注册流程

《Android BroadcastReceiver的注册》

从序列图上看,动态广播的注册流程还是很简单的,在Activity里调用registerReceiver实际是调用了ComtextImpl的registerReceiver函数,该函数会调用registerReceiverInternal函数

Step 1

ComtextImpl.registerReceiverInternal
该函数定义在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 {
             ......  
        }    
        try {
        //调用了ActivityManagerNative的registerReceiver函数
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) { 
            return null;
        }    
    }    

该函数主要是获取一个IIntentReceiver对象,最后AMS在分发广播的时候会通过这个对象回调到当前这个进程,一般情况下Receiver,mPackageInfo和context不为空,所以该对象是通过mPackageInfo.getReceiverDispatcher来获取的。同时scheduler指定了最后回调的函数运行的Handler,如果未指定,则是在主线程中。最后调用ActivityManagerNative的registerReceiver函数,该函数通过binder调用到AMS的registerReceiver。

Step 2

LoadedApk.getReceiverDispatcher函数
该函数定义在frameworks/base/core/java/android/app/LoadedApk.java函数中

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
    synchronized (mReceivers) {
        LoadedApk.ReceiverDispatcher rd = null;
        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
        if (registered) {
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }    
        }    
        //一个BroadcastReceiver可以注册多次,如果是多次注册,这里的rd不为空,返回的IIntentReceiver也是相同的
        if (rd == null) {
             rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
             if (registered) {
                 if (map == null) {
                     map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                     mReceivers.put(context, map);
                 }    
                 map.put(r, rd); 
             }    
        } else {
            rd.validate(context, handler);
        }    
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}
  1. registered参数表示是否是注册receiver函数调用。mReceivers是一个ArrayMap类型,key是Context,value也是ArrayMap类型,其中key是BroadcastReceiver,value是ReceiverDispatcher。
  2. 如果registered为true,函数首先会检查mReceivers是否已经注册了相同的BroadcastReceiver对象,如果是则直接返回该对象对应的ReceiverDispatcher中的IIntentReceiver。如果没有注册,则实例化一个ReceiverDispatcher对象,并将其加入到mReceivers中,然后其中的IIntentReceiver对象。
    从这里可以看到一个BroadcastReceiver是可以注册多次的,这里可以指定不同的IntentFilter,但是每个BroadcastReceiver只能指定一个Handler。
  3. 如果registered为false,则直接实例化一个ReceiverDispatcher并返回,这里并不会将ReceiverDispatcher对象保存在mReceivers里。

Step 3

ActivityManagerService.registerReceiver
该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

synchronized (this) {     
    ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
    //同一个BroadcastReceiver第一次注册时,rl为null
    if (rl == null) {
        rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);    
        mRegisteredReceivers.put(receiver.asBinder(), rl);
   } else if (rl.uid != callingUid) {
        throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid);
   } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid);
   } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId);
   }     
   BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
   rl.add(bf);
   mReceiverResolver.addFilter(bf);
}

这里只截取主要的代码
1. 如果一个BroadcastReceiver是第一次注册,则rl为空,会实例化一个ReceiverList,该类型为ArrayList,用于保存同一个BroadcastReceiver注册的多个IntentFilter,然后将实例化ReceiverList和IIntentReceiver的binder对象保存mRegisteredReceivers Map中。
2. 如果非第一次注册,则会检查pid,uid,userId是否一致,若不一致则抛出异常
3. 实例化一个BroadcastFilter,并加入ReceiverList,同时会保存在mReceiverResolver中

至此,动态广播的注册结束。AMS中mReceiverResolver里包含了BroadcastFilter,这个与IntentFilter一一对应,BroadcastFilter里包含了IntentFilter和IIntentReceiver。 AMS中mRegisteredReceivers中,Key是IIntentReceiver的binder对象,Value是一个列表,包含了该对象对应的多个BroadcastFilter(与IntentFilter一一对应)。

动态广播接收器取消注册流程

《Android BroadcastReceiver的注册》

Activity调用unregisterReceiver实际调用了ContextImpl的unregisterReceiver函数,该函数中调用LoadedApk的forgetReceiverDispatcher函数以及调用ActivityManagerNative的unregisterReceiver

Step 1

LoadedApk.forgetReceiverDispatcher
该函数定义在frameworks/base/core/java/android/app/LoadedApk.java函数中

public IIntentReceiver forgetReceiverDispatcher(Context context,
            BroadcastReceiver r) {
    synchronized (mReceivers) {
        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
        LoadedApk.ReceiverDispatcher rd = null;  
        if (map != null) {
            rd = map.get(r);
            if (rd != null) {
                map.remove(r);
                if (map.size() == 0) {                   
                    mReceivers.remove(context);              
                }
                rd.mForgotten = true;                    
                return rd.getIIntentReceiver();          
            }
        }
    }
}

该函数主要是将mReceivers中保存的BroadcastReceiver删除,并返回IIntentReceiver对象

Step 2

ActivityManagerService.unregisterReceiver
该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

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);             
            }
            removeReceiverLocked(rl);                
            if (rl.linkedToDeath) {                  
                rl.linkedToDeath = false;                
                rl.receiver.asBinder().unlinkToDeath(rl, 0);
            }
         }
      }
   } finally {
        Binder.restoreCallingIdentity(origId);
   }
}

该函数首先判断接收器当前是否正在接收广播,若正在接收广播,需要处理finishReceiverLocked处理。然后调用removeReceiverLocked函数移除IIntentReceiver

Step 3

ActivityManagerService.removeReceiverLocked
该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

void removeReceiverLocked(ReceiverList rl) {
    mRegisteredReceivers.remove(rl.receiver.asBinder());
    for (int i = rl.size() - 1; i >= 0; i--) {
        mReceiverResolver.removeFilter(rl.get(i));
    }
}

从前面动态广播的注册流程来看,当一个IIntentReceiver注册后,mRegisteredReceivers保存了IIntentReceiver对象及其所对应的BroadcastFilter列表。 mReceiverResolver会保存BroadcastFilter。因此这里取消注册时候,需要对应删除两个列表。

    原文作者:monkeyking430
    原文地址: https://blog.csdn.net/chenweiaiyanyan/article/details/76110921
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞