本文基于AOSP-7.1.1-R9源码分析,源码可以参见 frameworks/base/+/android-7.1.1_r9;
从名字上看,ContentService是内容服务,和ContentProvider以及ContentRelsover相互关联。在使用ContentProvider创建共享的数据之后,其他进程为了访问数据,会调用ContentRelsover来进行相关操作。
在Android系统代码里面,经常会有如下代码所示操作。首先通过ContentResolver注册一个内容观察者,当VIBRATE_WHEN_RINGING_URI对应的Uri发生变化之后,就会回调内容观察者的onChange方法,通知回调的核心类都是ContentService。代码如下:
final ContentResolver cr = getContentResolver();
cr.registerContentObserver(VIBRATE_WHEN_RINGING_URI, false, this);
在Android系统中,可以自己添加不同类型的账号,比如Google、163、微信、QQ,最终在设置下面的账号会列出系统所有的账号,我们可以控制所有的账户都不进行同步,也可以单独控制每个账号知否进行同步。ContentService会响应所有的请求同步操作,而且也会记录所有账户的同步信息。
综上所述,ContentService的主要作用有两个:
- 管理系统内容观察者
- 控制系统同步机制
接下来我们将从以上两个方面对ContentService的框架展开分析,首先从服务启动开始。
启动ContentService
和其他的系统服务一样,ContentService也是在SystemServer里面进行启动。ActivityManagerService启动完成之后,在PHASE_ACTIVITY_MANAGER_READY阶段会启动ContentService服务。在ContentService$LifeCycle的onStart方法里面,会对ContentService进行初始化,等收到onBootPhase之后,会调用systemReady初始化SyncManager.
@ContenteService.java
public static class Lifecycle extends SystemService {
private ContentService mService;
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
final boolean factoryTest = (FactoryTest
.getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
mService = new ContentService(getContext(), factoryTest);
publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mService);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mService.systemReady();
}
}
内容观察者机制
内容观察者机制就是使用的java的观察者模式。首先通过registerContentObserver注册观察者,等观察者感兴趣的Uri发生变化之后,ContentService会通知观察者进行更新的动作,调用观察者的onChange方法。
注册观察者
如图1-1所示,假设我们现在监听的uri是content://com.android.email.provider/account,当我们执行registerContentObserver之后,会调用到ConentService的registerContentObserver方法。
@ContentService.java
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
IContentObserver observer, int userHandle) {
// ...
synchronized (mRootNode) {
mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
uid, pid, userHandle);
// ...
}
}
在registerContentObserver方法里面,会判断当前请求进程的权限和当前的用户,紧接着就会通过mRootNode.addObserverLocked方法,把当前的Uri等信息添加进一个树结构。
首先介绍两个类:
- ObserverNode: 用来存储节点的数据结构。Uri的结构里面除了scheme,每一个部分都是一个节点。对于content://com.android.email.provider/account来说,com.android.email.provider和account是两个子节点
- ObserverEntry: 用来保存完整的Uri对应的观察者的一些信息。
mRootNode是ObserverNode的一个实例,每个节点有两个很重要的成员变量mChildren和mObservers。:
- mChildren: 用来保存所有的子节点。
- mObservers: 用来存储调用registerContentObserver的进程注册的observer对象、以及进程的pid、uid、等信息。后续Uri对应的值发生变化之后回调onChange,正是通过从mObservers里面获取得正确对象。
先做一个简单介绍:
private String mName;
private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
接下来我们来分析一下,content://com.android.email.provider/account是如何添加进mChildren和mObservers里面去的。
@ContentService.java
private void addObserverLocked(Uri uri, int index, IContentObserver observer,
boolean notifyForDescendants, Object observersLock,
int uid, int pid, int userHandle) {
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
// [a] 添加ObServerEntry
mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
uid, pid, userHandle));
return;
}
// Look to see if the proper child already exists
String segment = getUriSegment(uri, index);
if (segment == null) {
throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
}
int N = mChildren.size();
for (int i = 0; i < N; i++) {
ObserverNode node = mChildren.get(i);
if (node.mName.equals(segment)) {
// [b] 添加child node 的ObServer
node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
observersLock, uid, pid, userHandle);
return;
}
}
// No child found, create one
ObserverNode node = new ObserverNode(segment);
mChildren.add(node);
// [c] 添加当前节点的Observer
node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
observersLock, uid, pid, userHandle);
}
第一次addObserverLocked,[a] 处,index是0,不相等。[b] getUriSegment返回的是第一个子节点是com.android.email.provider,默认情况下,mChildren里面不存在名字是com.android.email.provider的节点。[c],创建新的ObserverNode,并添加进mChildrens,继续添加后续的节点。
第二次addObserverLocked,[a] 处,index是1,不相等。[b] segment是account,名称是com.android.email.provider节点没有子节点,故直接跳过,重新创建名称是account的子节点,并继续添加后续的节点。
第三次addObserverLocked,[a] 处,index是2,相等。给名称是account的子节点添加一个ObserverEntry对象。
至此添加观察者信息的动作完成。
总结:addObserverNode方法是通过遍历Uri的segments来进行的,每遍历一个segments就会创建一个新的子节点,等所有的segments遍历完成之后,会创建一个记录观察者信息的ObserverEntry信息。
叶子节点才包含观察者。根节点是没有观察者的。
dump 观察者树
用adb shell dumpsys content
方法,可以查看到系统所有的注册信息。
仅仅查看account的观察者树,可以看到系统已经注册的相关信息。
Observer Tree:
com.android.provider/accout: pid=2186 uid10038 user=0 target=3ef76f0
settings/system/alarm_alert: pid=20935 uid=10036 user=0 target=6eb95e9
settings/global: pid=1472 uid=1000 user=0 target=532026e
…
通知观察者Uri发生变化
注册了观察之后,等到我们观察的Uri对应的值发生了变化之后,就需要通知观察者。在ContentProvider的子类里面,通常会调用getContext.getContentRelsover.notifyChange(notifyUri,observer)来触发通知。最终调用ConetentService的notifyChange方法来实现相关的逻辑。
@ContentService.java
@Override
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, int flags,
int userHandle) {
// ...
try {
ArrayList<ObserverCall> calls =