Android System Server大纲之ContentService和ContentProvider原理剖析

Android System Server大纲之ContentService和ContentProvider原理剖析

内容提供程序

观察内容提供商

前言

ContentService是Android四大组件之一的ContentProvider密切相关的一个系统服务,且和AccountManagerService也有着紧密的联系。ContentService以功能来划分,就两大模块:

  • 提供管理通知监听ContentProvider数据变化的观察者的服务
  • 同步功能:同步日历、Email等

第一个功能,对于很多开发者来说可能熟悉。而对于第二个,可能就会陌生一点,这个是Android提供的同步功能,很多APP开发者,也很难用到这个Android的同步功能。ContentService的同步功能和AccountManagerService往往紧密联系在一起。本文将不再阐述ContentService的同步功能,有兴趣的读者可以自行阅读Android的参考文档了解该功能的使用方法https://developer.android.google.cn/reference/android/content/SyncRequest.html。那么本文将阐述的内容是:

  • ContentProvider的原理
  • ContentProvider的观察者服务

ContentProvider是Android的四大组件之一,是提供APP间安全共享数据的方式之一,APP可以通过ContentProvider从其它APP中获取到想要的数据,例如读取手机的联系人。也可以通过ContentProvider把APP的数据共享出去给其它APP使用。

以查询ContentProvider的数据的流程为线索,一步一步分析这个过程,从而了解ContentProvider的过程和原理。

APP发起查询

引用官方的一段话:如果您想要访问内容提供程序中的数据,可以将应用的 Context 中的 ContentResolver 对象用作客户端来与提供程序通信。 ContentResolver 对象会与提供程序对象(即实现 ContentProvider 的类实例)通信。 提供程序对象从客户端接收数据请求,执行请求的操作并返回结果。

APP发起查询,很简单,如下:

ContentResolver cr = getContentResolver();
cr.query(Uri uri, ....);

首先通过getContentResolver()获取到ContentResolver对象,ContentResolver对象好比内容分解器,能够分解出来需要调用哪个内容提供程序即ContentProvider。ContentResolver分解的依据便是参数Uri。Uri是如何把ContentResolver和ContentProvider联系起来的呢?Uri可以看作是一个地址描述,随意举一个Uri可描述的地址例子:

content://user_dictionary

可以和平时上网的网址做一个类比,如访问百度:

https://www.baidu.com

Uri和网址结构有很大相似性,https表示的是网络技术中的传输协议,那么content也可以当做ContentProvider中的一种协议,那么content就代表当前需要通过ContentProvider进行数据通信。www.baidu.com是百度的域名,表示需要访问网站的位置,user_dictionary也就可以比作ContentProvider的域名,user_dictionary表示是那个ContentProvider。换句话说,ContentResolver接收到content://user_dictionary这个Uri时,就知道当前需要发起ContentProvider数据交互,通过user_dictionary可以寻找用user_dictionary表示的ContentProvider。

深入ContentResolver

准备工作

查看ContentResolver的定义,如下:

public abstract class ContentResolver {
    ......
}

可见ContentResolver是一个抽象类,所以说getContentResolver()所获取到实例本质是ContentResolver的子类,查看getContentResolver()的具体实现:

private final ApplicationContentResolver mContentResolver;
public ContentResolver getContentResolver() {
    return mContentResolver;
}

上面的代码定义在文件frameworks/base/core/java/android/app/ContextImpl.java中

从上面的代码可知,getContentResolver()获取到的实际是ApplicationContentResolver的实例mContentResolver。cr.query(Uri uri, ….)的实现如下:

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,......) {
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    ......
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        ......
        try {
            qCursor = unstableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            unstableProviderDied(unstableProvider);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
        ......
}

这个方法定义在文件frameworks/base/core/java/android/content/ContentResolver.java中。

这里用文字描述一下上面代码的过程,先声明两个相同类型的局部变量unstableProvider、stableProvider,都是IContentProvider的实例,IContentProvider是AIDL的接口,所以IContentProvider所调用的方法是远程调用。通过acquireUnstableProvider()方法给unstableProvider变量赋值,unstableProvider调用query()方法,如果发生DeadObjectException异常,调用acquireProvider()方法给stableProvider赋值,然后调用stableProvider的query()方法。

这里有点奇怪,为什么要声明两个一样的IContentProvider变量,且调用相同的方法query()。从变量的命名上,也能找到一点线索,unstableProvider和stableProvider,前者是不稳定的provider,后者是稳定的provider。什么样表示稳定的,什么样表示不稳定的?从上面的代码来看,不稳定的provider,调用query()方法会抛出DeadObjectException。下面先看看unstableProvider的赋值过程:

public final IContentProvider acquireExistingProvider(......) {
    synchronized (mProviderMap) {
        final ProviderKey key = new ProviderKey(auth, userId);
        final ProviderClientRecord pr = mProviderMap.get(key);
        if (pr == null) {
            return null;
        }

        IContentProvider provider = pr.mProvider;
        IBinder jBinder = provider.asBinder();
        ......
        return provider;
    }
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

可见unstableProvider是从变量mProviderMap的实例中遍历出来,也就是说,在调用者的进程内部,对provider会缓存到mProviderMap这个变量中。而IContentProvider是一个跨进程的远程调用。换句话说,调用者的进程内部所缓存的unstableProvider实例所对应的远程进程不能保证还正常运行着,如果所对应的远程进程没有运行,那么就会抛出DeadObjectException异常,并调用unstableProviderDied()方法把本地的缓存清除。然后通过acquireProvider()重新生产一个新的stableProvider,且Android系统会启动和stableProvider对应的远程进程,这个过程下文会阐述。可见Android这个举措是为了性能考虑,目的让APP的运行仅可能的快。

获取ContentProvider

回到上文中实例化变量unstableProvider和stableProvider的代码,unstableProvider或stableProvider实例化后,直接调用query()方法,然后直接就可以返回Cursor给调用者了,说明在这里的query()这一步,已经拿到调用者需要的数据。所以理解unstableProvider和stableProvider的实质至关重要。那么unstableProvider和stableProvider所对应的远程进程的服务是哪一个呢?是不是就直接对应ContentProvider呢?

由于unstableProvider是一个调用者进程中缓存的实例,所以需要对stableProvider下手,了解stableProvider的实例化过程即可,如下:

public final IContentProvider acquireProvider(......) {
    ......
    IActivityManager.ContentProviderHolder holder = null;
    try {
        holder = ActivityManagerNative.getDefault().getContentProvider(
                getApplicationThread(), auth, userId, stable);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ......
    return holder.provider;
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

stableProvider被封装到ContentProviderHolder的变量中provider中。通过调用ActivityManagerNative.getContentProvider()获取ContentProviderHolder,ActivityManagerNative的所对应的远程服务是system_process中的ActivityManagerService,接着看getContentProvider()这个方法:

public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String name, int userId, boolean stable) {
    ......
    return getContentProviderImpl(caller, name, null, stable, userId);
}

这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

这里直接调用了getContentProviderImpl()方法:

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;

    synchronized(this) {
        ......

        boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
        if (providerRunning) {
            ......
        }

        if (!providerRunning) {
            ......
            // If the provider is not already being launched, then get it
            // started.
            if (i >= N) {

                try {
                    // Content provider is now in use, its package can't be stopped.
                    ......
                    if (proc != null && proc.thread != null && !proc.killed) {
                        ......
                    } else {
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0, "content provider",
                                new ComponentName(cpi.applicationInfo.packageName,
                                        cpi.name), false, false, false);
                       ......
                    }
                    cpr.launchingApp = proc;
                    mLaunchingProviders.add(cpr);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
            ......

            mProviderMap.putProviderByName(name, cpr);
            conn = incProviderCountLocked(r, cpr, token, stable);
            if (conn != null) {
                conn.waiting = true;
            }
        }
    }

    // Wait for the provider to be published...
    synchronized (cpr) {
        while (cpr.provider == null) {
            ......
                cpr.wait();
            ......
        }
    }
    return cpr != null ? cpr.newHolder(conn) : null;
}

这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

由于这个方法的代码篇幅非常大,这里省略了很多代码,省略的代码主要就是一些缓存、权限等等相关的代码。这里假设provider的进程没有启动,从最原始,最干净的时候分析ContentProvider的这个过程。聚焦上面的代码,首先分两种情况,providerRunning和!providerRunning,providerRunning是基于!providerRunning为前提的,因此provider第一次启动是运行!providerRunning这个代码路径。假若provider没有运行过,Android首先通过startProcessLocked()把provider所在的应用启动起来,这个过程这里暂时先放下,继续往下看,在最后面,有一个while循环,循环条件是cpr.provider == null?这里会一直等待cpr的provider变量被赋值,回到上文中的acquireProvider()方法的代码,acquireProvider()首先要获取的是ContentProviderHolder的实例,上面的代码如果cpr != null,就返回一个ContentProviderHolder对象(cpr.newHolder(conn))。所以这里的cpr.provider就是上文中acquireProvider()需要获取的IContentProvider的实例对象。

公布ContentProvider

因此,只要继续分析清楚cpr.provider的赋值过程,就知道开始的时候ContentResolver.query()所调用的真正的远程服务。回到上面的代码,startProcessLocked()启动应用时,运行到如下代码(APP的启动过程本文不再赘述):

public static void main(String[] args) {
    ......
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    ......
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

生成ActivityThread实例,也就是常说的主线程(UI线程了),然后调用attach()方法:

private void attach(boolean system) {
    ......
    final IActivityManager mgr = ActivityManagerNative.getDefault();
    try {
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ......
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

又回到ActivityManagerService了,接着往下看:

public final void attachApplication(IApplicationThread thread) {
    ......
    attachApplicationLocked(thread, callingPid);
    ......
}

这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

这里没有需要看的代码,继续往下跟踪:

 private final boolean attachApplicationLocked(IApplicationThread thread,.....) {
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,......);
}

这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

IApplicationThread实例thread是远程APP的远程调用端,所以thread.bindApplication()实际又从system_process进程回到了APP所在的进程,注意第三个参数providers,providers是封装了应用的所有的ContentProvider,详情读者可以阅读generateApplicationProvidersLocked(app)方法,本文不再阐述。继续往下看:

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,......) {
    ......
    data.providers = providers;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

发送了一个H.BIND_APPLICATION的消息,注意data.providers = providers。该消息的处理是在:

private void handleBindApplication(AppBindData data) {
if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
        ......
    }
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

调用了installContentProviders()方法,注意参数data.providers,如下:

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
        new ArrayList<IActivityManager.ContentProviderHolder>();

    for (ProviderInfo cpi : providers) {
        IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        ActivityManagerNative.getDefault().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

把应用的所有的ContentProvider信息封装到ContentProviderHolder中,这里要回到上文中acquireProvider()方法,获取IContentProvider的实例正好是ContentProviderHolder的中的变量provider,因此,这里需要了解上面代码ContentProviderHolder的实例cph的实例化过程,看installProvider()方法:

private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,.....) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
        ......
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            ......

    IActivityManager.ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                ......
            } else {
                holder = new IActivityManager.ContentProviderHolder(info);
                holder.provider = provider;
                ......
            }
            retHolder = pr.mHolder;
        } else {
            ......

    return retHolder;
}

这个方法定义在文件frameworks/base/core/java/android/app/ActivityThread.java中。

上面的代码通过ClassLoader映射ContentProvider的对象实例,然后调用getIContentProvider()方法,获取到IContentProvider的实例provider,然后赋值给ContentProviderHolder的实例holder的变量provider,所以provider正是acquireProvider()中获取的的IContentProvider的实例,而继续看getIContentProvider()方法查看provider的本质是:

......
private Transport mTransport = new Transport();
public IContentProvider getIContentProvider() {
    return mTransport;
}

class Transport extends ContentProviderNative {
    public Cursor query(.....){
        ......
    }
    ......
}
......

这个代码定义在文件frameworks/base/core/java/android/content/ContentProvider.java中。

上面的代码可见,provider实质是Transport的实例mTransport,Transport是ContentProviderNative的子类,而ContentProviderNative是IContentProvider的子类。

下面继续看看provider和ActivityManagerService的关系,回到上文中的installContentProviders()方法,调用了publishContentProviders()方法,如下:

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
    ......
    final int N = providers.size();
    for (int i = 0; i < N; i++) {
        ContentProviderHolder src = providers.get(i);
        if (src == null || src.info == null || src.provider == null) {
            continue;
        }
        ContentProviderRecord dst = r.pubProviders.get(src.info.name);
        if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
        if (dst != null) {
            ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
            mProviderMap.putProviderByClass(comp, dst);
            String names[] = dst.info.authority.split(";");
            for (int j = 0; j < names.length; j++) {
                mProviderMap.putProviderByName(names[j], dst);
            }
            ......
            synchronized (dst) {
                dst.provider = src.provider;
                dst.proc = r;
                dst.notifyAll();
            }
        }
    }
}

这个代码定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。

把应用的ContentProvider信息缓存到mProviderMap中,这里重点关注语句dst.provider = src.provider,回到上文中getContentProviderImpl()方法中的代码,后面的while循环,等待cpr.provider的赋值,dst.provider = src.provider正好就是这个赋值过程,cpr.provider赋值后,getContentProviderImpl()返回ContentProviderHolder的实例cpr.newHolder(conn),而这个实例持有变量provider指向Transport的实例mTransport,也就是应用的ContentProvider。

关系图

这个过程非常绕乱人的头绪,下面把这个过程简单转换成一张简单的关系图,理清这个过程和关系,如下:

这里写图片描述

ContentProvider数据监听

观察者注册过程

有时APP开发的时候,APP运行时,需要实时监听一些ContentProvider数据的变化。如用户添加了一个联系人,APP需要接收到联系人添加的通知。由于ContentProvider的提供者,和需求者不是同一个APP,所以,这个通知的纽带,就需要ContentService来完成。从注册一个观察者开始,分析这个通知的来龙去脉,注册代码如下:

ContentResolver cr = getContentResolver();
cr.registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
            @NonNull ContentObserver observer);

当ContentProvider的数据发生变化时,便会回调到ContentObserver的onChange()方法,观察者ContentObserver observer被注册到哪里去呢?继续进入分析:

public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
        ContentObserver observer, @UserIdInt int userHandle) {
    try {
        getContentService().registerContentObserver(uri, notifyForDescendents,
                observer.getContentObserver(), userHandle);
    } catch (RemoteException e) {
    }
}

这个方法定义在文件frameworks/base/core/java/android/content/ContentResolver.java中。

上面的代码getContentService()获取到的对象是:

public static IContentService getContentService() {
    if (sContentService != null) {
        return sContentService;
    }
    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;
}

这个方法定义在文件frameworks/base/core/java/android/content/ContentResolver.java中。

阅读《Android系统之System Server大纲 》可知,启动的CONTENT_SERVICE_NAME的系统服务如下:

private static final String CONTENT_SERVICE_CLASS =
            "com.android.server.content.ContentService$Lifecycle";
traceBeginAndSlog("StartContentService");
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

这个方法定义在文件frameworks/base/services/java/com/android/server/SystemServer.java中。

所以getContentService()获取到的是ContentService,继续往下分析registerContentObserver():

public void registerContentObserver(Uri uri, boolean notifyForDescendants,
                                    IContentObserver observer, int userHandle) {

    ......

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

这个方法定义在文件frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

上面的代码把观察者observer通过addObserverLocked放置在mRootNode对象中

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));
        return;
    }

    // Look to see if the proper child already exists
    String segment = getUriSegment(uri, index);
    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);
    }

这个方法定义在文件frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

首先把观察者observer再次封装到mObservers中,mObservers是ArrayList的实例,后面依次遍历整个需要监听的Uri,用segment生产一个ObserverNode实例,保存在ArrayList的实例mChildren中。

观察者都被封装好以后,就等待数据的变化了。在上文ContentProvider的原理中,可知,ContentProvider的需求者拿到ContentProvider提供者的远程调用后,两个APP直接进行交互了,任何数据变化并没有经过ContentService,因此,ContentService是如何监测ContentProvider数据发生变化并通知到观察者呢?这里的机制便是ContentProvider提供者的数据发生变化时,ContentProvider提供者必须主动通过notifyChange()方法通知ContentService数据发生了变化。下面以联系人的数据库变化为例,阐述这个过程。

数据变化通知过程

添加一个联系人,调用ContactsProvider的insert()方法,如下:

public Uri insert(Uri uri, ContentValues values) {
    incrementStats(mInsertStats, mInsertInBatchStats);
    ContactsTransaction transaction = startTransaction(false);
    try {
        Uri result = insertInTransaction(uri, values);
        if (result != null) {
            transaction.markDirty();
        }
        transaction.markSuccessful(false);
        return result;
    } finally {
        endTransaction(false);
    }
}

这个方法定义在文件packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。

上面的代码中,一次插入的业务抽象成一个ContactsTransaction,然后调用insertInTransaction()方法把数据插入数据库成功后,调用transaction.markDirty()标记当前的数据状态有变化,然后调用endTransaction()结束当前的业务。如下:

private void endTransaction(boolean callerIsBatch) {
    ContactsTransaction transaction = mTransactionHolder.get();
    if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
        boolean notify = false;
        try {
            if (transaction.isDirty()) {
                notify = true;
            }
            transaction.finish(callerIsBatch);
            if (notify) {
                notifyChange();
            }
        } finally {
            // No matter what, make sure we clear out the thread-local transaction reference.
            mTransactionHolder.set(null);
        }
    }
}

这个方法定义在文件packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。

上面的代码,通过mTransactionHolder.get()获取刚刚进行的业务transaction,如果的这个业务被标记为markDirty(),transaction.isDirty()返回true,所以notify被置成true,所以会调用notifyChange()。往下看这个方法:

protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
    getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
            syncToNetwork);

    getContext().getContentResolver().notifyChange(MetadataSync.METADATA_AUTHORITY_URI,
            null, syncToMetadataNetwork);
}

这个方法定义在文件packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java中。

上面的代码,获取到ContentResolver()对象,和query()类似,然后调用notifyChange()通知ContentResolver数据发生了变化,如下:

public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
        @UserIdInt int userHandle) {
    try {
        getContentService().notifyChange(
                uri, observer == null ? null : observer.getContentObserver(),
                observer != null && observer.deliverSelfNotifications(),
                syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
                userHandle);
    } catch (RemoteException e) {
    }
}

这个方法定义在文件frameworks/base/core/java/android/content/ContentResolver.java中。

上面的代码调用getContentService()方法,从上文可知,getContentService()获取到的是ContentService的远程调用,因此getContentService().notifyChange()实质会运行到ContentService的notifyChange()方法,如下:

public void notifyChange(Uri uri, ......) {
    ......
    try {
        ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
        synchronized (mRootNode) {
            mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
                    flags, userHandle, calls);
        }
        final int numCalls = calls.size();
        for (int i=0; i<numCalls; i++) {
            ObserverCall oc = calls.get(i);
            try {
                oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
            } catch (RemoteException ex) {
                ......
            }
        }
        ......
        }
    } finally {
        restoreCallingIdentity(identityToken);
    }
}

这个方法定义在文件frameworks/base/services/core/java/com/android/server/content/ContentService.java中。

上面的代码中的mRootNode变量,在注册观察者的章节中,mRootNode封装了所有的观察者。调用collectObserversLocked()根据观察者监听的Uri类型把观察者封装到ArrayList的实例calls中,然后调用oc.mObserver.onChange()方法通知APP数据发生变化。

总结

本文详细阐述了ContentProvider的原理,从ContentProvider的query过程入手,全面详细分析了从query()的发起APP到ActivityManagerService,再到ContentProvider提供程序的整个流程和交互过程。以及分析了ContentService在ContentProvider的数据变化监测通知的架构中,扮演了观察者模式中的抽象主题,具体主题部分。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《深入理解Android:卷2》是“深入理解Android”系列的第2本,第1本书上市后获得广大读者高度评价,在Android开发者社群内口口相传。《深入理解Android:卷2》不仅继承了第1本书的优点并改正了其在细微处存在的一些不足,而且还在写作的总体思想上进行了创新,更强调从系统设计者的角度去分析Android系统中各个模块内部的实现原理和工作机制。从具体内容上讲,重点是Android Framework的Java层,对Java层涉及的核心模块和服务进行了深入而细致的分析。通过《深入理解Android:卷2》,读者不仅能对Android系统本身有更深入的理解,而且还能掌握分析大型复杂源代码的能力。 《深入理解Android:卷2》共8章:第1章介绍了阅读本书所需要做的准备工作,包括Android 4.0源码的下载和编译、Eclipse环境的搭建,以及Android系统进程(system_process)的调试等;第2章对Java Binder和MessageQueue的实现进行了深入分析;第3章仔细剖析了SystemServer的工作原理,这些服务包括EntropyService、DropboxManagerService、DiskStatsService、DeviceStorageMonitorService、SamplingProfilerService和ClipboardService;第4章对系统中负责Package信息查询和APK安装、卸载、更新等工作的服务PackageManagerService进行了详细分析;第5章则对Android系统中负责电源管理的核心服务 PowerManagerService原理进行了一番深入的分析;第6章以ActivityManagerService为分析重点,它的启动、Activity的创建和启动、BroadcastReceiver的工作原理Android中的进程管理等内容展开了较为深入的研究;第7章对ContentProvider的创建和启动、SQLite、Cursor query和close的实现等进行了深入分析;第8章以ContentService和AccountManagerService为分析对象,介绍了数据更新通知机制的实现,以及账户管理和数据同步等相关知识。
《深入理解Android:卷2》是“深入理解Android”系列的第2本,第1本书上市后获得广大读者高度评价,在Android开发者社群内口口相传。《深入理解Android:卷2》不仅继承了第1本书的优点并改正了其在细微处存在的一些不足,而且还在写作的总体思想上进行了创新,更强调从系统设计者的角度去分析Android系统中各个模块内部的实现原理和工作机制。从具体内容上讲,重点是Android Framework的Java层,对Java层涉及的核心模块和服务进行了深入而细致的分析。通过《深入理解Android:卷2》,读者不仅能对Android系统本身有更深入的理解,而且还能掌握分析大型复杂源代码的能力。《深入理解Android:卷2》共8章:第1章介绍了阅读本书所需要做的准备工作,包括Android 4.0源码的下载和编译、Eclipse环境的搭建,以及Android系统进程(system_process)的调试等;第2章对Java Binder和MessageQueue的实现进行了深入分析;第3章仔细剖析了SystemServer的工作原理,这些服务包括EntropyService、DropboxManagerService、DiskStatsService、DeviceStorageMonitorService、SamplingProfilerService和ClipboardService;第4章对系统中负责Package信息查询和APK安装、卸载、更新等工作的服务PackageManagerService进行了详细分析;第5章则对Android系统中负责电源管理的核心服务 PowerManagerService原理进行了一番深入的分析;第6章以ActivityManagerService为分析重点,它的启动、Activity的创建和启动、BroadcastReceiver的工作原理Android中的进程管理等内容展开了较为深入的研究;第7章对ContentProvider的创建和启动、SQLite、Cursor query和close的实现等进行了深入分析;第8章以ContentService和AccountManagerService为分析对象,介绍了数据更新通知机制的实现,以及账户管理和数据同步等相关知识。
第1章 搭建Android源码工作环境 1.1 Android系统架构 1.2 搭建开发环境 1.2.1 下载源码 1.2.2 编译源码 1.2.3 利用Eclipse调试system_process 1.3 本章小结 第2章 深入理解Java Binder和MessageQueue 2.1 概述 2.2 Java层中的Binder架构分析 2.2.1 Binder架构总览 2.2.2 初始化Java层Binder框架 2.2.3 addService实例分析 2.2.4 Java层Binder架构总结 2.3 心系两界的MessageQueue 2.3.1 MessageQueue的创建 2.3.2 提取消息 2.3.3 nativePollOnce函数分析 2.3.4 MessageQueue总结 2.4 本章小结 第3章 深入理解SystemServer 3.1 概述 3.2 SystemServer分析 3.2.1 main函数分析 3.2.2 Service群英会 3.3 EntropyService分析 3.4 DropBoxManagerService分析 3.4.1 DBMS构造函数分析 3.4.2 dropbox日志文件的添加 3.4.3 DBMS和settings数据库 3.5 DiskStatsService和DeviceStorageMonitorService分析 3.5.1 DiskStatsService分析 3.5.2 DeviceStorageManagerService分析 3.6 SamplingProfilerService分析 3.6.1 SamplingProfilerService构造函数分析 3.6.2 SamplingProfilerIntegration分析 3.7 ClipboardService分析 3.7.1 复制数据到剪贴板 3.7.2 从剪切板粘贴数据 3.7.3 CBS中的权限管理 3.8 本章小结 第4章 深入理解PackageManagerService 4.1 概述 4.2 初识PackageManagerService 4.3 PKMS的main函数分析 4.3.1 构造函数分析之前期准备工作 4.3.2 构造函数分析之扫描Package 4.3.3 构造函数分析之扫尾工作 4.3.4 PKMS构造函数总结 4.4 APK Installation分析 4.4.1 adb install分析 4.4.2 pm分析 4.4.3 installPackageWithVerification函数分析 4.4.4 APK 安装流程总结 4.4.5 Verification介绍 4.5 queryIntentActivities分析 4.5.1 Intent及IntentFilter介绍 4.5.2 Activity信息的管理 4.5.3 Intent 匹配查询分析 4.5.4 queryIntentActivities总结 4.6 installd及UserManager介绍 4.6.1 installd介绍 4.6.2 UserManager介绍 4.7 本章学习指导 4.8 本章小结 第5章 深入理解PowerManagerService 5.1 概述 5.2 初识PowerManagerService 5.2.1 PMS构造函数分析 5.2.2 init分析 5.2.3 systemReady分析 5.2.4 BootComplete处理 5.2.5 初识PowerManagerService总结 5.3 PMS WakeLock分析 5.3.1 WakeLock客户端分析 5.3.2 PMS acquireWakeLock分析 5.3.3 Power类及LightService类介绍 5.3.4 WakeLock总结 5.4 userActivity及Power按键处理分析 5.4.1 userActivity分析 5.4.2 Power按键处理分析 5.5 BatteryService及BatteryStatsService分析 5.5.1 BatteryService分析 5.5.2 BatteryStatsService分析 5.5.3 BatteryService及BatteryStatsService总结 5.6 本章学习指导 5.7 本章小结 第6章 深入理解ActivityManagerService 6.1 概述 6.2 初识ActivityManagerService 6.2.1 ActivityManagerService的main函数分析 6.2.2 AMS的 setSystemProcess分析 6.2.3 AMS的 installSystemProviders函数分析 6.2.4 AMS的 systemReady分析 6.2.5 初识ActivityManagerService总结 6.3 startActivity分析 6.3.1 从am说起 6.3.2 AMS的startActivityAndWait函数分析 6.3.3 startActivityLocked分析 6.4 Broadcast和BroadcastReceiver分析 6.4.1 registerReceiver流程分析 6.4.2 sendBroadcast流程分析 6.4.3 BROADCAST_INTENT_MSG消息处理函数 6.4.4 应用进程处理广播分析 6.4.5 广播处理总结 6.5 startService之按图索骥 6.5.1 Service知识介绍 6.5.2 startService流程图 6.6 AMS中的进程管理 6.6.1 Linux进程管理介绍 6.6.2 关于Android中的进程管理的介绍 6.6.3 AMS进程管理函数分析 6.6.4 AMS进程管理总结 6.7 App的 Crash处理 6.7.1 应用进程的Crash处理 6.7.2 AMS的handleApplicationCrash分析 6.7.3 AppDeathRecipient binderDied分析 6.7.4 App的Crash处理总结 6.8 本章学习指导 6.9 本章小结 第7章 深入理解ContentProvider 7.1 概述 7.2 MediaProvider的启动及创建 7.2.1 Context的getContentResolver函数分析 7.2.2 MediaStore.Image.Media的query函数分析 7.2.3 MediaProvider的启动及创建总结 7.3 SQLite创建数据库分析 7.3.1 SQLite及SQLiteDatabase家族 7.3.2 MediaProvider创建数据库分析 7.3.3 SQLiteDatabase创建数据库的分析总结 7.4 Cursor 的query函数的实现分析 7.4.1 提取query关键点 7.4.2 MediaProvider 的query分析 7.4.3 query关键点分析 7.4.4 Cursor query实现分析总结 7.5 Cursor close函数实现分析 7.5.1 客户端close的分析 7.5.2 服务端close的分析 7.5.3 finalize函数分析 7.5.4 Cursor close函数总结 7.6 ContentResolver openAssetFileDescriptor函数分析 7.6.1 openAssetFileDescriptor之客户端调用分析 7.6.2 ContentProvider的 openTypedAssetFile函数分析 7.6.3 跨进程传递文件描述符的探讨 7.6.4 openAssetFileDescriptor函数分析总结 7.7 本章学习指导 7.8 本章小结 第8章 深入理解ContentService和AccountManagerService 8.1 概述 8.2 数据更新通知机制分析 8.2.1 初识ContentService 8.2.2 ContentResovler 的registerContentObserver分析 8.2.3 ContentResolver的 notifyChange分析 8.2.4 数据更新通知机制总结和深入探讨 8.3 AccountManagerService分析 8.3.1 初识AccountManagerService 8.3.2 AccountManager addAccount分析 8.3.3 AccountManagerService的分析总结 8.4 数据同步管理SyncManager分析 8.4.1 初识SyncManager 8.4.2 ContentResolver 的requestSync分析 8.4.3 数据同步管理SyncManager分析总结 8.5 本章学习指导 8.6 本章小结
好的,这里给您提供一份前后端分离的代码示例: 后端Java代码: ```java @RestController @RequestMapping("/api") public class ContentController { @Autowired private ContentService contentService; @PostMapping("/content") public void saveContent(@RequestBody Content content) { contentService.save(content); } @GetMapping("/content/{id}") public Content getContent(@PathVariable Long id) { return contentService.getById(id); } } ``` 这里使用了Spring Boot框架,通过@RestController注解将类标记为Controller并启用RESTful风格的接口,@RequestMapping注解定义了接口的根路径。@PostMapping注解表示接收POST请求,@RequestBody注解表示请求体中的JSON数据将被映射到Content对象中。 ContentServiceContent对象的定义略过,这里假设已经实现了。 前端Vue代码: ```vue <template> <div> <div ref="editor"></div> <button @click="saveContent">保存</button> </div> </template> <script> import tinymce from 'tinymce/tinymce'; import 'tinymce/themes/silver/theme'; import 'tinymce/plugins/advlist'; import 'tinymce/plugins/autolink'; import 'tinymce/plugins/lists'; import 'tinymce/plugins/link'; export default { name: 'Editor', data() { return { editor: null, content: '', } }, mounted() { tinymce.init({ selector: this.$refs.editor, plugins: 'advlist autolink lists link', toolbar: 'undo redo | bold italic | bullist numlist | link', height: 500, setup: (editor) => { this.editor = editor; editor.on('change', () => { this.content = editor.getContent(); }); }, }); }, methods: { saveContent() { axios.post('/api/content', {content: this.content}) .then(() => { alert('保存成功'); }).catch((error) => { alert('保存失败:' + error.message); }); } } } </script> ``` 这里使用了Vue.js框架和tinymce富文本编辑器。mounted钩子函数中初始化了tinymce编辑器,并使用setup方法监听了编辑器内容的变化,将内容保存到了组件的data中。saveContent方法将保存的内容通过axios.post方法发送到后端的/api/content接口。 当移动端需要展示富文本内容时,可使用Vue.js和v-html指令将内容直接渲染到页面上。例如: ```vue <template> <div v-html="content"></div> </template> <script> import axios from 'axios'; export default { name: 'Content', data() { return { content: '', } }, mounted() { axios.get('/api/content/' + this.$route.params.id) .then((response) => { this.content = response.data.content; }).catch((error) => { alert('获取内容失败:' + error.message); }); }, } </script> ``` 这里使用了Vue Router,从URL中获取内容ID参数,并通过axios.get方法从后端的/api/content/{id}接口获取内容。将获取到的内容通过v-html指令渲染到页面上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值