ContentProvider 简介
内容提供程序(Content Provider)是 Android 应用程序四大组件之一,它提供了对应用使用的底层数据的抽象,使其他应用可以通过 ContentResolver 接口访问其数据,而无需了解数据的具体存储方式。
使用 ContentProvider
实现 ContentProvider 类
ContentProvider 是一个抽象类,开发者需要实现其中一些方法才能使用:
onCreate()
: 用来初始化内容提供程序提供程序。
query(Uri, String[], Bundle, CancellationSignal)
: 返回数据给调用者。
insert(Uri, ContentValues)
: 插入新数据。
update(Uri, ContentValues, String, String[])
: 更新现有的数据。
delete(Uri, String, String[])
: 删除数据。
getType(Uri)
: 返回数据的 MIME 类型。
注意数据访问的方法可能被多个线程同时访问,因此需要做到线程安全。
配置清单文件
仅仅一个自定义类还不够,ContentProvider与activity、service一样,需要在AndroidManifest.xml文件中进行配置。
1 2 3 4 5 6 <provider android:name =".MyContentProvider" android:authorities ="com.example.content.provider" android:multiprocess ="false" android:process =":remote" android:exported ="true" />
name
为自定义的 ContentProvider 子类名称
authorities
类似 URL 的域名,其他应用使用该值获取访问内容提供程序的 URI,需要保证全局唯一。
exported
为 true 则表示允许其他应用访问应用中的 ContentProvider(跨应用访问),默认为 false。
process
表示 ContentProvider 所在的进程。
multiprocess
为 true 表示每个调用者进程都会创建一个 ContentProvider
实例,默认为 false。
当 multiprocess、process 这两个参数结合起来会产生以下几种情况。
android:process=":remote"、android:multiprocess="true"
,ContentProvider 不会随应用的启动而加载,当调用 ContentProvider 的时候才会加载,并且 ContentProvider 是在调用者的进程中初始化。这时候可能定义 ContentProvider 的":remote"进程还没有启动。
android:process=":remote"、android:multiprocess="false"
(默认情况) ,ContentProvider 不会随应用的启动而加载,当调用到 ContentProvider 的时候才会加载,并且 ContentProvider 是在“:remote”进程中初始化。
android:multiprocess="true"
,ContentProvider 会随着应用的启动而加载,并且 ContentProvider 是在应用进程的主线程中初始化的。当被调用时会在调用者进程中实例化一个 ContentProvider 对象。
android:multiprocess="false"
(默认情况),ContentProvider 会随着应用的启动而加载,并且 ContentProvider 是在应用主进程的主线程中初始化的。这种 ContentProvider 只有一个实例,运行在自己 App 的进程中。所有调用者共享该 ContentProvider 实例,调用者与 ContentProvider 实例位于两个不同的进程。
获取 URI
ContentProvider 的使用者需要获取 URI,再使用 ContentResolver 对象进行 CRUD 操作。示例代码如下:
1 2 3 4 5 6 Uri uri_user = Uri.parse("content://com.example.content.provider" );ContentResolver resolver = getContentResolver();resolver.insert(uri_user, ...); resolver.query(uri_user,...); resolver.update(uri_user,...); resolver.delete(uri_user,...);
URI 类似 URL,由四个部分组成:
URI Scheme,固定为 "content://"
;
Content Provider 组件的 authorities
属性值;
资源相对路径;
资源 ID。
如果这个 ContentProvider 只有一个资源,那么资源相对路径和资源 ID 不是必须的。
源码分析
读者可以参考这张类图,对 ContentProvider 使用到的类和方法建立一个初步印象。
ContentResolver
getContentResolver
应用开发者使用 Context.getContentResolver()
方法获取 ContentResolver,其实现在 ContextImpl 类中:
1 2 3 4 @Override public ContentResolver getContentResolver () { return mContentResolver; }
其中 mContentResolver
是 ApplicationContentResolver 类型的私有变量,该类型实现了 ContentResolver,提供了一系列 ActivityThread mainThread
的代理方法。
ContentResolver 交互过程
下面从 ContentResolver.query
入手,分析 ContentResolver 如何解析并获取对应的 ContentProvider 的。
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public final @Nullable Cursor query (final @RequiresPermission .Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri" ); try { if (mWrapped != null ) { return mWrapped.query(uri, projection, queryArgs, cancellationSignal); } } catch (RemoteException e) { return null ; } IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null ) { return null ; } IContentProvider stableProvider = null ; Cursor qCursor = null ; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null ; if (cancellationSignal != null ) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } try { qCursor = unstableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null ) { return null ; } qCursor = stableProvider.query( mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } if (qCursor == null ) { return null ; } 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 { ... } }
使用 Preconditions 检查 uri
是否为空,若为空抛出 NullPointerException;
检查是否设定对象成员ContentInterface mWrapped
,若设定则直接使用其 query
方法返回值;
尝试调用 acquireUnstableProvider(Uri)
方法获取 IContentProvider unstableProvider
,如果获取失败则直接返回 null;
如果传入了 cancellationSignal
,处理之;
调用 unstableProvider.query
方法,获取 qCursor
;
如果这时远程进程已结束(DeadObjectException),尝试获取 stableProvider
:调用 unstableProviderDied
释放相关引用,再尝试用 acquireProvider
方法获取 stableProvider
,最后调用其 query
方法获取 qCursor
;
如果之前没有获取 stableProvider
,获取之;
将 stableProvider
与 qCursor
包装到 CursorWrapperInner
对象,返回这个对象;
最后释放资源。
unstableProvider 与 stableProvider 在 ActivityThread 和 AMS 都有各自独立的引用计数,区别在于使用 unstableProvider 的客户端进程不会受到 ContentProvider 服务端进程死亡的牵连,而使用 stableProvider,服务端死亡将导致客户端进程也被杀死。
insert, update, delete
方法直接使用 stableProvider
。这一设计的目的是保证客户端与服务端的数据一致性。
获取 ContentProvider 过程
acquireUnstableProvider
& acquireProvider
1 2 3 4 5 6 7 8 public final IContentProvider acquireUnstableProvider (Uri uri) { ... String auth = uri.getAuthority(); if (auth != null ) { return acquireUnstableProvider(mContext, uri.getAuthority()); } return null ; }
acquireUnstableProvider(Uri)
方法检查 URI sheme,再调用 acquireUnstableProvider(Context, String)
方法。这个方法的实现在之前介绍的 ApplicationContentResolver 中,实际上是代理了 ActivityThread 中的 acquireProvider 方法。
android.app.ContextImpl
1 2 3 4 5 6 @Override protected IContentProvider acquireUnstableProvider (Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false ); }
acquireProvider(Uri)
方法的情况与 acquireUnstableProvider
的情况相似,唯一要注意的是调用 ActivityThread.acquireProvider
方法时最后一个参数为 true
而非 false
。
ActivityThread.acquireProvider
android.app.ActivityThread
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 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 ; try { synchronized (getGetProviderLock(auth, userId)) { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), c.getOpPackageName(), auth, userId, stable); } } catch (RemoteException ex) {...} if (holder == null ) {...} holder = installProvider(c, holder, holder.info, true , holder.noReleaseNeeded, stable); return holder.provider; }
尝试调用 acquireExistingProvider
获取已有的 ContentProvider,如果成功则直接返回;
获取锁,再调用 AMS 的 getContentProvider
方法获取 ContentProviderHolder 对象;
使用 installProvider
方法安装内容提供程序;
返回 ContentProviderHolder 的 provider
成员;
ActivityThread.acquireExistingProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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()) {...} ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null ) { incProviderRefLocked(prc, stable); } return provider; } }
以 auth
和 userId
为 key,从 ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
中查询已有的记录。
如果找到记录,获取对应的 Binder 对象,尝试获取并增加引用计数;
返回 ProviderClientRecord 的 mProvider
成员。
调用 incProviderPrefLocked
方法增加引用计数时会传入 stable
标志,因为需要 AMS 对 stableProvider 和 unstableProvider 做区别处理,所以需要分别计数。ActivityThread 在增加本地计数时,也会通知 AMS 增加那里的引用计数,以使两边计数保持同步。
注意不是所有 provider 都需要引用计数,因为有的系统内容提供程序从不需要被释放,也就完全没有引用计数来管理其生命周期。
ActivityManagerService.getContentProvider
这个方法调用 getContentProviderImpl
方法。
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 37 38 39 40 41 42 43 44 private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, int callingUid, String callingPackage, String callingTag, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; boolean providerRunning = false ; synchronized (this ) { ... ProcessRecord r = null ; if (caller != null ) { r = getRecordForAppLocked(caller); ... } boolean checkCrossUser = true ; ... cpr = mProviderMap.getProviderByName(name, userId); if (cpr == null && userId != UserHandle.USER_SYSTEM) { cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM); if (cpr != null ) { cpi = cpr.info; if (isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags) && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) { userId = UserHandle.USER_SYSTEM; checkCrossUser = false ; } else { cpr = null ; cpi = null ; } } } if (cpr != null && cpr.proc != null ) { providerRunning = !cpr.proc.killed; if (cpr.proc.killed && cpr.proc.killedByAm) {...} }
首先检查请求的内容提供程序是否已经发布并记录在 ProviderMap mProviderMap
中,注意这里的mProviderMap
是存在于 AMS 中的 ProviderMap 对象,与 ActivityThread 中的 mProviderMap
不是一个东西。如果当前用户中找不到,再从系统用户(UserId = 0)的内容提供程序中寻找合法的单例调用。找到的记录在 ContentProviderRecord cpr
中。
接下来检查这个记录对应的进程是否还在运行。如果是被 ActivityManager 所杀,就主动清理进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 if (providerRunning) { cpi = cpr.info; String msg; if (r != null && cpr.canRunHere(r)) { ...... ContentProviderHolder holder = cpr.newHolder(null ); holder.provider = null ; return holder; } ...... conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, stable); if (conn != null && (conn.stableCount+conn.unstableCount) == 1 ) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { mProcessList.updateLruProcessLocked(cpr.proc, false , null ); } } ...... }
如果内容提供程序对应进程正在运行,这里分两种情况处理:
这个 ContentProvider 能在调用者的进程中直接运行,就不建立调用者和当前运行进程的连接,而是使调用者启动自己的 ContentProvider 实例(通过设置 holder.provider = null
),向调用者返回 ContentProviderHolder。
不能在调用者进程运行,需要连接到服务端进程,就获取一个 ContentProviderConnection,同时增加提供程序的引用计数。如果这个连接的总引用计数为 1,需要更新 LruProcess 列表。最后还要调整 OOM Adj。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if (!providerRunning) { try { cpi = AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); } catch (RemoteException ex) { } if (cpi == null ) { return null ; } boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags) && isValidSingletonCall(r.uid, cpi.applicationInfo.uid); if (singleton) { userId = UserHandle.USER_SYSTEM; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); ......
如果对应进程没有在运行,首先利用 authorities 从 PackageManager 获取 ProviderInfo。处理提供者是系统单例提供者的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ComponentName comp = new ComponentName (cpi.packageName, cpi.name);cpr = mProviderMap.getProviderByClass(comp, userId); final boolean firstClass = cpr == null ;if (firstClass) { ... try { ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); if (ai == null ) { return null ; } ai = getAppInfoForUser(ai, userId); cpr = new ContentProviderRecord (this , cpi, ai, comp, singleton); } catch (RemoteException ex) { } finally {...} }
尝试调用 mProviderMap.getProviderByClass
方法获取 ContentProviderRecord。如果获取失败,说明这个 ContentProvider 是第一次运行。调用 PackageManager.getApplicationInfo
获取应用信息,创建 ContentProviderRecord。
1 2 3 4 5 6 7 if (r != null && cpr.canRunHere(r)) { return cpr.newHolder(null ); }
如果内容提供程序是多进程的,只需要返回一个新的 ContentProviderHolder,让调用者去初始化其余内容。
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 37 38 39 40 41 42 43 44 45 final int N = mLaunchingProviders.size();int i;for (i = 0 ; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break ; } } if (i >= N) { try { try { AppGlobals.getPackageManager().setPackageStoppedState( cpr.appInfo.packageName, false , userId); } catch (RemoteException e) { } catch (IllegalArgumentException e) {...} ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false ); if (proc != null && proc.thread != null && !proc.killed) { if (!proc.pubProviders.containsKey(cpi.name)) { proc.pubProviders.put(cpi.name, cpr); try { proc.thread.scheduleInstallProvider(cpi); } catch (RemoteException e) { } } } else { proc = startProcessLocked(cpi.processName, cpr.appInfo, false , 0 , new HostingRecord ("content provider" , new ComponentName (cpi.applicationInfo.packageName, cpi.name)), false , false , false ); if (proc == null ) {...} } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); } finally {...} }
尝试启动内容提供程序。
首先遍历 ArrayList<ContentProviderRecord> mLaunchingProviders
,检查之前是否已经安排启动了该内容提供程序。
如果没有找到,尝试启动之:
调用 PackageManager.setStoppedState
方法,使应用不能被停止;
尝试获取应用的 ProcessRecord,如果获取成功,调用其 ActivityThread.scheduleInstallProvider
方法;
如果获取失败,说明应用进程没有启动,调用 startProcessLocked
方法启动该进程
设置 cpr
的 launchingApp
变量,将其加入到 mLaunchingProviders
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, stable); if (conn != null ) { conn.waiting = true ; } }
如果是首次启动,向 mProviderMap
添加 ComponentName 记录;
向 mProviderMap
添加 authorities 的记录;
尝试获取 ContentProviderConnection,设定其 waiting
标志为 true。
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 ... final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT; boolean timedOut = false ; synchronized (cpr) { while (cpr.provider == null ) { if (cpr.launchingApp == null ) {... return null ;} try { final long wait = Math.max(0L , timeout - SystemClock.uptimeMillis()); ... if (conn != null ) { conn.waiting = true ; } cpr.wait(wait); if (cpr.provider == null ) { timedOut = true ; break ; } } catch (InterruptedException ex) { } finally { if (conn != null ) { conn.waiting = false ; } } } } if (timedOut) {... return null ;} return cpr.newHolder(conn); }
等待内容提供程序启动完成并发布。当前设置的超时时间 CONTENT_PROVIDER_WAIT_TIMEOUT = 20000
,单位毫秒,也就是 20 秒。cpr
将等待 cpr.provider
就绪,直到超时时间已过或 publishContentProviders
方法调用 notify
唤醒线程。如果超时时间已过,就返回 null
。
最后,cpr
以获取的 conn(可能为 null)创建 ContentProviderHolder 返回给调用者。
与 ContentProvider 交互过程
ContentResolver 与 ContentProvider 实现了相同的接口 ContentInterface,但 IPC 通信时调用的是 IContentProvider 这套接口。
ContentResolver 通过 acquireProvider
方法获取的 IContentProvider 接口,得到的实际上是 ContentProviderPorxy 对象。我们来看这个代理如何处理 insert
请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public Uri insert (String callingPkg, Uri url, ContentValues values) throws RemoteException{ Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); url.writeToParcel(data, 0 ); values.writeToParcel(data, 0 ); mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0 ); DatabaseUtils.readExceptionFromParcel(reply); Uri out = Uri.CREATOR.createFromParcel(reply); return out; } finally { data.recycle(); reply.recycle(); } }
代理对象把数据打包发送到服务端对象处理,在这里是 ContentProviderNative。
1 2 3 4 5 6 7 8 9 10 11 12 13 case INSERT_TRANSACTION:{ data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); Uri out = insert(callingPkg, url, values); reply.writeNoException(); Uri.writeToParcel(reply, out); return true ; }
收到数据后调用 IContentProvider 接口的 insert
方法,其实现位于 Transport 类。
1 2 3 4 5 6 7 8 public Uri insert (String callingPkg, Uri uri, ContentValues initialValues) { ...... try { return maybeAddUserId(mInterface.insert(uri, initialValues), userId); } catch (RemoteException e) { ... } finally {...} }
在这里调用了 ContentInterface mInterface
接口的 insert
方法。
1 2 3 4 5 6 7 8 public final void setTransportLoggingEnabled (boolean enabled) { if (enabled) { mTransport.mInterface = new LoggingContentInterface (getClass().getSimpleName(), this ); } else { mTransport.mInterface = this ; } }
Transport 类作为 ContentProvider 的成员 mTransport
实例化。ContentProvider 在初始化时,会设置其 mInterface
成员为自身(this
)。终于,服务端 ContentProvider 中的各种方法可以被客户端所使用。
ContentProvider 初始化过程
应用的内容提供程序初始化有两个入口。
应用进程已存在时,由 AMS 远程调用 ActivityThread 进行实例化;
应用进程启动时,进行批量安装;
AMS 发起的 ContentProvider 安装
1 2 3 public void scheduleInstallProvider (ProviderInfo provider) { sendMessage(H.INSTALL_PROVIDER, provider); }
1 2 3 4 case INSTALL_PROVIDER: handleInstallProvider((ProviderInfo) msg.obj); break ;
AMS 首先调用 ActivityThread.ApplicationThread.scheduleInstallProvider
这个方法向 ActivityThread 的 Handler 发送消息以安排 provider 的安装。Handler 收到消息后启动 handleInstallProvider
方法。这个方法进一步调用 installContentProviders
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 , true , true ); if (cph != null ) { cph.noReleaseNeeded = true ; results.add(cph); } } try { ActivityManager.getService().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) {...} }
这个方法遍历接收到的 ProviderInfo 列表,对每个 ProviderInfo 调用 installProvider
方法。随后,调用 AMS 的 publishContentProviders
方法,发布成功安装的 ContentProvider。
应用进程启动时的 ContentProvider 安装
进程启动完成时,会调用 AMS 的 attachApplication
方法,进一步调用 installContentProviders
方法,将清单声明在进程启动时安装的 ContentProvider 批量安装。
ActivityThread.installProvider
这一方法负责安装内容提供程序。根据官方文档,同一进程或来自 system server 的内容提供程序可以永久安装,因此设置 noReleaseNeeded
为 true。其他内容提供程序使用引用计数来管理其生命周期。这个方法还会检测当前内容提供程序是否已经安装。如果已经安装,就增加引用计数,返回已有的内容提供程序。
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 37 38 39 40 41 42 43 44 45 46 47 48 private ContentProviderHolder installProvider (Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null ; IContentProvider provider; if (holder == null || holder.provider == null ) { ... Context c = null ; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { } } if (c == null ) {...; return null ;} if (info.splitName != null ) { try { c = c.createContextForSplit(info.splitName); } catch (NameNotFoundException e) {...} } try { final java.lang.ClassLoader cl = c.getClassLoader(); LoadedApk packageInfo = peekPackageInfo(ai.packageName, true ); if (packageInfo == null ) { packageInfo = getSystemContext().mPackageInfo; } localProvider = packageInfo.getAppFactory() .instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); if (provider == null ) {...; return null ;} localProvider.attachInfo(c, info); } catch (java.lang.Exception e) {...; return null ;} } else { provider = holder.provider; ... }
这一节获取 IContentProvider。如果 holder.provider
不为空,则直接使用。否则:
获取 ContentProvider 所在的 Context 对象;
创建 ContentProvider 实例,获取对应的 IContentProvider 接口;
调用 ContentProvider.attachInfo
方法,该方法调用 onCreate
方法;
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 ) { ... provider = pr.mProvider; } else { holder = new ContentProviderHolder (info); holder.provider = provider; holder.noReleaseNeeded = true ; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null ) { ... if (!noReleaseNeeded) { incProviderRefLocked(prc, stable); try { ActivityManager.getService().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { } } } else { ProviderClientRecord client = installProviderAuthoritiesLocked( provider, localProvider, holder); if (noReleaseNeeded) { prc = new ProviderRefCount (holder, client, 1000 , 1000 ); } else { prc = stable ? new ProviderRefCount (holder, client, 1 , 0 ) : new ProviderRefCount (holder, client, 0 , 1 ); } mProviderRefCountMap.put(jBinder, prc); } retHolder = prc.holder; } } return retHolder; }
这一节获取 ContentProviderHolder。
如果 localProvider != null
,说明之前走了 holder.provider == null
的分支,获取了本地的 ContentProvider 实例,那么:
尝试从 mLocalProvidersByName
查询对应的 ProviderClientRecord,如果找到,则直接返回其 mProvider 成员;
如果没有找到,创建新的 ContentProviderHolder 对象,存入所需信息,调用installProviderAuthoritiesLocked
注册;
否则,说明引用了已有的 ContentProvider:
尝试从 mProviderRefCountMap
查询对应的 ProviderRefCount,如果找到,更新其引用计数;
如果没有找到,调用 installProviderAuthoritiesLocked
注册,创建新的引用计数,存入 mProviderRefCountMap
;
使用ProviderRefCount 的 holder
成员作为返回的对象。
最后返回获取的 ContentProviderHolder。
installProviderAuthoritiesLocked
创建新的 ProviderClientRecord 对象,将其保存到 ProviderMap mProviderMap
中。
AMS.publishContentProviders
这个方法将收到的 ContentProviderHolder 对应的 ContentProviderRecord 保存到 mProviderMap
中,完成其初始化,并调用 notifyAll
唤醒之前在 getContentProviderImpl
方法等待其就绪的线程。
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 void publishContentProviders (IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null ) { return ; } ... synchronized (this ) { final ProcessRecord r = getRecordForAppLocked(caller); if (r == null ) {throw new SecurityException (...);} ... 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 (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); }
首先获取调用者的 ProcessRecord。
接下来遍历 ContentProviderHolder 的列表,对于每一个 ContentProviderHolder:
获取 ProcessRecord 的 pubProviders
成员中对应的 ContentProviderRecord 到 dst
;
保存 dst
到 mProviderMap
;
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 int launchingCount = mLaunchingProviders.size(); int j; boolean wasInLaunchingProviders = false ; for (j = 0 ; j < launchingCount; j++) { if (mLaunchingProviders.get(j) == dst) { mLaunchingProviders.remove(j); wasInLaunchingProviders = true ; j--; launchingCount--; } } if (wasInLaunchingProviders) { mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } ... synchronized (dst) { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); } updateOomAdjLocked(r, true , OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }
检查 dst
是否在 mLaunchingProviders
中,如果是,就从该列表中去除,并按需从 AMS 的 Handler 中拆除发布内容提供程序超时的“定时炸弹”消息,这个“炸弹”是在 attachApplicationLocked
方法中安装的;
设置 dst
的 provider
和 proc
属性;
调用 notifyAll
唤醒之前等待该 ContentProviderRecord 就绪的线程
调整应用的 OOM Adj。
ContentProvider 服务端被杀牵连客户端的过程
在 AMS.attachApplicationLocked
方法中,添加了 ActivityThread 对象的死亡回调。
1 2 3 4 5 6 try { AppDeathRecipient adr = new AppDeathRecipient ( app, pid, thread); thread.asBinder().linkToDeath(adr, 0 ); app.deathRecipient = adr; } catch (RemoteException e) {...}
这个 AppDeathRecipient 的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 private final class AppDeathRecipient implements IBinder .DeathRecipient { final ProcessRecord mApp; final int mPid; final IApplicationThread mAppThread; ...... @Override public void binderDied () { ... synchronized (ActivityManagerService.this ) { appDiedLocked(mApp, mPid, mAppThread, true ); } } }
可以看到调用了 appDiedLocked
方法。继续追踪:
appDiedLocked -> handleAppDiedLocked -> cleanUpApplicationRecordLocked
cleanUpApplicationRecordLocked
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 ...... for (int i = app.pubProviders.size() - 1 ; i >= 0 ; i--) { ContentProviderRecord cpr = app.pubProviders.valueAt(i); final boolean always = app.bad || !allowRestart; boolean inLaunching = removeDyingProviderLocked(app, cpr, always); if ((inLaunching || always) && cpr.hasConnectionOrHandle()) { restart = true ; } cpr.provider = null ; cpr.setProcess(null ); } app.pubProviders.clear(); if (cleanupAppInLaunchingProvidersLocked(app, false )) { restart = true ; } if (!app.conProviders.isEmpty()) { for (int i = app.conProviders.size() - 1 ; i >= 0 ; i--) { ContentProviderConnection conn = app.conProviders.get(i); conn.provider.connections.remove(conn); stopAssociationLocked(app.uid, app.processName, conn.provider.uid, conn.provider.appInfo.longVersionCode, conn.provider.name, conn.provider.info.processName); } app.conProviders.clear(); } ......
首先移除已经发布的 ContentProvider,如有必要杀掉客户端进程;
清理正在启动且有客户端正在等待的 ContentProvider;
从关联的 ContentProviderConnection 中注销;
removeDyingProviderLocked
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 private final boolean removeDyingProviderLocked (ProcessRecord proc, ContentProviderRecord cpr, boolean always) { final boolean inLaunching = mLaunchingProviders.contains(cpr); if (!inLaunching || always) { synchronized (cpr) { cpr.launchingApp = null ; cpr.notifyAll(); } mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid)); String names[] = cpr.info.authority.split(";" ); for (int j = 0 ; j < names.length; j++) { mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid)); } } for (int i = cpr.connections.size() - 1 ; i >= 0 ; i--) { ContentProviderConnection conn = cpr.connections.get(i); if (conn.waiting) { if (inLaunching && !always) { continue ; } } ProcessRecord capp = conn.client; conn.dead = true ; if (conn.stableCount > 0 ) { if (!capp.isPersistent() && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { capp.kill(..., true ); } } else if (capp.thread != null && conn.provider.provider != null ) { try { capp.thread.unstableProviderDied(conn.provider.provider.asBinder()); } catch (RemoteException e) { } cpr.connections.remove(i); if (conn.client.conProviders.remove(conn)) { stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName); } } } if (inLaunching && always) { mLaunchingProviders.remove(cpr); } return inLaunching; }
如果这个内容提供程序当前正在运行,调用 notifyAll
(TODO),从 mProviderMap 移除记录;
遍历所有 ContentProviderConnection:
如果当前连接正在等待提供程序就绪,并且不是必须要移除连接的情况,就跳过处理该连接;
否则,获取当前连接的客户端程序 ProcessRecord,如果其 stableCount > 0
,不是常驻进程,且正在运行,就调用 ProcessRecord.kill
方法杀掉该进程;
否则(没有 stableConnection),调用 ApplicationThread.unstableProviderDied
方法,再从 ContentProviderRecord 中移除该连接;
按需移除 mLaunchingProviders
中相应记录,返回 inLaunching
状态给调用者。
Q&A
Q: ContentProviderConnection中stableCount与unstableCount的作用是什么?
A: stableCount
与 unstableCount
分别记录这个 ContentProviderConnection 中使用 stableProvider
与 unstableProvider
的个数。如果 stableCount > 0
,服务端进程死亡时,AMS 就会杀掉对应的客户端进程,以确保数据一致性。
参考文献
ContentProvider | Android 开发者, https://developer.android.com/guide/topics/providers/content-providers
Android之ContentProvider源码解析, https://juejin.im/post/5c789ebee51d453f0d4413a2
理解ContentProvider原理, http://gityuan.com/2016/07/30/content-provider/
ContentProvider流程解析, https://www.jianshu.com/p/f01357e6bb32
从源码角度看ContentProvider, https://juejin.im/post/5a0c611f6fb9a0450b65ee7c
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 frameworks/base/core/java/android/content/ContentProvider.java frameworks/base/core/java/android/content/ContentResolver.java frameworks/base/core/java/android/content/IContentProvider.java frameworks/base/core/java/android/content/pm/ProviderInfo.java frameworks/base/core/java/android/content/pm/PackageManager.java frameworks/base/core/java/android/app/ActivityThread.java frameworks/base/core/java/android/app/ContentProviderHolder.java frameworks/base/core/services/com/android/server/am/ActivityManagerService.java frameworks/base/core/services/com/android/server/am/ProviderMap.java frameworks/base/core/services/com/android/server/am/ContentProviderRecord.java frameworks/base/core/services/com/android/server/am/ContentProviderConnection.java frameworks/base/core/services/com/android/server/am/ProviderMap.java