ContentProvider 的 query 流程分析

ContentProvider 的 query 流程分析

ContentProvider 将底层的数据结构(比如数据库、文件)封装并且提供增删改查的接口,提供给本应用或者外部的应用调用。

ContentResolver 的 query 方法

ContentProvider 的 query 操作是通过 ContentResolver 的 query 调用的,而不是直接调用 ContentProvider 的 query 方法,ContentProvider 的 query 方法类似于回调方法。

ContentResolver 的 query 方法一般调用方式如下:

context.getContentResolver().query(getUri(), null, null, null, null);

Context 是一个抽象类,它的具体实现都在 ContextImpl 类中。

ContextImpl 的 getContentResolver 方法如下:

    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

mContentResolver 是 ContextImpl 构造时创建的。

构造如下:

    private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
            @NonNull LoadedApk packageInfo, @Nullable String splitName,
            @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
            @Nullable ClassLoader classLoader) {
        ...
        mContentResolver = new ApplicationContentResolver(this, mainThread);
    }

可以看出传入了 ContextImpl 自身和 mainThread(ActivityThread),构造出 ApplicationContentResolver。

ApplicationContentResolver 继承了 ContentResolver(ContentResolver是一个抽象类)。它的实现如下:

    private static final class ApplicationContentResolver extends ContentResolver {
       ...
        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
    }

可以看出它的 acquireProvider 方法直接调用了 ActivityThread 的 acquireProvider 方法,并返回一个 IContentProvider 对象。

ApplicationContentResolver 的 query 方法实际是它的父类 ContentResolver 的 query 方法。

ContentResolver 的 query 方法如下:

    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        ...
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        ...
        try {
            ...
            try {
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                ...
                qCursor = stableProvider.query(
                        mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
            }
            ...
    }

可以看出 ContentResolver 的 query 方法首先调用 acquireUnstableProvider 获取 unstableProvider。如果 unstableProvider 可以查询到数据,就使用 unstableProvider。否则就在 catch 中调用 acquireProvider 方法获取 stableProvider,使用 stableProvider 查询。不管是 unstableProvider 还是 stableProvider 的 query 方法,都是调用的 IContentProvider。

IContentProvider

IContentProvider 代码如下:

/**
 * The ipc interface to talk to a content provider.
 * @hide
 */
public interface IContentProvider extends IInterface {
    public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
            throws RemoteException;
    ...
    /* IPC constants */
    static final String descriptor = "android.content.IContentProvider";
}

可以看出 IContentProvider 与 AIDL 文件定义的 binder 接口类似,它也继承了 IInterface 接口。IContentProvider 的实现就是一个 binder 接口。

ActivityThread 的 acquireProvider 方法如下:

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }
...    
            synchronized (getGetProviderLock(auth, userId)) {
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), auth, userId, stable);
            }
        ...
        // Install provider will increment the reference count for us, and break
        // any ties in the race.
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

acquireProvider 方法主要有 3 步骤:

  1. 获取已存在的 contentProvider(acquireExistingProvider)
  2. 如果第 1 步获取不到,就向 AMS 获取 contentProvider(ActivityManager.getService().getContentProvider)
  3. 安装第 2 步 的 contentprovider(installProvider)

acquireExistingProvider 方法的代码如下:

    public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }
...
            IContentProvider provider = pr.mProvider;
            ... 
            return provider;
        }
    }

如果 mProviderMap 里面有要找的 providerClientRecord, 就返回 providerClientRecord 的 provider,否则返回 null。

mProviderMap 是一个 ProviderKey 和 ProviderClientRecord 的 ArrayMap。在 ActivityThread 的 installProviderAuthoritiesLocked 方法中会把 ProviderClientRecord 装入 mProviderMap。

AMS 获取 ContentProviderHolder

ActivityManager.getService().getContentProvider 方法调用了 AMS 的 getContentProvider 方法。最终会调到 AMS 的 getContentProviderImpl 方法。

getContentProviderImpl 首先会检查 ContentProvider 是否已经 publish 了。

   private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
      ...
        ContentProviderRecord cpr;
      ...
            cpr = mProviderMap.getProviderByName(name, userId);
            ...

可以看出 getContentProviderImpl 会先从 mProviderMap 获取 ContentProviderRecord。

if (providerRunning) {
                ...
                if (r != null && cpr.canRunHere(r)) {
                    // This provider has been published or is in the process
                    // of being published...  but it is also allowed to run
                    // in the caller's process, so don't make a connection
                    // and just let the caller instantiate its own instance.
                    ContentProviderHolder holder = cpr.newHolder(null);
                    // don't give caller the provider object, it needs
                    // to make its own.
                    holder.provider = null;
                    return holder;
                }

如果 cp 正在运行而且可以在调用者的进程中运行(canRunHere),那么直接返回这个 ContentProviderHolder。让调用者进程初始化 contentprovider。

if (!providerRunning) {
               ...
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                 ...
                 ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                       ...
                        ai = getAppInfoForUser(ai, userId);
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);

如果 cp 没有在运行,从 packageManager 获取 ProviderInfo、ApplicationInfo,构造 ContentProviderRecord。

if (proc != null && proc.thread != null && !proc.killed) {
                           ...
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                ...
                                    proc.thread.scheduleInstallProvider(cpi);
                               ...
                            }
                        } else {
                            ...
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            ...
                        }

如果 cp 的进程启动了,但是 cp 没有 publish,需要通知应用安装 provider( scheduleInstallProvider)。

如果 cp 的进程没有启动,启动进程(startProcessLocked)。

// Wait for the provider to be published...
        synchronized (cpr) {
            while (cpr.provider == null) {
                ...
                try {
                    ...
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    cpr.wait();
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                    ...
                }
            }
        }

getContentProviderImpl 的最后有一个无限循环,一直轮询等待,直到 contentProviderRecord的 provider 不为空。

AMS 的 getContentProviderImpl 这部分主要是为了获取 ContentProviderHolder,如果能从缓存中读取到,就出缓存中取,否则创建新的 ContentProviderHolder。

ApplicationThread 的 scheduleInstallProvider

        @Override
        public void scheduleInstallProvider(ProviderInfo provider) {
            sendMessage(H.INSTALL_PROVIDER, provider);
        }

第三步可以看到调用了 proc.thread.scheduleInstallProvider(cpi) 安装 provider。它的实际操作是发消息给 ActivityThread,让它 installProvider。

ActivityThread 的 installContentProviders 方法如下:

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

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

        ...
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        ...
    }

可以看出 installContentProviders 做了 2 件事。

  1. installProvider;
  2. 通知 AMS publishContentProviders

ActivityThread 的 installProvider 方法如下:

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ...
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                ...
        } else {
            provider = holder.provider;
           ...
        }

        ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                    + " / " + info.name);
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ...
                retHolder = pr.mHolder;
            } else {
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                ...
                    mProviderRefCountMap.put(jBinder, prc);
                ...
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

可以看出如果 holder 的 provider 为空,就说明是本地 ContentProvider,这时创建本地的 ContentProvider,然后调用它的 getIContentProvider方法。
如果 holder 的 provider 不为空,说明是外部 ContentProvider,就给 mProviderRefCountMap 增加引用计数。

Transport 的 query

获取到 ContentProviderHolder 后,实际获取的是 ContentProvider 的 mTransport 对象,调用的是 mTransport 的 query 方法。

    public IContentProvider getIContentProvider() {
        return mTransport;
    }

mTransport 是 Transport 类型,它是 ContentProvider 的内部类。

    class Transport extends ContentProviderNative {

ContentProviderNative 是实现了 getIContentProvider 接口的 Binder 类,因此它可以跨进程调用。

Transport 的 query 方法如下:

        public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
                @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
            validateIncomingUri(uri);
            uri = maybeGetUriWithoutUserId(uri);
            ...
                return ContentProvider.this.query(
                        uri, projection, queryArgs,
                        CancellationSignal.fromTransport(cancellationSignal));
            ...
        }

可以看出 Transport 调用了它的父类 ContentProvider 的 query 方法。因此最后可以执行到 ContentProvider 的 query,完成查询操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值