ContentObserver内容观察者源码解析

Quibbler 2023-8-11 1205

ContentObserver内容观察者源码解析


        一定要看看ContentObserver源码内部是如何实现观察者模式观察ContentProvider内容的。用它来观察我们感兴趣的ContentProvider是很有用的(见ContentProvider笔记)。



1、注册

        先从ContentResolver注册ContentObserverregisterContentObserver()方法开始:

    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系统服务启动系统服务启动一文中,我们粗略的了解了系统服务的启动流程。其中最后一步SystemServermain()方法真正开始初始化系统中的各项服务:

    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在数据变化时,回调ContentResolvernotifyChange()方法

    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {
        ...
        //插入成功,通知观察者
        getContext().getContentResolver().notifyChange(uri, null);
        return uri;
    }


3.2、notifyChange

        看看ContentResolvernotifyChange()方法,通知更新的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()回调方法。


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com