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()回调方法。