• Android应用程序注冊广播接收器(registerReceiver)的过程分析


            前面我们介绍了Android系统的广播机制,从本质来说,它是一种消息订阅/公布机制,因此,使用这样的消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息事实上就是注冊广播接收器,本文将探讨Android应用程序是怎样注冊广播接收器以及把广播接收器注冊到哪里去的。

            在Android的广播机制中,ActivityManagerService扮演着广播中心的角色,负责系统中全部广播的注冊和公布操作,因此,Android应用程序注冊广播接收器的过程就把是广播接收器注冊到ActivityManagerService的过程。Android应用程序是通过调用ContextWrapper类的registerReceiver函数来把广播接收器BroadcastReceiver注冊到ActivityManagerService中去的,而ContextWrapper类本身又借助ContextImpl类来注冊广播接收器。

            在Android应用程序框架中,Activity和Service类都继承了ContextWrapper类,因此,我们能够在Activity或者Service的子类中调用registerReceiver函数来注冊广播接收器。Activity、Service、ContextWrapper和ContextImpl这四个类的关系能够參考前面Android系统在新进程中启动自己定义服务过程(startService)的原理分析一文中描写叙述的Activity类图。

            这篇文章还是继续以实例来进行情景分析,所用到的样例便是上一篇文章Android系统中的广播(Broadcast)机制简要介绍和学习计划里面介绍的应用程序了,所以希望读者在继续阅读本文之前,先看看这篇文章;又因为Android应用程序是把广播接器注冊到ActivityManagerService中去的,因此,这里又会涉入到Binder进程间通信机制,所以希望读者对Android系统的Binder进程间通信机制有所了解,具体请參考Android进程间通信(IPC)机制Binder简要介绍和学习计划一文。

            開始进入主题了,在Android系统中的广播(Broadcast)机制简要介绍和学习计划一文所介绍的样例中,注冊广播接收器的操作是MainActivity发起的,我们先来看看注冊过程的序列图:


            在分析这个序列图之前,我们先来看一下MainActivity是怎样调用registerReceiver函数来注冊广播接收器的:

    public class MainActivity extends Activity implements OnClickListener {  
    	......
    
    	@Override   
    	public void onResume() {  
    		super.onResume();  
    
    		IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION);  
    		registerReceiver(counterActionReceiver, counterActionFilter);  
    	} 
    
    	......
    
    }
            MainActivity在onResume函数里,通过其父类ContextWrapper的registerReceiver函数注冊了一个BroadcastReceiver实例counterActionReceiver,而且通过IntentFilter实例counterActionFilter告诉ActivityManagerService,它要订阅的广播是CounterService.BROADCAST_COUNTER_ACTION类型的,这样,ActivityManagerService在收到CounterService.BROADCAST_COUNTER_ACTION类型的广播时,就会分发给counterActionReceiver实例的onReceive函数。

            接下来,就開始分析注冊过程中的每个步骤了。

            Step 1. ContextWrapper.registerReceiver

            这个函数实如今frameworks/base/core/java/android/content/ContextWrapper.java文件里:

    public class ContextWrapper extends Context {
    	Context mBase;
    	......
    
    	@Override
    	public Intent registerReceiver(
    		BroadcastReceiver receiver, IntentFilter filter) {
    		return mBase.registerReceiver(receiver, filter);
    	}
    
    	......
    
    }
            这里的成员变量mBase是一个ContextImpl实例,想知道为什么,能够回过头去看看Android应用程序启动过程源码分析这篇文章>~<。

            Step 2. ContextImpl.registerReceiver

            这个函数实如今frameworks/base/core/java/android/app/ContextImpl.java文件里:

    class ContextImpl extends Context {
    	......
    
    	@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, filter, broadcastPermission,
    			scheduler, getOuterContext());
    	}
    
    	private Intent registerReceiverInternal(BroadcastReceiver receiver,
    			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(),
    					rd, filter, broadcastPermission);
    		} catch (RemoteException e) {
    				return null;
    		}
    	}
    
    	......
    
    }
            通过两个函数的中转,终于就进入到ContextImpl.registerReceiverInternal这个函数来了。这里的成员变量mPackageInfo是一个LoadedApk实例,它是用来负责处理广播的接收的,在后面一篇文章讲到广播的发送时(sendBroadcast),会具体描写叙述。參数broadcastPermission和scheduler都为null,而參数context是上面的函数通过调用函数getOuterContext得到的,这里它就是指向MainActivity了,因为MainActivity是继承于Context类的,因此,这里用Context类型来引用。

            因为条件mPackageInfo != null和context != null都成立,而且条件scheduler == null也成立,于是就调用mMainThread.getHandler来获得一个Handler了,这个Hanlder是后面用来分发ActivityManagerService发送过的广播用的。这里的成员变量mMainThread是一个ActivityThread实例,在前面Android应用程序启动过程源码分析这篇文章也描写叙述过了。我们先来看看ActivityThread.getHandler函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

            Step 3. ActivityThread.getHandler

            这个函数实如今frameworks/base/core/java/android/app/ActivityThread.java文件里:

    public final class ActivityThread {
    	......
    
    	final H mH = new H();
    
    	private final class H extends Handler {
    		......
    
    		public void handleMessage(Message msg) {
    			......
    
    			switch (msg.what) {
    			......
    			}
    
    			......
    		}
    
    		......
    
    	}
    
    	......
    
    	final Handler getHandler() {
    		return mH;
    	}
    
    	......
    
    }
            有了这个Handler之后,就能够分发消息给应用程序处理了。

            再回到上一步的ContextImpl.registerReceiverInternal函数中,它通过mPackageInfo.getReceiverDispatcher函数获得一个IIntentReceiver接口对象rd,这是一个Binder对象,接下来会把它传给ActivityManagerService,ActivityManagerService在收到对应的广播时,就是通过这个Binder对象来通知MainActivity来接收的。

            我们也是先来看一下mPackageInfo.getReceiverDispatcher函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

            Step 4. LoadedApk.getReceiverDispatcher

            这个函数实如今frameworks/base/core/java/android/app/LoadedApk.java文件里:

    final class LoadedApk {
    	......
    
    	public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
    			Context context, Handler handler,
    			Instrumentation instrumentation, boolean registered) {
    		synchronized (mReceivers) {
    			LoadedApk.ReceiverDispatcher rd = null;
    			HashMap<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 HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
    						mReceivers.put(context, map);
    					}
    					map.put(r, rd);
    				}
    			} else {
    				rd.validate(context, handler);
    			}
    			return rd.getIIntentReceiver();
    		}
    	}
    
    	......
    
    	static final class ReceiverDispatcher {
    
    		final static class InnerReceiver extends IIntentReceiver.Stub {
    			final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
    			......
    
    			InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
    				mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
    				......
    			}
    
    			......
    		}
    
    		......
    
    		final IIntentReceiver.Stub mIIntentReceiver;
    		final Handler mActivityThread;
    
    		......
    
    		ReceiverDispatcher(BroadcastReceiver receiver, Context context,
    				Handler activityThread, Instrumentation instrumentation,
    				boolean registered) {
    			......
    
    			mIIntentReceiver = new InnerReceiver(this, !registered);
    			mActivityThread = activityThread;
    			
    			......
    		}
    
    		......
    
    		IIntentReceiver getIIntentReceiver() {
    			return mIIntentReceiver;
    		}
    
    	}
    
    	......
    
    }

            在LoadedApk.getReceiverDispatcher函数中,首先看一下參数r是不是已经有对应的ReceiverDispatcher存在了,如果有,就直接返回了,否则就新建一个ReceiverDispatcher,而且以r为Key值保在一个HashMap中,而这个HashMap以Context,这里即为MainActivity为Key值保存在LoadedApk的成员变量mReceivers中,这样,仅仅要给定一个Activity和BroadcastReceiver,就能够查看LoadedApk里面是否已经存在对应的广播接收公布器ReceiverDispatcher了。

            在新建广播接收公布器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,能够通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的參数activityThread保存下来,以便后面在分发广播的时候使用。

            如今,再回到ContextImpl.registerReceiverInternal函数,在获得了IIntentReceiver类型的Binder对象后,就開始要把它注冊到ActivityManagerService中去了。

            Step 5. ActivityManagerProxy.registerReceiver

            这个函数实如今frameworks/base/core/java/android/app/ActivityManagerNative.java文件里:

    class ActivityManagerProxy implements IActivityManager
    {
    	......
    
    	public Intent registerReceiver(IApplicationThread caller,
    			IIntentReceiver receiver,
    			IntentFilter filter, String perm) throws RemoteException
    	{
    		Parcel data = Parcel.obtain();
    		Parcel reply = Parcel.obtain();
    		data.writeInterfaceToken(IActivityManager.descriptor);
    		data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    		data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
    		filter.writeToParcel(data, 0);
    		data.writeString(perm);
    		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;
    	}
    
    	......
    
    }
    
             这个函数通过Binder驱动程序就进入到ActivityManagerService中的registerReceiver函数中去了。

             Step 6. ActivityManagerService.registerReceiver

             这个函数实如今frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件里:

    public final class ActivityManagerService extends ActivityManagerNative
    		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    	......
    
    	public Intent registerReceiver(IApplicationThread caller,
    			IIntentReceiver receiver, IntentFilter filter, String permission) {
    		synchronized(this) {
    			ProcessRecord callerApp = null;
    			if (caller != null) {
    				callerApp = getRecordForAppLocked(caller);
    				if (callerApp == null) {
    					......
    				}
    			}
    
    			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);
    				}
    			} else {
    				......
    			}
    
    			// The first sticky in the list is returned directly back to
    			// the client.
    			Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
    
    			......
    
    			if (receiver == null) {
    				return sticky;
    			}
    
    			ReceiverList rl
    				= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
    			if (rl == null) {
    				rl = new ReceiverList(this, callerApp,
    					Binder.getCallingPid(),
    					Binder.getCallingUid(), receiver);
    
    				if (rl.app != null) {
    					rl.app.receivers.add(rl);
    				} else {
    					......
    				}
    				mRegisteredReceivers.put(receiver.asBinder(), rl);
    			}
    
    			BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
    			rl.add(bf);
    			......
    			mReceiverResolver.addFilter(bf);
    
    			// Enqueue broadcasts for all existing stickies that match
    			// this filter.
    			if (allSticky != null) {
    				......
    			}
    
    			return sticky;
    		}
    	}
    
    	......
    
    }
             函数首先是获得调用registerReceiver函数的应用程序进程记录块:

        ProcessRecord callerApp = null;
        if (caller != null) {
    	callerApp = getRecordForAppLocked(caller);
    	if (callerApp == null) {
    	    ......
            }
        }
            这里得到的便是上一篇文章Android系统中的广播(Broadcast)机制简要介绍和学习计划里面介绍的应用程序Broadcast的进程记录块了,MainActivity就是在里面启动起来的。

            

        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);
    	}
        } else {
    	......
        }
    
        // The first sticky in the list is returned directly back to
        // the client.
        Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
            这里传进来的filter仅仅有一个action,就是前面描写叙述的CounterService.BROADCAST_COUNTER_ACTION了,这里先通过getStickiesLocked函数查找一下有没有对应的sticky intent列表存在。什么是Sticky Intent呢?我们在最后一次调用sendStickyBroadcast函数来发送某个Action类型的广播时,系统会把代表这个广播的Intent保存下来,这样,后来调用registerReceiver来注冊同样Action类型的广播接收器,就会得到这个最后发出的广播。这就是为什么叫做Sticky Intent了,这个最后发出的广播尽管被处理完了,可是仍然被粘住在ActivityManagerService中,以便下一个注冊对应Action类型的广播接收器还能继承处理。

            这里,如果我们不使用sendStickyBroadcast来发送CounterService.BROADCAST_COUNTER_ACTION类型的广播,于是,这里得到的allSticky和sticky都为null了。

            继续往下看,这里传进来的receiver不为null,于是,继续往下运行:

        ReceiverList rl
    	= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
    	rl = new ReceiverList(this, callerApp,
    		Binder.getCallingPid(),
    		Binder.getCallingUid(), receiver);
    
    	if (rl.app != null) {
    		rl.app.receivers.add(rl);
    	} else {
    		......
    	}
    	mRegisteredReceivers.put(receiver.asBinder(), rl);
        }
            这里事实上就是把广播接收器receiver保存一个ReceiverList列表中,这个列表的宿主进程是rl.app,这里就是MainActivity所在的进程了,在ActivityManagerService中,用一个进程记录块来表示这个应用程序进程,它里面有一个列表receivers,专门用来保存这个进程注冊的广播接收器。接着,又把这个ReceiverList列表以receiver为Key值保存在ActivityManagerService的成员变量mRegisteredReceivers中,这些都是为了方便在收到广播时,高速找到对应的广播接收器的。

            再往下看:

        BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
        rl.add(bf);
        ......
        mReceiverResolver.addFilter(bf);
            上面仅仅是把广播接收器receiver保存起来了,可是还没有把它和filter关联起来,这里就创建一个BroadcastFilter来把广播接收器列表rl和filter关联起来,然后保存在ActivityManagerService中的成员变量mReceiverResolver中去。

            这样,广播接收器注冊的过程就介绍完了,比較简单,可是工作又比較琐碎,主要就是将广播接收器receiver及其要接收的广播类型filter保存在ActivityManagerService中,以便以后能够接收到对应的广播并进行处理,在下一篇文章,我们将具体分析这个过程,敬请关注。

    老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

  • 相关阅读:
    【Javascript】javascript学习 二十二 JavaScript 对象简介
    【Javascript】javascript学习 二十六 JavaScript Boolean(逻辑)对象
    【Javascript】javascript学习 二十九 JavaScript HTML DOM 对象
    【Javascript】javascript学习 二十八 JavaScript RegExp 对象
    【Javascript】javascript学习 二十一 JavaScript 指导方针
    【Javascript】javascript学习 二十三 JavaScript 字符串(String)对象
    【Javascript】javascript学习 三十 JavaScript 浏览器检测
    【Javascript】javascript学习 二十五 JavaScript Array(数组)对象
    【Javascript】javascript学习 二十四 JavaScript Date(日期)对象
    【Javascript】javascript学习 二十七 JavaScript Math(算数)对象
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4069814.html
Copyright © 2020-2023  润新知