BroadcastReceiver探讨之动态广播注册流程

来源:互联网 发布:域名注册量查询 编辑:程序博客网 时间:2024/05/17 07:26

BroadcastReceiver

博文来源于

BroadcastReceiver探讨之动态广播注册流程

csdn:linyh1992

源码:

frameworks/base/+/android-4.4.1_r1/core/java/android/app/ContextImpl.java

frameworks/base/+/android-4.4.1_r1/core/java/android/app/ActivityManagerNative.java

frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/ActivityManagerService.java

frameworks/base/+/android-4.4.1_r1/core/java/android/app/LoadedApk.java

frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/ReceiverList.java

frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/BroadcastFilter.java

相关

在Android中,BroadcastReceiver是一套用来实现组件之间的通信机制,它是基于消息发布和订阅机制,可以用在不同线程之间组件的通信,也可以跨进程进行组件间通信。

在Android中,根据注册类型来看,BroadcastReceiver可以分为两种类型:静态广播和动态广播,其中静态广播中的广播中心是PMS(PackageManagerService),而动态广播的广播中心是AMS(ActivityManagerService),这边文章主要是分析动态广播的。

动态BroadcastReceiver的注册是借助Context接口中的registerReceiver方法来实现的。其中,继承Context接口的类有:Activity,Service,Application,所以在这3个类中,都可以动态注册广播。此外,Context接口的实现类,是ContextImpl类。

在讨论BroadcastReceiver前,需要稍微了解下Android不同进程之间的通信,以及一些Android系统级的Service。

Android跨进程之间的通信,主要有Binder实现的IPC机制,匿名共享内存,socket等等,而广播的跨进程通信是使用Binder来实现的,通过Binder将App进程切换到system_server进程和AMS通信。

在手机开机的时候,Android启动了很多系统级的进程,比如zygote进程,system_server进程,其中在system_server进程上,又启动了很多服务,
比如PowerManagerService(电源管理服务),ActivityManagerService(管理四大组件)

下面,主要是通过探讨广播,主要是分为两部分,

  • 用户进程和system_server进程,广播registerReceiver的流程

  • 用户进程和system_server进程,广播底层的数据存储结构

广播在用户进程中的注册流程

Context.java

public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter);

receiver: 广播接收组件,接收到相关的广播会调用BroadcastReceiver中的onReceive方法

filter:主要是用来过滤广播的,只有符合条件的广播才会接收.

registerReceiver方法最终是调用ContextImpl中的registerReceiverInternal方法

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 {        return ActivityManagerNative.getDefault().registerReceiver(                mMainThread.getApplicationThread(), mBasePackageName,                rd, filter, broadcastPermission, userId);    } catch (RemoteException e) {        return null;    }}

mPackageInfo:LoadedApk类型,保存了整个App内部的信息,比如包名,资源的路径,应用中所有启动的service,注册的reciver等等,用户进程(即一个应用进程)中,只会存在一个对象,所有ContextImpl中的mPackageInfo都指向共同的mPackageInfo对象,因此,mPackageInfo在整个用户进程中是唯一的。

registerReceiverInternal方法:

  • 1,获取Handler类型的scheduler对象,如果scheduler不为null,则scheduler为用户传递进来的Handler,否则为主线程中的Hanlder(这也是为什么BroadcastReceiver中的onReceive方法调用是处于主线程的原因),这个Handler对象主要是用来分发AMS传递过来的Intent,

  • 2,如果mPackageInfo存在,则通过mPackageInfo获取IIntentReceiver类型的rd对象,否则创建一个,rd同样也是Binder类型。然后,将rd传递给AMS,AMS处理相对应的广播,会通过这个Binder对象,跨进程回传到这个Context所处的进程中。

  • 3,通过ActivityManagerNative.getDefault()获取到ActivityManagerProxy对象,然后借助ActivityManagerProxy对象,和AMS通信,这时候进程由用户进程切换到system_server进程,最终调用的是AMS中的registerReceiver方法。

scheduler = mMainThread.getHandler();

mMainThread是ActivityThread对象,也可以说是主线程

这里有个疑惑,关于mPackageInfo,这个对象什么时候不存在?(以后再查看)按照我目前的理解,mPackageInfo最早是在Application创建的时候,就被初始化了,而context是当前的Activity,Service或者Application,所以符合条件(mPackageInfo != null && context != null)

rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler, mMainThread.getInstrumentation(), true);

LoadedApk

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);            }        }        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,如果广播已经注册过,则通过mReceivers对象获取key为当前Context的LoadedApk.ReceiverDispatcher类型的rd对象

  • 2,如果rd对象不存在,则创建,并且将rd添加到map中;否则,验证rd对象中的context和activitythread(validate这个方法有疑惑,什么场景下,rd中context和activitythread和目前的不一致)

  • 3,返回rd对象的中IIntentReceiver对象

广播在用户进程中的底层数据存储

上面说过,在用户进程中所有ContextImpl中的mPackageInfo都指向同一对象。

mPackageInfo中存在维持一个ArrayMap的mReceivers变量,mReceivers是ArrayMap嵌套ArrayMap的数据结构

  • key:为注册的Context(即Activity,Service或者Application),

  • value:同样也是个ArrayMap,

    • key:注册的BroadcastReceiver

    • value:ReceiverDispatcher对象,广播分发者,该对象内部保存注册者的Context,Handler,BroadcastReceiver,IIntentReceiver.Stub(和用户进程交互的)等等,
      发送广播时,AMS将会将Intent信息分发到BroadcastReceiver的onRecieve方法中。

mReceivers

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

LoadedApk.ReceiverDispatcher

final IIntentReceiver.Stub mIIntentReceiver;final BroadcastReceiver mReceiver;final Context mContext;final Handler mActivityThread;final Instrumentation mInstrumentation;final boolean mRegistered;final IntentReceiverLeaked mLocation;RuntimeException mUnregisterLocation;//....

广播在AMS中的注册流程

try {    // 跨进程和AMS通信,广播的注册实际上是在AMS中执行的    return ActivityManagerNative.getDefault().registerReceiver(            mMainThread.getApplicationThread(), mBasePackageName,            rd, filter, broadcastPermission, userId);} catch (RemoteException e) {    return null;}

ActivityManagerNative.getDefault()

public abstract class ActivityManagerNative extends Binder implements IActivityManager{    // ...省略    // 获取到唯一的    static public IActivityManager getDefault() {        return gDefault.get();    }    // ...省略    // Singleton是一个抽象类,Android实现的单例模式    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {        protected IActivityManager create() {            // 获取指向ActivityManagerService的IBinder对象            IBinder b = ServiceManager.getService("activity");            if (false) {                Log.v("ActivityManager", "default service binder = " + b);            }            // 创建ActivityManagerProxy对象,并将ActivityManagerService的Binder传递给ActivityManagerProxy            IActivityManager am = asInterface(b);            if (false) {                Log.v("ActivityManager", "default service = " + am);            }            return am;        }    };    // 创建一个ActivityManagerProxy对象    static public IActivityManager asInterface(IBinder obj) {        if (obj == null) {            return null;        }        IActivityManager in =            (IActivityManager)obj.queryLocalInterface(descriptor);        if (in != null) {            return in;        }        return new ActivityManagerProxy(obj);    }}

ActivityManagerNative.getDefault()第一次会创建一个AMP(ActivityManagerProxy)对象,并通过ServiceManager远程获取到一个指向AMS
的IBinder对象,并将这个IBinder引用传递给AMS对象,最后返回AMP对象,注意这个对象运行在用户进程中的,
ActivityManagerProxy中的IBinder引用是用来跨进程和AMS通信的。

ActivityManagerProxy.registerReceiver

public Intent registerReceiver(IApplicationThread caller, String packageName,        IIntentReceiver receiver,        IntentFilter filter, String perm, int userId) throws RemoteException{    Parcel data = Parcel.obtain();    Parcel reply = Parcel.obtain();    data.writeInterfaceToken(IActivityManager.descriptor);    data.writeStrongBinder(caller != null ? caller.asBinder() : null);    data.writeString(packageName);    data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);    filter.writeToParcel(data, 0);    data.writeString(perm);    data.writeInt(userId);    // 跨进程和AMS通信,注意Binder驱动会挂起当前用户线程    mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);    reply.readException();    Intent intent = null;    int haveIntent = reply.readInt();    if (haveIntent != 0) {        intent = Intent.CREATOR.createFromParcel(reply);    }    reply.recycle();    data.recycle();    return intent;}

很明显,这段代码是用来跨进程通信的。mRemote是一个IBinder对象,指向AMS的。

mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);

Binder机制非常复杂,这里不做讨论。我们只需要知道IBinder.transact方法,会调用到IBinder.onTransact方法。

题外话:

这里有个疑惑,mRemote.transact会不会阻塞线程呢?仔细思考了下,应该会的,准确的来说,Binder驱动会挂起当前用户进程。

因为registerReceiver返回Intent对象,如果当前用户线程没有挂起的话,那么后面的代码就没意义了,比如reply.readException肯定没效果。

mRemote这个IBinder对象是指向AMS的,而AMS继承了ActivityManagerNative类,并且AMS的onTransact方法是由ActivityManagerNative实现的。

ActivityManagerNative.onTransact,注意调用这个方法的时候,进程已经从用户进程切换到system_server进程了。

@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)        throws RemoteException {    switch (code) {        case REGISTER_RECEIVER_TRANSACTION:        {            data.enforceInterface(IActivityManager.descriptor);            IBinder b = data.readStrongBinder();            IApplicationThread app =                b != null ? ApplicationThreadNative.asInterface(b) : null;            String packageName = data.readString();            b = data.readStrongBinder();            IIntentReceiver rec                = b != null ? IIntentReceiver.Stub.asInterface(b) : null;            IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);            String perm = data.readString();            int userId = data.readInt();            // 调用AMS中的registerReceiver方法,这个方法是广播注册的最终实现            Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);            reply.writeNoException();            if (intent != null) {                reply.writeInt(1);                intent.writeToParcel(reply, 0);            } else {                reply.writeInt(0);            }            return true;        }    }

ActivityManagerService.registerReceiver

public Intent registerReceiver(IApplicationThread caller, String callerPackage,        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {    enforceNotIsolatedCaller("registerReceiver");    int callingUid;    int callingPid;    synchronized(this) {        ProcessRecord callerApp = null;        if (caller != null) {            callerApp = getRecordForAppLocked(caller);            if (callerApp == null) {                throw new SecurityException(                        "Unable to find app for caller " + caller                        + " (pid=" + Binder.getCallingPid()                        + ") when registering receiver " + receiver);            }            if (callerApp.info.uid != Process.SYSTEM_UID &&                    !callerApp.pkgList.containsKey(callerPackage) &&                    !"android".equals(callerPackage)) {                throw new SecurityException("Given caller package " + callerPackage                        + " is not running in process " + callerApp);            }            callingUid = callerApp.info.uid;            callingPid = callerApp.pid;        } else {            callerPackage = null;            callingUid = Binder.getCallingUid();            callingPid = Binder.getCallingPid();        }        userId = this.handleIncomingUser(callingPid, callingUid, userId,                true, true, "registerReceiver", callerPackage);        List allSticky = null;        // Look for any matching sticky broadcasts...        Iterator actions = filter.actionsIterator();        if (actions != null) {            while (actions.hasNext()) {                String action = (String)actions.next();                allSticky = getStickiesLocked(action, filter, allSticky,                        UserHandle.USER_ALL);                allSticky = getStickiesLocked(action, filter, allSticky,                        UserHandle.getUserId(callingUid));            }        } else {            allSticky = getStickiesLocked(null, filter, allSticky,                    UserHandle.USER_ALL);            allSticky = getStickiesLocked(null, filter, allSticky,                    UserHandle.getUserId(callingUid));        }        // The first sticky in the list is returned directly back to        // the client.        Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;        if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter                + ": " + sticky);        if (receiver == null) {            return sticky;        }        ReceiverList rl            = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());        if (rl == null) {            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);        } 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);        if (!bf.debugCheck()) {            Slog.w(TAG, "==> For Dynamic broadast");        }        mReceiverResolver.addFilter(bf);        // Enqueue broadcasts for all existing stickies that match        // this filter.        if (allSticky != null) {            ArrayList receivers = new ArrayList();            receivers.add(bf);            int N = allSticky.size();            for (int i=0; i<N; i++) {                Intent intent = (Intent)allSticky.get(i);                BroadcastQueue queue = broadcastQueueForIntent(intent);                BroadcastRecord r = new BroadcastRecord(queue, intent, null,                        null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,                        null, null, false, true, true, -1);                queue.enqueueParallelBroadcastLocked(r);                queue.scheduleBroadcastsLocked();            }        }        return sticky;    }}
  • 1,首先会获取应用的uid和pid,然后调用handleIncomingUser检测注册广播所需的权限,并获取userId

  • 2,检测所有符合条件的黏性事件,如果广播为null,则返回最近的一条黏性事件

  • 3,通过mRegisteredReceivers获取key为receiver(IBinder对象,从用户进程中传递进来的IIntentReceiver对象),获取相关的ReceiverList;
    如果ReceiverList为null,则创建,并添加到mRegisteredReceivers中

  • 4,创建BroadcastFilter(继承IntentFilter)对象,并添加进入到ReceiverList中

  • 5,处理符合条件的黏性事件(这里暂不探讨)

广播在AMS中的底层数据存储

ReceiverList:继承ArrayList,存储的是BroadcastFilter对象

BroadcastFilter:继承IntentFilter,主要是用来过滤广播的,广播接收器只能接收指定的广播类型

AMS中维持一个mRegisteredReceivers变量的HashMap

final HashMap<IBinder, ReceiverList> mRegisteredReceivers =            new HashMap<IBinder, ReceiverList>();

mRegisteredReceivers的数据结构是HashMap

  • key:IBinder,也就是ReceiverDispatcher中的IIntentReceiver对象,是用于AMS和用户进程通信的

  • value:ReceiverList,存储一系列的BroadcastFilter的List

mReceiverResolver:存储对应的BroadcastFilter

动态广播注册的底层数据存储

图1

参考:

Android应用程序注册广播接收器(registerReceiver)的过程分析
四大组件的工作过程
Android中Context详解 —- 你所不知道的Context
品茗论道说广播(Broadcast内部机制讲解)(上)
Android 内核–Context对象

0 0
原创粉丝点击