ContentObserver内容观察者源码解析
一定要看看ContentObserver源码内部是如何实现观察者模式观察ContentProvider内容的。用它来观察我们感兴趣的ContentProvider是很有用的(见ContentProvider笔记)。
1、注册
先从ContentResolver注册ContentObserver的registerContentObserver()方法开始:
public final void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) { Objects.requireNonNull(uri, "uri"); Objects.requireNonNull(observer, "observer"); registerContentObserver( ContentProvider.getUriWithoutUserId(uri), notifyForDescendants, observer, ContentProvider.getUserIdFromUri(uri, mContext.getUserId())); }
被观察的Uri和观察者ContentObserver都不能为Null,经过这道检测。继续后面的调用流程:
1.1、生成UId
中间插入一步,继续后面的调用链之前,来看看这两个ContentProvider中的方法:getUriWithoutUserId(Uri)将传入的Uri简化,只保留authority部分:
public static Uri getUriWithoutUserId(Uri uri) { if (uri == null) return null; Uri.Builder builder = uri.buildUpon(); builder.authority(getAuthorityWithoutUserId(uri.getAuthority())); return builder.build(); }
getUserIdFromUri(Uri uri, int defaultUserId):从Uri中获取UserID,若无就使用默认的用户UID。
public static int getUserIdFromUri(Uri uri, int defaultUserId) { if (uri == null) return defaultUserId; return getUserIdFromAuthority(uri.getAuthority(), defaultUserId); } public static int getUserIdFromAuthority(String auth, int defaultUserId) { if (auth == null) return defaultUserId; int end = auth.lastIndexOf('@'); if (end == -1) return defaultUserId; String userIdString = auth.substring(0, end); try { return Integer.parseInt(userIdString); } catch (NumberFormatException e) { Log.w(TAG, "Error parsing userId.", e); return UserHandle.USER_NULL; } }
1.2、方法重载
调用另一个重载的注册方法registerContentObserver(Uri uri, boolean notifyForDescendents,ContentObserver observer,int userHandle):
/** @hide - designated user version */ @UnsupportedAppUsage public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, @UserIdInt int userHandle) { try { getContentService().registerContentObserver(uri, notifyForDescendents, observer.getContentObserver(), userHandle, mTargetSdkVersion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
看到这个方法里抛出了RemoteException异常,就应该知道方法执行到了和Service有关的部分。
1.3、获取系统内容服务
ContentResolve类中的getContentService()方法如下:
private static volatile IContentService sContentService; ... /** @hide */ public static final String CONTENT_SERVICE_NAME = "content"; /** @hide */ @UnsupportedAppUsage public static IContentService getContentService() { if (sContentService != null) { return sContentService; } IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME); sContentService = IContentService.Stub.asInterface(b); return sContentService; }
IContentService是AIDL接口,在框架framework侧,位于/frameworks/base/core/java/android/content目录下
interface IContentService { void unregisterContentObserver(IContentObserver observer); void registerContentObserver(in Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle, int targetSdkVersion); .... }
2、ContentService系统服务
而实现上面IContentServiceAIDL的接口就是Android系统中的ContentService内容服务。先来看看系统ContentService内容服务相关的源码。
public final class ContentService extends IContentService.Stub { static final boolean DEBUG = false; ... }
2.1、ContentService
从之前多次阅读Android源码以及熟悉Android源码目录结构一文中,我们可以知道系统服务位于/frameworks/base/services/core/java/com/android/server目录中,通过绑定的方式获取IBinder和framework应用层交互。
2.2、ContentService初始化
在SystemServer系统服务启动系统服务启动一文中,我们粗略的了解了系统服务的启动流程。其中最后一步SystemServer的main()方法真正开始初始化系统中的各项服务:
private void run() { ... // Start services. try { traceBeginAndSlog("StartServices"); startBootstrapServices(); startCoreServices(); startOtherServices(); SystemServerInitThreadPool.shutdown(); } ... // Loop forever. Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
其中的startOtherServices()方法加载其它不重要的服务,就包括ContentService内容服务:
private void startOtherServices() { ... // The AccountManager must come before the ContentService traceBeginAndSlog("StartAccountManagerService"); mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS); traceEnd(); traceBeginAndSlog("StartContentService"); mSystemServiceManager.startService(CONTENT_SERVICE_CLASS); traceEnd(); ... }
2.3、注册内容观察者
在1.3节中获取系统服务接口后,调用registerContentObserver()在系统服务中注册ContentObserver观察者。
①ContentObserver通过下面的方法转换成可传输状态,这样通过AIDL传给系统服务进行注册:
/** * Gets access to the binder transport object. Not for public consumption. * */ public IContentObserver getContentObserver() { synchronized (mLock) { if (mTransport == null) { mTransport = new Transport(this); } return mTransport; } }
②注册方法在ContentService内容服务中的实现如下:
@Override public void registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle, int targetSdkVersion) { if (observer == null || uri == null) { throw new IllegalArgumentException("You must pass a valid uri and observer"); } final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); userHandle = handleIncomingUser(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, true, userHandle); final String msg = LocalServices.getService(ActivityManagerInternal.class) .checkContentProviderAccess(uri.getAuthority(), userHandle); if (msg != null) { if (targetSdkVersion >= Build.VERSION_CODES.O) { throw new SecurityException(msg); } else { if (msg.startsWith("Failed to find provider")) { // Sigh, we need to quietly let apps targeting older API // levels notify on non-existent providers. } else { Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg); return; } } } synchronized (mRootNode) { mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, uid, pid, userHandle); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + " with notifyForDescendants " + notifyForDescendants); } }
③mRootNode保存了ContentService中注册的观察者
private final ObserverNode mRootNode = new ObserverNode("");
④添加观察者:这部分代码类似数据结构中的node节点添加,如无节点就创建根节点。
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)) { mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, uid, pid, userHandle, uri)); 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)) { 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); node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, observersLock, uid, pid, userHandle); }
2.4、反注册内容观察者
通过ContentResolve反注册观察者,层层调用最后也是在系统服务中调用unregisterContentObserver(IContentObserver observer)方法将注册的ContentObserver移除
@Override public void unregisterContentObserver(IContentObserver observer) { if (observer == null) { throw new IllegalArgumentException("You must pass a valid observer"); } synchronized (mRootNode) { mRootNode.removeObserverLocked(observer); if (false) Log.v(TAG, "Unregistered observer " + observer); } }
3、回调通知
前面注册观察者和反注册观察者都捋清了,那么回调在哪里?我们知道通过Uri观察的ContentProvider内容发生变化时会通知观察者,那么代码一定和ContentProvider的增删改有关系。
3.1、哪里回调了?
ContentProvider抽象类的增删改方法都是未实现的抽象方法,难不成在对应ContentResolve的增删改方法中回调了?就从ContentResolve的增删改方法入手,看看哪里回调通知了ContentService系统服务,再回调给我们注册的观察者。
期初思路就是上面那样,但是无果。本以为系统会自动回调数据变化告诉ContentObserver,实际上并没有。看源码的时候注意到一行注释:Implement this to handle requests to insert a new row. As a courtesy,call ContentResolver#notifyChange() after inserting. This method can be called from multiple threads.
通知回调的是我们自己,更确切的说是定义被观察的ContentProvider在数据变化时,回调ContentResolver的notifyChange()方法
@Override public Uri insert(@NonNull Uri uri, ContentValues values) { ... //插入成功,通知观察者 getContext().getContentResolver().notifyChange(uri, null); return uri; }
3.2、notifyChange
看看ContentResolver的notifyChange()方法,通知更新的Uri必须非空,而需要通知的观察者可以为NULL。盲猜这里如果通知的观察者不为NULL只通知这个观察者,而如果传入的观察者为NULL则通知所有注册的观察者。
public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer, @NotifyFlags int flags) { Objects.requireNonNull(uri, "uri"); notifyChange( ContentProvider.getUriWithoutUserId(uri), observer, flags, ContentProvider.getUserIdFromUri(uri, mContext.getUserId())); }
经过多次内部的调用链最后调用下面这个方法:
public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags, @UserIdInt int userHandle) { try { getContentService().notifyChange( uris, observer == null ? null : observer.getContentObserver(), observer != null && observer.deliverSelfNotifications(), flags, userHandle, mTargetSdkVersion, mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
3.3、ContentService服务通知
在ContentResolve中最后还是需要通过ContentService服务的notifyChange()方法
public void notifyChange(Uri[] uris, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int userId, int targetSdkVersion, String callingPackage) { ... final ArrayMap<Pair<String, Integer>, String> validatedProviders = new ArrayMap<>(); for (Uri uri : uris) { ... } final long token = clearCallingIdentity(); try { // Actually dispatch all the notifications we collected collector.dispatch(); } }
这里看的是API 30的源码,和之前有所不同。调用ContentService内部静态类ObserverCollector的dispatch()方法
public void dispatch() { for (int i = 0; i < collected.size(); i++) { ... final Runnable task = () -> { try { key.observer.onChangeEtc(key.selfChange, value.toArray(new Uri[value.size()]), key.flags, key.userId); } catch (RemoteException ignored) { } }; ... if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) { task.run(); } else { BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY); } } }
回调封装成Runnable任务,回调ContentObserver对应的AIDL接口IContentObserver方法
interface IContentObserver { /** * This method is called when an update occurs to the cursor that is being * observed. selfUpdate is true if the update was caused by a call to * commit on the cursor that is being observed. */ oneway void onChange(boolean selfUpdate, in Uri uri, int userId); }
3.4、回调ContentObserver
上面的IContentObserver AIDL接口实现类是ContentObserver内部的TranSport。前面注册ContentObserver给ContentService的时候也提到这个类,相当于将ContentObserver转换成可传输状态,共ContentService系统服务回调。
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) { // This is kept intact purely for apps using hidden APIs, to // redirect to the updated implementation onChangeEtc(selfChange, new Uri[] { uri }, 0, userId); } @Override public void onChangeEtc(boolean selfChange, Uri[] uris, int flags, int userId) { ContentObserver contentObserver = mContentObserver; if (contentObserver != null) { contentObserver.dispatchChange(selfChange, Arrays.asList(uris), flags, userId); } } public void releaseContentObserver() { mContentObserver = null; } }
在ContentService通知注册在ContentService中的观察者,在这里实现跨进程的回调IContentObserver 的onChangeEtc()方法:
/** @hide */ public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris, @NotifyFlags int flags, @UserIdInt int userId) { if (mHandler == null) { onChange(selfChange, uris, flags, userId); } else { mHandler.post(() -> { onChange(selfChange, uris, flags, userId); }); } }
这个类内部保存着外部ContentObserver观察者实例,最终再经过层层重载方法的回调->...->回调它的onChange()方法
public void onChange(boolean selfChange) { // Do nothing. Subclass should override. }
也就是我们自定义ContentObserver观察者中重写的onChange()回调方法。