Android开发艺术探索:四大组件的工作过程之registerReceiver注册广播

BroadcastReceiver消息型组件,用于不同组件之间或应用之间传递消息。消息订阅不同于观察者模式,观察者模式耦合性太强,消息订阅模式中发布消息者完全不需要知道订阅者的存在,所以就需要第三方的组件来帮助存储转发,这个组件就是ActivityManagerService。

注册广播流程

注册广播的入口ContextImpl,对应的时序图

//两个重载的方法:

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext());
}

broadcastPermission广播者需要有相应的权限才接收(权限当然在Manifest声明),scheduler表示收到广播处理消息的Handler,默认是在主线程ActivityThread里执行的,所以不能有耗时的操作。

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

mPackageInfo是LoadedApk的类型,调用getReceiverDispatcher得到IItentReceiver,用于收到广播后服务端回调performReceive方法,并将注册广播的信息放到LoadedApk的mReceivers集合里,ReceiverDispatcher内部保存了BroadcastReceiver和InnerReceiver,当接收到广播时,ReceiverDispatcher可以方便地调用BroadcastReceiver#onReceive。

//一个Context对应一个注册的广播集合,重复注册只接收一次,不同的Context注册相同的广播每个注册的Context会收到广播
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
    = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

//registered为传递过来的true
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 (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.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

然后通过ActivityManagerNative.getDefault()获得ActivityManagerProxy向AMS发起IPC,调用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);
    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;
}

然后就执行到了AMS的registerReceiver方法:

//为了更快地查找,这里使用的就不是ArrayMap了
final HashMap<IBinder, ReceiverList> mRegisteredReceivers =
        new HashMap<IBinder, ReceiverList>();

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    //...
    int callingUid;
    int callingPid;
    synchronized(this) {
        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = getRecordForAppLocked(caller);
            if (callerApp == null) {
               //...throw exception
            }
            //如果调用进程没有运行
            if (callerApp.info.uid != Process.SYSTEM_UID &&
                    !callerApp.pkgList.containsKey(callerPackage) &&
                    !"android".equals(callerPackage)) {
               //...throw exception
            }
            callingUid = callerApp.info.uid;
            callingPid = callerApp.pid;
        } else {
            //...
        }

        userId = this.handleIncomingUser(callingPid, callingUid, userId,
                true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

        List allSticky = null;

        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 {
          //...
        }

        // The first sticky in the list is returned directly back to
        // the client.
        Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
        //直接返回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) {
            //...   
        }
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                permission, callingUid, userId);
        rl.add(bf);
        //..
        mReceiverResolver.addFilter(bf);

        // Enqueue broadcasts for all existing stickies that match
        // this filter.
        if (allSticky != null) {
           //...
        }

        return sticky;
    }
}

final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

private final List getStickiesLocked(String action, IntentFilter filter,
        List cur, int userId) {
    final ContentResolver resolver = mContext.getContentResolver();
    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
    if (stickies == null) {
        return cur;
    }
    final ArrayList<Intent> list = stickies.get(action);
    if (list == null) {
        return cur;
    }
    int N = list.size();
    for (int i=0; i<N; i++) {
        Intent intent = list.get(i);
        if (filter.match(resolver, intent, true, TAG) >= 0) {
            if (cur == null) {
                cur = new ArrayList<Intent>();
            }
            cur.add(intent);
        }
    }
    return cur;
}

当我们发送sticky广播的时候,AMS会把其保存在mStickyBroadcasts集合里,如果能在里面匹配到对应的action,就会把Intent寻找出来并返回,如果只想获取结果而不想注册广播可以将BroadcastReceiver传递为null。

mRegisteredReceivers用于保存IIntentReceiver与注册广播的对应关系,用于使用IIntentReceiver快速找到注册的广播ReceiverList(里面存储多个BroadcastFilter),例如反注册广播。所以一个广播可以接收到多个不同action的广播。

mReceiverResolver用于保存注册的所有广播(BroadcastFilter),用于发送广播快速找到注册的IIntentReceiver,进行分发。

反注册广播流程

入口同样是在ContextImpl

@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
    if (mPackageInfo != null) {
        IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
                getOuterContext(), receiver);
        try {
            ActivityManagerNative.getDefault().unregisterReceiver(rd);
        } catch (RemoteException e) {
        }
    } else {
        //...
    }
}

调用mPackageInfo#forgetReceiverDispatcher移除mReceiver里面注册过的广播。

//LoadedApk.java
public IIntentReceiver forgetReceiverDispatcher(Context context,
        BroadcastReceiver r) {
    synchronized (mReceivers) {
        //找到Context注册的广播集合
        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);
                }
                if (r.getDebugUnregister()) {
                   //...调试使用的
                }
                rd.mForgotten = true;
                return rd.getIIntentReceiver();
            }
        }

        //...

        if (context == null) {
            throw new IllegalStateException("Unbinding Receiver " + r
                    - " from Context that is no longer in use: " + context);
        } else {
            throw new IllegalArgumentException("Receiver not registered: " + r);
        }

    }
}

从mReceivers找到注册广播的集合(没找到就抛异常),移除已经注册的BroadcastRecevier,并返回对应的IIntentReceiver,用于AMS反注册。

接着通过ActivityManagerNative.getDefault()得到ActivityManagerProxy,向AMS发起IPC调用:

//ActivityManagerProxy
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(receiver.asBinder());
    mRemote.transact(UNREGISTER_RECEIVER_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}

进入到AMS的unregisterReceiver:

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) {
                if (rl.curBroadcast != null) {
                  //...
                }

                if (rl.app != null) {
                    rl.app.receivers.remove(rl);
                }
                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());
    int N = rl.size();
    for (int i=0; i<N; i++) {
        mReceiverResolver.removeFilter(rl.get(i));
    }
}

将注册的IIntentReceiver和BroadcastFilter(只移除对应IIntentReceiver注册的)移除。