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

简介

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

使用实例

  • 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) {
  • 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

```java
public List 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)) {
top Created with Sketch.