← 返回文章流

Android源码分析: 使用场景获取ContentProvider分析

之前已经分析过在应用启动的时候安装ContentProvider的流程了,现在我们再从使用者的角度看看是怎样去拿到ContentProvider的。 在使用ContentProvider的时候,我们通常会使用Context拿到ContentResolver,然后在执行CURD的操作,比如我们要查询手机中的联系人,通常会这样做: 1 2 3 4 5 6 7 8 String[] projection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; Cursor profileCursor = getContentResolver().query( Profile.CONTENT_URI, projection , null, null,null); 这里先重点分析一下拿到ContentProvider的过程。首先来看看这个ContentResolver是什么东西。通过源码我们可以看到它是一个抽象类,实现了ContentInterface接口,ContentInterface中则定义了CRUD的相关方法。我们可以在ContextImpl中找到getContentResolver(),通过源码我们知道,实际上我们拿到的是ApplicationContentResolver对象。 这里看完,我们可以继续看query方法,实现在ContentResolver类当中。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 IContentProvider unstableProvider = acquireUnstableProvider(uri); IContentProvider stableProvider = null; Cursor qCursor = null; try { try { qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection, queryArgs, remoteCancellationSignal); } qCursor.getCount(); final IContentProvider provider = (stableProvider != null) ? stableProvider : acquireProvider(uri); final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider); stableProvider = null; qCursor = null; return wrapper; } catch (RemoteException e) { return null; } finally { if (qCursor != null) { qCursor.close(); } if (unstableProvider != null) { releaseUnstableProvider(unstableProvider); } if (stableProvider != null) { releaseProvider(stableProvider); } } 上面的代码看起来还是比较简单的,首先是是去调用acquireUnstableProvider拿到unstableProvider,通过它去取数据,如果拿不到再去调用acquireProvider拿stableProvider,最后把stableProvider和数据使用CursorWrappInner包装返回给调用者,在finally中把cursor关掉,把provider给释放掉。 我们先来看看拿stableProvider的逻辑: 1 2 3 4 5 6 7 8 9 10 public final IContentProvider acquireUnstableProvider(Uri uri) { if (!SCHEME_CONTENT.equals(uri.getScheme())) { return null; } String auth = uri.getAuthority(); if (auth != null) { return acquireUnstableProvider(mContext, uri.getAuthority()); } return null; } 简单说一下,上面首先会判断我们的URL是否为content:开头,因为这是ContentProvider的scheme。之后会到url中拿到authority, autority包括这几个部分:[userinfo@]host[:port] 。最后通过authority去调用ApplicationContentResolver中的同名方法。 1 2 3 4 5 protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); } 上面的方法会从我们的authority分别拿出userId和host,当然userId有可能是不传的,就会默认使用当前用户。我们继续去看ActivityThread.acquireProvider()代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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; } ContentProviderHolder holder = null; final ProviderKey key = getGetProviderKey(auth, userId); try { synchronized (key) { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), c.getOpPackageName(), auth, userId, stable); if (holder != null && holder.provider == null && !holder.mLocal) { synchronized (key.mLock) { if(key.mHolder != null) { } else { key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS) } holder = key.mHolder; } } } } finally { synchronized (key.mLock) { key.mHolder = null; } } holder = installProvider(c, holder, holder.info, true, holder.noReleaseNeeded, stable); return holder.provider; } 这里我们有一个参数stable,因此我们前面获取stableProvider和unstableProvider都会走到这个方法里面来。 第3行代码,我们首先会到已存在的Provider列表中去拿,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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; IBinder jBinder = provider.asBinder(); if (!jBinder.isBinderAlive()) { //处理Binder不存活的情况 handleUnstableProviderDiedLocked(jBinder, true); return null; } ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { incProviderRefLocked(prc, stable); //增加引用计数 } return provider; } } 可以看到此处为通过auth构建出来的key到mProviderMap中查找ProviderClientRecord,而这个就是我们之前分析安装Provider时候所创建并且放置到这个map中去的。后面会检查Binder是否仍然存活,并返回。 在这里我们需要注意一点,如果安装我们之前分析安装的流程,我们在自己的app里面拿自己的ContentProvider这里是肯定可以拿到的,但是如果是其他的应用提供的ContentProvider这里很显然是拿不到的。因此我们需要继续回到acquireProvider方法去看其他部分的代码。 在第11行中,我们会到AMS中去获取ContentProviderHolder,如果拿到了远端的holder,但是我们本地的ProviderKey中的holder为空,说明我们本地还没有安装这个ContentProvider,需要等待,也就是执行第16行代码进入等待状态。而这个地方的解除等待在ContentProviderHelper类的publishConten

来自 码农明明桑作者主页
在原博客阅读全文 (新标签页打开)

以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

一起聊聊 0

尊重不同观点

还没有评论。欢迎分享第一个想法。

RSS SUBSCRIPTIONS

选择 RSS 订阅

把博客汇的文章添加到你常用的阅读器。

关注文章 RSS 订阅本订阅需要登录后使用