Android 系统服务之 ContentService

本文基于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 = 
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值