`
mixer_a
  • 浏览: 341853 次
社区版块
存档分类
最新评论

[原]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中,以便以后能够接收到相应的广播并进行处理,在下一篇文章,我们将详细分析这个过程,敬请关注。

作者:Luoshengyang 发表于2011-9-2 1:26:33 原文链接
阅读:7141 评论:14 查看评论
分享到:
评论

相关推荐

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

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

    Android AOSP 6.0.1 registerReceiver广播注册流程分析

    广播作为 Android 开发的四大组间之一,当我们发送广播以后,发生了什么?广播接收者最终如何收到了广播。 一、复盘广播的使用 在 Android 开发中使用广播分为三个步骤: 1.新建广播接收者 BroadcastReceiver; 2....

    android_broadcast测试代码

    下面通过2个简单的例子来学会怎样在andorid中使用广播信息,其实在文章Android开发历程_13(Service的使用)中已经使用过广播机制,即在servier下发送广播信息,主activity中接收该信息来更新UI。在那个程序中使用的是...

    新版Android开发教程.rar

    Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 ...

    《深入理解Android》卷Ⅱ

    6.4.4 应用进程处理广播分析 6.4.5 广播处理总结 6.5 startService之按图索骥 6.5.1 Service知识介绍 6.5.2 startService流程图 6.6 AMS中的进程管理 6.6.1 Linux进程管理介绍 6.6.2 关于Android中的进程...

    tabHost的简单使用以及registerReceiver和Handler的使用

    NULL 博文链接:https://wang-peng1.iteye.com/blog/562189

    Handler的简单使用

    简单的在一个Activity里实现一个简单的动画。Handler用来传值,在run方法里执行动态的显示过程。

    com.lampa.startapp:Phonegap插件,用于检查或启动Android设备中的其他应用程序

    科尔多瓦插件startapp 用于检查或启动其他应用程序的Phonegap插件,可在phonegap应用程序中获取其他功能。 最新版本6.1.6 添加支持Java 1.6 添加支持Java 1.7 添加完整的支持activityForResult,sendBroadcast和...

    android-BroadcastReceiverExample

    BroadcastReceiver 的例子,实现动态注册registerReceiver,和AndroidManifest.xml 静态注册

    BroadCastDemo

    广播的两种注册方式(静态(在AndroidManifest.xml注册)和动态(代码中通过registerReceiver()手工注册.当程序关闭时,该接收器也会随之销毁))

    Android 广播大全 Intent Action 事件详解

    //电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册 Intent.ACTION_BATTERY_LOW; //表示电池电量低 Intent.ACTION_BATTERY_OKAY; //表示电池电量充足,即从...

    Android输入手机号发送短信示例.rar

    Android输入手机号发送短信示例,EditText number框中的是电话号码,EditText body框中的是短信内容:  public void onCreate(Bundle savedInstanceState) {//重写的onCreate方法  super.onCreate...

    一种基于匹配的Android系统漏洞检测方法

    目前Android系统占领了智能移动操作系统的绝大多数份额,然而Android系统的安全问题却令人忧虑,近年来频频爆出高危漏洞,给广大Android用户带来了很大的安全隐患。对Android系统漏洞检测进行了研究,提出了一种基于...

    handler,registerReceiver以及activty和BroadcastReceiver的传值

    NULL 博文链接:https://wang-peng1.iteye.com/blog/632852

    Android 简单播放歌曲功能演示.rar

    Android 简单播放歌曲功能演示,播放本地音乐,非网络播放,可播放、暂停,运行效果如截图所示,在编写时,要注意以下代码:  int status = 1;//当前的状态,1没有声音播放 ,2 正在播放声音,3暂停  ImageButton ...

    Android判断和监听底座状态和类型的方法介绍

    广播的Action是ACTION_DOCK_EVENT,sticky Intent不需要注册真实的接收器 代码如下: IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT); Intent dockStatus = context.registerReceiver(null, ...

    Android BroadcastReceiver实现网络状态实时监听

    (1)动态注册:即在代码中使用registerReceiver()方法进行注册,动态注册需要在onPause或onDestory方法中反注册,不然会出现泄露。 (2)静态注册:在清单文件的application节点下通过标签声明,系统首次启动时会...

    Android代码-Wear-Emmet

    Emmet is an protocol based data-transfer for Android Wear Usage 1/ Declare your protocols into interfaces public interface WearProtocol{ public void sendMyObject(MyObject object); } 2/ Use Emmet to ...

    Android编程实现检测当前电源状态的方法

    本文实例讲述了Android编程实现检测当前电源状态的方法。分享给大家供大家参考,具体如下: 检测到现在在电源状态: IntentFilter mIntentFilter = new IntentFilter(); mIntentFilter.addAction(Intent.ACTION_...

    Android编程实现监控apk安装,卸载,替换的方法

    本文实例讲述了Android编程实现监控apk安装,卸载,替换的方法。分享给大家供大家参考,具体如下: public class GetBroadcast extends BroadcastReceiver { private static GetBroadcast mReceiver = new ...

Global site tag (gtag.js) - Google Analytics