Android广播机制——广播的注册

基于Android 7.0源码,分析Android广播机制的注册过程。

一、概述

  简单地说,Android广播机制的主要工作是为了实现一处发生事情,多处得到通知的效果,这种通知工作常常要牵涉跨进程通讯,实现广播的功能时,需要一方注册广播接收器,另外一方需要发送广播。由于注册广播接收器和发送广播的过程比较长,这里分成两篇文章讲解,本篇主要说说注册广播接收器的过程。

  注册广播接收器可以通过两种方式————静态注册、动态注册。

二、静态注册

  静态注册时,是通过在AndroidManifest.xml中配置

<receiver android:name="com.example.my_broadcast.MyBroadcastReceiver" >
  <intent-filter>
    <action android:name="com.android.broadcasttest.NEW_LIFEFROM" />
  </intent-filter>
</receiver>

  配置了以上信息之后,只要是com.android.broadcasttest.NEW_LIFEFROM这个地址的广播,MyBroadcastReceiver都能够接收的到。
  注意,这种方式的注册是常驻型的,也就是说当应用进程不存在时,如果有该粘性广播信息传来,MyBroadcastReceiver所在的应用进程会先被系统拉起,随后自动运行。
  另外,还有如下intent-filter

<intent-filter . . . >
  <category android:name="android.Intent.Category.DEFAULT" />
  <category android:name="android.Intent.Category.BROWSABLE" />
</intent-filter>
<intent-filter . . . >
  <data android:type="video/mpeg" android:scheme="http" . . . />
  <data android:type="audio/mpeg" android:scheme="http" . . . />
</intent-filter>

  它们的信息会在系统启动时,由PackageManagerService解析并记录下来。以后,当AMS调用PKMS的接口来查询“和intent匹配的组件”时,PKMS内部就会去查询当初记录下来的数据,并把结果返回AMS。有的同学认为静态receiver是常驻内存的,这种说法并不准确。因为常驻内存的只是静态receiver的描述性信息,并不是receiver实体本身。
  静态注册的具体过程需要比较熟悉包管理的代码,这里暂时略去,后面详细看过PackageManagerService的代码后,在回来补上。

三、动态注册

  动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service使用registerReceiver注册广播接收器:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler)

IntentFileter类成员:

private int mPriority;
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
private ArrayList<AuthorityEntry> mDataAuthorities = null;
private ArrayList<PatternMatcher> mDataPaths = null;
private ArrayList<String> mDataTypes = null;

  注意,动态注册方式与静态注册相反,不是常驻型的,也就是说广播接收器的生命周期会跟随程序的生命周期。
  我们以两个参数的registerReceiver接口为例,来分析一下动态注册的具体流程。
  registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,而ContextWrapper的具体实现者又是ContextImpl。

1. ContextImpl.registerReceiver

[===>frameworks\base\core\java\android\app\ContextImpl.java]

  @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());
  }

  传入的receiver参数一般是用户自定义并继承自BroadcastReceiver类。

2. ContextImpl.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 {
              if (scheduler == null) {
                  scheduler = mMainThread.getHandler();
              }
              rd = new LoadedApk.ReceiverDispatcher(
                      receiver, context, scheduler, null, true).getIIntentReceiver();
          }
      }
      try {
          final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                  mMainThread.getApplicationThread(), mBasePackageName,
                  rd, filter, broadcastPermission, userId);
          if (intent != null) {
              intent.setExtrasClassLoader(getClassLoader());
              intent.prepareToEnterProcess();
          }
          return intent;
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }

  从以上的代码可以看出,动态注册的BroadcastReceiver和ReceiverDispatcher存在着一一对应的关系。
  广播最终是通过AMS递送出去,AMS利用binder机制,将语义传递给各个应用进程,应用进程再辗转调用到receiver的onReceive()中以完成这次广播。而IIntentReceiver类型的rd对象正是承担“语义传递工作“的binder实体。
  ReceiverDispatcher类就可以看成是这类binder实体的管理者或者是承载者。

static final class ReceiverDispatcher {

    final static class InnerReceiver extends IIntentReceiver.Stub {
        final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
        final LoadedApk.ReceiverDispatcher mStrongRef;

        InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
            mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
            mStrongRef = strong ? rd : null;
        }

        @Override
        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            final LoadedApk.ReceiverDispatcher rd;
            if (intent == null) {
                Log.wtf(TAG, "Null intent received");
                rd = null;
            } else {
                rd = mDispatcher.get();
            }

            if (rd != null) {
                rd.performReceive(intent, resultCode, data, extras,
                        ordered, sticky, sendingUser);
            } else {
                IActivityManager mgr = ActivityManagerNative.getDefault();
                try {
                    if (extras != null) {
                        extras.setAllowFds(false);
                    }
                    mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    }

    final IIntentReceiver.Stub mIIntentReceiver; //注意,它就是传到外面的rd
    final BroadcastReceiver mReceiver;
    final Context mContext;
    final Handler mActivityThread;
    final Instrumentation mInstrumentation;

    ReceiverDispatcher(BroadcastReceiver receiver, Context context,
            Handler activityThread, Instrumentation instrumentation,
            boolean registered) {
        mIIntentReceiver = new InnerReceiver(this, !registered);
        mReceiver = receiver;
        mContext = context;
        mActivityThread = activityThread;
        mInstrumentation = instrumentation;

    }

    public void performReceive(Intent intent, int resultCode, String data,
            Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
        final Args args = new Args(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);

        if (intent == null || !mActivityThread.post(args)) {
            if (mRegistered && ordered) {
                IActivityManager mgr = ActivityManagerNative.getDefault();
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing sync broadcast to " + mReceiver);
                args.sendFinished(mgr);
            }
        }
    }
}

  这里以一张简单的不规范类图来说明。
类图
  另外,一个应用可以动态注册多个BroadcastReceiver,即应用和BroadcastReceiver是一个一对多的关系,反映这种关系的代码位于LoadedApk中,在LoadedApk中有一个成员变量mReceivers。应用进程里是使用LoadedApk来指代一个apk的,进程里有多少个apk,就有多少个LoadedApk。

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

  该表的key项是Context,也就是说可以是Activity、Service或Application。而value项则是另一张“子ArrayMap”。这是个“表中表”的形式。言下之意就是,每个Context(比如一个activity),是可以注册多个receiver的,这个很好理解。mReceivers里的“子ArrayMap”的key值为BroadcastReceiver,value项为ReceiverDispatcher,即反映了动态BroadcastReceiver和ReceiverDispatcher是一一对应。
新建 Microsoft Office Visio 绘图.png

  在ContextImpl.registerReceiverInternal的最后,会调用:

final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
        mMainThread.getApplicationThread(), mBasePackageName,
        rd, filter, broadcastPermission, userId);

  通过Binder机制会最终调用到ActivityManagerService.registerReceiver中。

3. ActivityManagerService.registerReceiver

[===>frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java]

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

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

        final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
                = new IntentResolver<BroadcastFilter, BroadcastFilter>() {
            ...
        };//发送广播时,获取动态注册广播接收器时使用

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

        public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
            ArrayList<Intent> stickyIntents = null;
            ProcessRecord callerApp = null;
            int callingUid;
            int callingPid;
            synchronized(this) {

                // Collect stickies of users
                int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
                while (actions.hasNext()) {
                    String action = actions.next();
                    for (int id : userIds) {
                        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                        if (stickies != null) {
                            ArrayList<Intent> intents = stickies.get(action);
                            if (intents != null) {
                                if (stickyIntents == null) {
                                    stickyIntents = new ArrayList<Intent>();
                                }
                                stickyIntents.addAll(intents);
                            }
                        }
                    }
                }
            }

            ArrayList<Intent> allSticky = null;
            if (stickyIntents != null) {
                final ContentResolver resolver = mContext.getContentResolver();
                // Look for any matching sticky broadcasts...
                for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                    Intent intent = stickyIntents.get(i);
                    if (filter.match(resolver, intent, true, TAG) >= 0) {
                        if (allSticky == null) {
                            allSticky = new ArrayList<Intent>();
                        }
                        allSticky.add(intent);
                    }
                }
            }

            // The first sticky in the list is returned directly back to the client.
            Intent sticky = allSticky != null ? allSticky.get(0) : null;
            if (receiver == null) {
                return sticky;
            }

            synchronized (this) {

                ReceiverList rl = 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);
                }

                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) {
                    ArrayList receivers = new ArrayList();
                    receivers.add(bf);

                    final int stickyCount = allSticky.size();
                    for (int i = 0; i < stickyCount; i++) {
                        Intent intent = allSticky.get(i);
                        BroadcastQueue queue = broadcastQueueForIntent(intent);
                        BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                                null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                                null, 0, null, null, false, true, true, -1);
                        queue.enqueueParallelBroadcastLocked(r);
                        queue.scheduleBroadcastsLocked();
                    }
                }

                return sticky;
            }
        }
}

  我们知道,对于同一个BroadcastReceiver对象,是可以注册多个IntentFilter的,这些IntentFilter信息会存放到AMS的mRegisteredReceivers表中。
在AMS端,可以通过以下代码访问相应的汇总表:

ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());

  其中的receiver参数为IIntentReceiver类型,正对应着ReceiverDispatcher中那个binder实体成员变量mIIntentReceiver。换句话说就是,每个客户端的ReceiverDispatcher,会对应AMS端的一个ReceiverList。

final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    ...
}
final class BroadcastFilter extends IntentFilter {
    // Back-pointer to the list this filter is in.
    final ReceiverList receiverList;
    ...
}

  ReceiverList继承自ArrayList,BroadcastFilter继承自IntentFilter,那么ReceiverList可以看成是IntentFilter数组列表。
  上面代码的大致工作主要是以下四个:
- 根据userId和intentfiler过滤后,得到相应的粘性广播列表;
- 根据userId、IIntentReceiver等信息创建ReceiverList,存放到AMS.mRegisteredReceivers中,key值为IIntentReceiver;
- 创建BroadcastFilter,将它分别添加到ReceiverList中,同时添加到AMS.mReceiverResolver中,mReceiverResolver在发送广播时获取动态注册广播接收器时使用;
- 最后,发送相应的粘性广播;

Registerreceivers.png

  结合LoadedApk和ActivityManagerService的代码和上面的小图,可以最后得到一张总体的图,如下:
广播总图.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值