ContentObserver注册和触发流程分析

本文分析了ContentObserver在Android中的注册和触发流程,从获取ContentResolver开始,详细讲解了注册阶段,数据修改后的notifyChange过程,以及SettingsProvider的源码分析,揭示了不同进程中对同一Setting的修改如何触发Observer。

ContentObserver注册和触发流程

获取ContentResolver

mContext.getContentResolver()

Context是一个抽象类,他的实现类之一是ContextImpl,分析ContextImpl源码可以看到:

//android.app.ContextImpl
public ContentResolver getContentResolver() {
        return mContentResolver;
    }

mContentResolver声明如下:

private final ApplicationContentResolver mContentResolver;

注册阶段

mContext.getContentResolver().registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer)

通过context获取到ContentResolver后,我们知道他是一个ApplicationContentResolver的实例对象,分析ApplicationContentResolver源码,并没有找到registerContentObserver方法,进入他的父类ContentResolver

registerContentObserver的实现如下:

//android.content.ContentResolver
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();
        }
    }

getContentService返回的是ContentService的Binder对象,分析ContentService源码:

//com.android.server.content.ContentService
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
            IContentObserver observer, int userHandle, int targetSdkVersion) {
    
        //权限检查等操作,忽略...

        synchronized (mRootNode) {
            mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
                    uid, pid, userHandle);
            if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
                    " with notifyForDescendants " + notifyForDescendants);
        }
    }

mRootNodeObserverNode的实例,ObserverNodeContentService的静态内部类。

ObserverNode内部有ArrayList<ObserverNode>,ArrayList<ObserverEntry>。对于Observer是用树来存储的,可以看作左节点存储的是Children,右节点存储Observer。

举个栗子:content://authority/path1/path2 , authority是path1父节点,path1是path2父节点,即path1左节点存储的path2节点,右节点存储的自己的observer。

//ContentService$ObserverNode
//就是在Observer树上插入节点。用递归实现的(╯°□°)╯︵ ┻━┻
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);
        }

( •̀ ω •́ )y注册过程终于说完了。

数据修改

当在自定义ContentProvider里修改数据库后,需要显示调用ContentResolver.notifyChange以达到通知Observer的目的,否则该URI上注册的Observer将不会收到数据改变的通知。

notifyChange阶段

ContentResolver.notifyChange方法最终会进入ContentService.notifyChange,由ContentService分发change事件。

//com.android.server.content.ContentService$ObserverNode
public void notifyChange(Uri[] uris, IContentObserver observer,
            boolean observerWantsSelfNotifications, int flags, int userId,
            int targetSdkVersion, String callingPackage) {
        	//...忽略亿点点
            //收集所有需要通知的observer
            synchronized (mRootNode) {
                final int segmentCount = ObserverNode.countUriSegments(uri);
                mRootNode.collectObserversLocked(uri, segmentCount, 0, observer,
                        observerWantsSelfNotifications, flags, resolvedUserId, collector);
            }
        }

        final long token = clearCallingIdentity();
        try {
            // 通知所有Observer
            collector.dispatch();
            //...忽略亿点点
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
//com.android.server.content.ContentService$ObserverNode
//Observer树上查找所有需要通知的Observer,结果存储在collector里。又是递归(╯°□°)╯︵ ┻━┻
public void collectObserversLocked(Uri uri, int segmentCount, int index,
                IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
                int targetUserHandle, ObserverCollector collector) {
            String segment = null;
            if (index >= segmentCount) {
                // This is the leaf node, notify all observers
                if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
                collectMyObserversLocked(uri, true, observer, observerWantsSelfNotifications,
                        flags, targetUserHandle, collector);
            } else if (index < segmentCount){
                segment = getUriSegment(uri, index);
                if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
                        + segment);
                // Notify any observers at this level who are interested in descendants
                collectMyObserversLocked(uri, false, observer, observerWantsSelfNotifications,
                        flags, targetUserHandle, collector);
            }

            int N = mChildren.size();
            for (int i = 0; i < N; i++) {
                ObserverNode node = mChildren.get(i);
                if (segment == null || node.mName.equals(segment)) {
                    // We found the child,
                    node.collectObserversLocked(uri, segmentCount, index + 1, observer,
                            observerWantsSelfNotifications, flags, targetUserHandle, collector);
                    if (segment != null) {
                        break;
                    }
                }
            }
        }

大概写了下ContentObserver注册和触发的时机,中间省略了很多底层的东西,我也不是很懂。

SettingsProvider分析

这里分析下SettingsProvider的源码,分析Settings.Global.putString写入自定义字段的过程。

直接上代码:

//android.provider.Settings.Global
public boolean putStringForUser(ContentResolver cr, String name, String value,
                String tag, boolean makeDefault, final int userHandle,
                boolean overrideableByRestore) {
            try {
                Bundle arg = new Bundle();
                arg.putString(Settings.NameValueTable.VALUE, value);
                arg.putInt(CALL_METHOD_USER_KEY, userHandle);
                if (tag != null) {
                    arg.putString(CALL_METHOD_TAG_KEY, tag);
                }
                if (makeDefault) {
                    arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                }
                if (overrideableByRestore) {
                    arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
                }
                IContentProvider cp = mProviderHolder.getProvider(cr);
                //mCallSetCommand = CALL_METHOD_PUT_GLOBAL
                cp.call(cr.getPackageName(), cr.getAttributionTag(),
                        mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
            } catch (RemoteException e) {
                Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                return false;
            }
            return true;
        }

中间关于binder通信的部分就省略了,我自己也不是很明白。(┬┬﹏┬┬)

//com.android.providers.settings.SettingsProvider
public Bundle call(String method, String name, Bundle args) {
    //取出CALL_METHOD_USER_KEY的值
    final int requestingUserId = getRequestingUserId(args);
	switch (method) {
            //...省略亿点点
        case Settings.CALL_METHOD_PUT_GLOBAL: {
            //取出VALUE的值
                    String value = getSettingValue(args);
            //取出CALL_METHOD_TAG_KEY的值
                    String tag = getSettingTag(args);
            //取出CALL_METHOD_MAKE_DEFAULT_KEY的值
                    final boolean makeDefault = getSettingMakeDefault(args);
            //取出CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY的值
                    final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
                    insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
                            overrideableByRestore);
                    break;
                }
            //...省略亿点点
    }
}

跟进到insertGlobalSetting,再进一步到mutateGlobalSetting,在该方法内进入MUTATION_OPERATION_INSERT这个分支。

//...
case MUTATION_OPERATION_INSERT: {
                    return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
                            UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
                            getCallingPackage(), forceNotify,
                            CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
                }
//...
public boolean insertSettingLocked(int type, int userId, String name, String value,
                String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
                boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
			//...
            final int key = makeKey(type, userId);
            boolean success = false;
            SettingsState settingsState = peekSettingsStateLocked(key);
            if (settingsState != null) {
                //数据插入在这里。这里返回true会触发对应的Observer
                success = settingsState.insertSettingLocked(name, value,
                        tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
            }
			//...
            if (forceNotify || success) {
                //数据改变后调用notify方法通知observer
                notifyForSettingsChange(key, name);
            }
            return success;
        }
public boolean insertSettingLocked(String name, String value, String tag,
            boolean makeDefault, boolean forceNonSystemPackage, String packageName,
            boolean overrideableByRestore) {
        if (TextUtils.isEmpty(name)) {
            return false;
        }

        Setting oldState = mSettings.get(name);
        String oldValue = (oldState != null) ? oldState.value : null;
        String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
        Setting newState;

        if (oldState != null) {
            //值更新操作。天真的我以为Settings.Global.putString只会更新value。真的是naive ಠ_ಠ
            if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage,
                    overrideableByRestore)) {
                return false;
            }
            newState = oldState;
        } 
        //...
        return true;
    }
private boolean update(String value, boolean setDefault, String packageName, String tag,
                boolean forceNonSystemPackage, boolean overrideableByRestore,
                boolean resetToDefault) {
             //...
    		//这几个属性都没有改变的情况下才认为Setting的值没有改变
            // Is something gonna change?
            if (Objects.equals(value, this.value)
                    && Objects.equals(defaultValue, this.defaultValue)
                    && Objects.equals(packageName, this.packageName)
                    && Objects.equals(tag, this.tag)
                    && defaultFromSystem == this.defaultFromSystem
                    && isPreserved == this.isValuePreservedInRestore) {
                return false;
            }
            return true;
        }

不同进程即使对同一个Setting写入相同的值,也会改变Setting,触发Observer。

(❁´◡`❁)菜鸟一个,记录下学习过程。

如果文中有错误,欢迎大家指正。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值