从源码层解析ContentService 如何实现数据变化监听

该博客仅因手机阅读不方便而创建,感兴趣同学可以微信搜索小专栏平台,订阅从源码角度看Android,支持作者原创

简介

ContentService 是 ContentResolver 的服务端,运行在 system_server 进程,

SystemServer中添加代码:

private static final String CONTENT_SERVICE_CLASS = "com.android.server.content.ContentService$Lifecycle"
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);

为所有应用提供访问 ContentProvider 数据接口。同时 ContentService 也提供监听数据变化的接口,这篇文章将从源码的角度去解析 registerContentObserver 和 unregisterContentObserver 的流程。再此之前,我先提供一个简短的接口使用实例。

使用实例

public class ContentManager {
    private static final Uri URI = Settings.System.getUriFor(Settings.System.EXAMPLE_URI);
    private MyObserver observer;

    class MyObserver extends ContentObserver {
        public MyObserver(Handler handler) {
            super(handler);
        }
    }

    @Override
    public void onChange(boolean selfChange) {
        this.onChange(selfChange,null);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        // 数据变化时会回调到这个接口
    }

    public void start(Context context, Handler handler) {
        observer = new MyObserver(handler);
        context.getContentResolver().registerContentObserver(URI, false, observer);
    }

    public void stop(Context context) {
        context.getContentResolver().unregisterContentObserver(observer);
    }
} 

通过调用start接口并传入Context与Handler即可开始监听数据变化,其中onChange是在Handler所在线程进行执行的。调用stop接口即可终止数据监听。

时序图

 注册:

registerContentObserver

ContextImpl.java

private final ApplicationContentResolver mContentResolver;

private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
…
    mContentResolver = new ApplicationContentResolver(this, mainThread, user);         
}

@Override
public ContentResolver getContentResolver() {
      // mContentResolver在ContextImpl初始化时被实例化,调用get接口时直接被返回。
     return mContentResolver;
}

private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        private final UserHandle mUser;
…       
}
ContextImpl是Context背后真正的办事者,许多操作诸如registerBroadcastReceiver, startService, startActivity都是通过它来进行操作的。至于ContextImpl是怎么被初始化则牵扯到进程的初始化流程,以后有机会再另写一篇来讲。

ContentResolver.java

public final void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendents,
            @NonNull ContentObserver observer) {
   // registerContentObserver方法首先会进行uri与observer的判空,如何这两个其中一个为空注册都不会成功。
      Preconditions.checkNotNull(uri, “uri”);
      Preconditions.checkNotNull(observer, “observer”);
       registerContentObserver(ContentProvider.getUriWithoutUserId(uri),
            notifyForDescendents,
           observer,
           ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
}

public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
            ContentObserver observer, int userHandle) {

...
mRootNode.addObserverLocked (uri,observer,notifyForDescendants,mRootNode,uid,pid,userHandle);
    try {
        getContentService().registerContentObserver(uri, notifyForDescendents,
                observer.getContentObserver(), userHandle);
    } catch (RemoteException e) {

    }
}

public static IContentService getContentService() {
    if (sContentService != null) {
        return sContentService;
    }
   // ContentService运行在system_server端,所以要准备binder通信了,通过ServiceManager获取IBinder来进行通信。
    // ServiceManager如何调用getService来获取ContentService其实也是个很复杂的流程,涉及到binder,其实网上的相关概括性的资料很多,以后有机会讲讲细节方面的东西。
    IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
    if (false) Log.v(“ContentService”, “default service binder = “ + b);
    sContentService = IContentService.Stub.asInterface(b);
    if (false) Log.v(“ContentService”, “default service = “ + sContentService);
    return sContentService;
}
ContentService.java

public void registerContentObserver(Uri uri, boolean notifyForDescendants,
        IContentObserver observer) {
    registerContentObserver(uri, notifyForDescendants, observer,
            UserHandle.getCallingUserId());
}

@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
        IContentObserver observer, int userHandle) {
    // 判空操作,observer对象与uri都是必需的
    if (observer == null || uri == null) {
        throw new IllegalArgumentException(“You must pass a valid uri and observer”);
    }

     // 获取应用端进程与线程id
    final int uid = Binder.getCallingUid();
    final int pid = Binder.getCallingPid();
    final int callingUserHandle = UserHandle.getCallingUserId();
    // 权限验证,有些uri如短信读取,需要在manifest注明权限的,用户允许后才能监听
    if (callingUserHandle != userHandle &&
            mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    != PackageManager.PERMISSION_GRANTED) {
        enforceCrossUserPermission(userHandle,
                “no permission to observe other users’ provider view”);
    }

    if (userHandle < 0) {
        if (userHandle == UserHandle.USER_CURRENT) {
            userHandle = ActivityManager.getCurrentUser();
        // 当UserHandle传入的不为USER_ALL时会抛出异常,默认的UserHandle是客户端的uid
        } else if (userHandle != UserHandle.USER_ALL) {
            throw new InvalidParameterException(“Bad user handle for registerContentObserver: “
                    + userHandle);
        }
    }

    synchronized (mRootNode) {
        // node确定可以被添加,使用初始化完成的root节点添加node
        // 这里用到了设计模式中的组合模式
        mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
                uid, pid, userHandle);
        if (false) Log.v(TAG, “Registered observer “ + observer + “ at “ + uri +
                “ with notifyForDescendants “ + notifyForDescendants);
    }
}
addObserverLocked

CountService.java

// userHandle要么是客户端uid要么是USER_ALL
public void addObserverLocked(Uri uri, IContentObserver observer,
        boolean notifyForDescendants, Object observersLock,
        int uid, int pid, int userHandle) {
    // index初始化为0
    addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
            uid, pid, userHandle);
}

private void addObserverLocked(Uri uri, int index, IContentObserver observer,
        boolean notifyForDescendants, Object observersLock,
        int uid, int pid, int userHandle) {
    // 如何这个node是子节点,则加入到树中
    // 如何不是,则继续向树的下一层搜索
    if (index == countUriSegments(uri)) {
        mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
                uid, pid, userHandle));
        return;
    }

    // 通过查看uri字符串确认是否有合法的子节点存在
    // 如果不存在会抛出异常终止注册
    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)) {
              // 如果匹配上了,则把当前节点添加到这个匹配上的节点当中
              // index自增1
            node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
                    observersLock, uid, pid, userHandle);
            return;
        }
    }

    // 如果目前已经存在的节点都不能和当前节点匹配上,则新建一个子节点,将observer添加到这个节点下
    // index自增1
    ObserverNode node = new ObserverNode(segment);
    mChildren.add(node);
    node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
            observersLock, uid, pid, userHandle);
}
private int countUriSegments(Uri uri) {
    if (uri == null) {
        return 0;
    }
    // 返回uri路径的分段个数
    // 如:content://sms/inbox,则这个方法会返回3,表示这个节点将会被放到第3层,mRootNode为第1层
    return uri.getPathSegments().size() + 1;
}

private String getUriSegment(Uri uri, int index) {
    if (uri != null) {
        if (index == 0) {
            return uri.getAuthority();
        } else {
            return uri.getPathSegments().get(index - 1);
        }
    } else {
        return null;
    }
}
StringUri.java

public List<String> getPathSegments() {
    return getPathPart().getPathSegments();
}

private PathPart getPathPart() {
    return path == null
            ? path = PathPart.fromEncoded(parsePath())
            : path;
}

private String parsePath() {
    String uriString = this.uriString;
    int ssi = findSchemeSeparator();

    // If the URI is absolute.
    if (ssi > -1) {
        // Is there anything after the ‘:’?
        boolean schemeOnly = ssi + 1 == uriString.length();
        if (schemeOnly) {
            // Opaque URI.
            return null;
        }

        // A ‘/‘ after the ‘:’ means this is hierarchical.
        if (uriString.charAt(ssi + 1) != ‘/‘) {
            // Opaque URI.
            return null;
        }
    } else {
        // All relative URIs are hierarchical.
    }

    return parsePath(uriString, ssi);
}

static String parsePath(String uriString, int ssi) {
    int length = uriString.length();

    // Find start of path.
    int pathStart;
    if (length > ssi + 2
            && uriString.charAt(ssi + 1) == ‘/‘
            && uriString.charAt(ssi + 2) == ‘/‘) {
        // Skip over authority to path.
        pathStart = ssi + 3;
        LOOP: while (pathStart < length) {
            switch (uriString.charAt(pathStart)) {
                case ‘?’: // Start of query
                case ‘#’: // Start of fragment
                    return “”; // Empty path.
                case ‘/‘: // Start of path!
                    break LOOP;
            }
            pathStart++;
        }
    } else {
        // Path starts immediately after scheme separator.
        pathStart = ssi + 1;
    }

    // Find end of path.
    int pathEnd = pathStart;
    LOOP: while (pathEnd < length) {
        switch (uriString.charAt(pathEnd)) {
            case ‘?’: // Start of query
            case ‘#’: // Start of fragment
                break LOOP;
        }
        pathEnd++;
    }

    return uriString.substring(pathStart, pathEnd);
}

总结

 

ContentService中使用了以mRootNode为root节点的树来维护已注册的ContentObserver, 属于设计模式中的组合模式

StringUri中的路径层级和组合模式中的树可以相互对应

ObserverNode维护了mChilderen和mObservers两个链表,mChildren代表了这个节点的子节点,mObservers代表了这个节点下的ContentObserver

notifyChange

 

这一节是这篇文章的核心章节,当客户端调用register接口后,相应的ContentObserver已经被存取到了ContentService维护的树中。当ContentProvider数据变化后,这时ContentService该怎么找到对应的Observer进行回调呢?

 

notifyChange

 

随便在源码里找来一个继承ContentProvider并实现了onUpdate接口的类:

 

LocalProvider.java

 

public class LocalProvider extends ContentProvider {

    @Override

    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {

        // 进行数据更新操作,省略掉

        …

 

        getContext().getContentResolver().notifyChange(url, null);

        return count;

    }

}

可以看见,当数据更新完毕后,会调用ContentResolver.notifyChange通知ContentObserver数据已经变化。

 

代码最终会调用到ContentService.notifyChange方法,中间获取ContentResovler实例的逻辑和registerContentObserver一样。

ContentService.java

public void notifyChange(Uri uri, IContentObserver observer,
        boolean observerWantsSelfNotifications, boolean syncToNetwork) {
    notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
            UserHandle.getCallingUserId());
}

    @Override
public void notifyChange(Uri uri, IContentObserver observer,
        boolean observerWantsSelfNotifications, boolean syncToNetwork,
        int userHandle) {
    // 省略一些检查权限的代码

    …

    long identityToken = clearCallingIdentity();
    try {
        ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
        synchronized (mRootNode) {
              // 传入新建的链表,用来存储符合规则的ContentObserver
            mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
                    userHandle, calls);
        }
        final int numCalls = calls.size();
        for (int i=0; i<numCalls; i++) {
            ObserverCall oc = calls.get(i);
            try {
                // 这里调用了binder IPC, 通知各个客户端数据已经变化
                oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, “Notified “ + oc.mObserver + “ of “ + “update at “ + uri);
                }
            } catch (RemoteException ex) {
                // 如果进程间通信失败了,很有可能是客户端的进程已经死亡
                // 也有可能客户端binder空间被占满无法通信
                synchronized (mRootNode) {
                    Log.w(TAG, “Found dead observer, removing”);
                    IBinder binder = oc.mObserver.asBinder();
                    final ArrayList<ObserverNode.ObserverEntry> list
                            = oc.mNode.mObservers;
                    int numList = list.size();
                    // 进行删除操作
                    for (int j=0; j<numList; j++) {
                        ObserverNode.ObserverEntry oe = list.get(j);
                        if (oe.observer.asBinder() == binder) {
                            list.remove(j);
                            j—;
                            numList—;
                        }
                    }
                }
            }
        }
        // 通知SyncManager启动对应账号的同步
        // 这里涉及到安卓自带的账号同步框架,了解的不多,有机会接触再深入了解吧
        if (syncToNetwork) {
            SyncManager syncManager = getSyncManager();
            if (syncManager != null) {
                syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
                        uri.getAuthority());
            }
        }
    } finally {
        restoreCallingIdentity(identityToken);
    }
}
collectObserversLocked

ObserverNode.java

public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
        boolean observerWantsSelfNotifications, int targetUserHandle,
        ArrayList<ObserverCall> calls) {
    String segment = null;
    // uri 层级
    int segmentCount = countUriSegments(uri);
    // 如果当前递归的层级已经到了底部,很有可能已经到叶节点
    if (index >= segmentCount) {
        collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
                targetUserHandle, calls);
    } else if (index < segmentCount){
        // 获取当前的segment名称
        segment = getUriSegment(uri, index);
        // notifyForDescendants如果为true,也是要进行通知的
        collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
                targetUserHandle, calls);
    }

    int N = mChildren.size();
    for (int i = 0; i < N; i++) {
        ObserverNode node = mChildren.get(i);
        if (segment == null || node.mName.equals(segment)) {
            // 进行下一级递归去搜寻
            node.collectObserversLocked(uri, index + 1,
                    observer, observerWantsSelfNotifications, targetUserHandle, calls);
            if (segment != null) {
                break;
            }
        }
}
}

private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
        boolean observerWantsSelfNotifications, int targetUserHandle,
        ArrayList<ObserverCall> calls) {
    int N = mObservers.size();
    IBinder observerBinder = observer == null ? null : observer.asBinder();
    for (int i = 0; i < N; i++) {
        ObserverEntry entry = mObservers.get(i);

        // 如果selfChange为true, 不在同一进程的change将不会被通知
        boolean selfChange = (entry.observer.asBinder() == observerBinder);
        if (selfChange && !observerWantsSelfNotifications) {
            continue;
        }

        // 进行匹配,如果匹配上了将会被加入到calls链表,之后会进行依次notify
        if (targetUserHandle == UserHandle.USER_ALL
                || entry.userHandle == UserHandle.USER_ALL
                || targetUserHandle == entry.userHandle) {
            // Make sure the observer is interested in the notification
            if (leaf || (!leaf && entry.notifyForDescendants)) {
                calls.add(new ObserverCall(this, entry.observer, selfChange));
            }
        }
    }
}
补充一点,既然ContentObserver的onChange方法能够被binder调用,为什么ContentObserver没有继承Binder Stub接口呢?

ContentObserver.java

public IContentObserver getContentObserver() {
    synchronized (mLock) {
        if (mTransport == null) {
            mTransport = new Transport(this);
        }
        return mTransport;
    }
}

private static final class Transport extends IContentObserver.Stub {
    private ContentObserver mContentObserver;

    public Transport(ContentObserver contentObserver) {
        mContentObserver = contentObserver;
    }

    @Override
    public void onChange(boolean selfChange, Uri uri, int userId) {
        ContentObserver contentObserver = mContentObserver;
        if (contentObserver != null) {
            contentObserver.dispatchChange(selfChange, uri, userId);
        }
    }

    public void releaseContentObserver() {
        mContentObserver = null;
    }
}
可以看见,ContentObserver中有getContentObserver接口在ContentResolver.registerContentObserver中会被调用,真正传输到服务端的对象是Transport,它的onChange方法会被binder call调用,最终调用dispatchChange接口。

private void dispatchChange(boolean selfChange, Uri uri, int userId) {
    if (mHandler == null) {
        // mHanlder为空将在主线程中直接执行onChange方法
        onChange(selfChange, uri, userId);
    } else {
        // 不为空则在Hanlder所在进程异步执行中执行
        mHandler.post(new NotificationRunnable(selfChange, uri, userId));
    }
}

private final class NotificationRunnable implements Runnable {
    private final boolean mSelfChange;
    private final Uri mUri;
    private final int mUserId;

    public NotificationRunnable(boolean selfChange, Uri uri, int userId) {
        mSelfChange = selfChange;
        mUri = uri;
        mUserId = userId;
    }

    @Override
    public void run() {
        ContentObserver.this.onChange(mSelfChange, mUri, mUserId);
    }
}

mHandler是在ContentObserver被实例化时传进来的,Handler的传入涉及到业务逻辑真正在那个线程执行,做应用的人应该格外关注。

 

总结

 

ContentProvider调用onUpdate更新数据完毕后将会调用notifyChange通知数据已经更新

ContentService先调用ObserverNode的收集接口,获取所有需要通知的observer

最后ContentService会使用binder call调用onChange接口

unregisterContentObserver

 

register和notifyChange这两块的逻辑很重要,unregister其实在实战中也很重要,因为如果不在合适的时候unregister,服务端就还会维护额外的ObserverNode,给系统带来不必要的开销。同时也会在客户端这边造成内存泄露,更严重的是,如果onChange在主线程更新了控件也会有几率抛出空指针异常导致应用FC.

ContentService.java

public void unregisterContentObserver(IContentObserver observer) {
    if (observer == null) {
        throw new IllegalArgumentException(“You must pass a valid observer”);
    }
    synchronized (mRootNode) {
        // 从root节点开始remove
        mRootNode.removeObserverLocked(observer);
        if (false) Log.v(TAG, “Unregistered observer “ + observer);
    }
}

public boolean removeObserverLocked(IContentObserver observer) {
    int size = mChildren.size();
    for (int i = 0; i < size; i++) {
        boolean empty = mChildren.get(i).removeObserverLocked(observer);
        // 当前节点的孩子如果全部被移出,则将当前节点在他的父节点链表中也移除掉
        if (empty) {
            mChildren.remove(i);
            i—;
            size—;
        }
    }

    IBinder observerBinder = observer.asBinder();
    size = mObservers.size();
    for (int i = 0; i < size; i++) {
        ObserverEntry entry = mObservers.get(i);
        // 进行匹配,值得注意的是,同节点下相同的observer一次只能移除一个
        if (entry.observer.asBinder() == observerBinder) {
            mObservers.remove(i);
            // observer binder不再需要监听死亡回掉了
            observerBinder.unlinkToDeath(entry, 0);
            break;
        }
    }

    // 当前节点的孩子已经全部被移除了
    if (mChildren.size() == 0 && mObservers.size() == 0) {
        return true;
    }
    return false;
}

类图:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值