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

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进程了。

@Override
public 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

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

参考:

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

你可能感兴趣的:(动态广播注册流程)