Android BroadcastReceiver的注册

来源:互联网 发布:传奇霸业魔血符数据 编辑:程序博客网 时间:2024/05/21 10:42

广播接收器的注册

广播接收器,是用来接收系统和应用发出的广播,常见的是开机广播,可以用于实现开机启动服务的功能,还有网络变化,电池电量变化等等均会发出相应的广播。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。

动态广播接收器注册流程

这里写图片描述

从序列图上看,动态广播的注册流程还是很简单的,在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一一对应)。

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

这里写图片描述

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。因此这里取消注册时候,需要对应删除两个列表。

原创粉丝点击