---
title: '四大組件 - ContentProvider'
disqus: kyleAlien
---
四大組件 - ContentProvider
===
## OverView of Content
[TOC]
## ContentProvider 基本使用
ContentProvider 基本使用如下,可以透過 ContextProvider 來操作其他應用進程內的內部資料 (像是 DB 之類的)
```java=
public class TestContentProvider extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = Uri.parse("content://com.alien.providers.GameInfo");
ContentValues values = new ContentValues();
values.put("id", 9521);
values.put("name", "HelloWorld");
values.put("phone", 123456789);
// @ 查看 getContentResolver 方法
this.getContentResolver().insert(uri, values);
}
}
```
## App 訪問指定 Provider
透過上述的 Context#getContentResolver 訪問指定 Provider
### Context 訪問 getContentResolver
* 從 Activity#getContentResolver 開始看,他會先訪問 ContextWrapper#getContentResolver,而 ContextWrapper 只是裝飾類,實作類是 [**ContextImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java)
```java=
// ContextWrapper.java
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
// -------------------------------------------------------
// ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
```
* mContentResolver 成員是在 ContextImpl 建構函數內創建,ApplicationContentResolver 是 ContextImpl 內的靜態內部類
```java=
// ContextImpl.java
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @NonNull ContextParams params,
@Nullable String attributionTag, @Nullable AttributionSource nextAttributionSource,
@Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
... 省略部分
// ApplicationContentResolver 是 ContextImpl 內的靜態內部類
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
// 繼承於 ContentResolver 類
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = Objects.requireNonNull(mainThread);
}
... 省略部分方法
}
```
> 
### ContentResolver 類 - query 方法
* ApplicationContentResolver 繼承於 [**ContentResolver 類**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/ContentResolver.java)
```java=
// ContentResolver.java
public abstract class ContentResolver implements ContentInterface {
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
// 檢查 Uri 不為 null
Objects.requireNonNull(uri, "uri");
... 省略部分
// @ 查看 acquireUnstableProvider 方法
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
...
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
... 省略部分
try {
// 調用 IContentProvider#query 方法
qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} /* catch */
... 省略部分
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
if (qCursor != null) {
qCursor.close();
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
}
```
* ContentResolver#**acquireUnstableProvider** 方法
1. 檢查 Uri Scheme 是否是 **content**、Authority 是否合法
:::info
* 這也就是我們在指定 Provider Uri 時為何要以 content 開頭
```java=
Uri uri = Uri.parse("content://com.alien.providers.GameInfo");
```
:::
2. 取得 authority 後傳入 acquireUnstableProvider 抽象方法,該方法由子類 ApplicationContentResolver 實現
```java=
// ContentResolver.java
public static final String SCHEME_CONTENT = "content";
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
// 抽象方法,由子類 ApplicationContentResolver 實現
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
// ----------------------------------------------------------
// ContextImpl.java
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = Objects.requireNonNull(mainThread);
}
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
// @ 呼叫 ActivityThread#acquireProvider 方法
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
}
```
* 藉由 [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java) 取得 IContentProvider 對象
1. 查看是否有 IContentProvider 緩存
2. 透過 ActivityManager 取得訪問 AMS 的代理類,並呼叫 AMS#getContentProvider 方法
:::danger
* 非 Local Provider ?
若目標 Provider 並非本地 Provider,就需要等待遠端 Provider 建立
:::
```java=
// ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 1. 查看是否有 IContentProvider 緩存
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) {
// 訪問 AMS#getContentProvider
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// 如果正常取得 holder,但沒有 provider,那代表 provider 並非本地
// 我們必須等待 provider 建立
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
holder = key.mHolder;
}
if (holder != null && holder.provider == null) {
// probably timed out
holder = null;
}
}
}
} /* 省略 catch、finally */
if (holder == null) {
...
return null;
}
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
```
> 
### [AMS](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) - 取得 ContentProvider 對象
* AMS#getContentProvider 會呼叫到 [**ContentProviderHelper**](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ContentProviderHelper.java)#getContentProvider 方法,最後我們分析的重點會放在 ContentProviderHelper#**getContentProviderImpl**
```java=
// ActivityManagerService.java
public ActivityManagerService(Injector injector, ServiceThread handlerThread) {
...
mCpHelper = new ContentProviderHelper(this, false);
}
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProvider: ", name);
try {
// @ 呼叫 ContentProviderHelper#getContentProvider
return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
// ----------------------------------------------------------------------
// ContentProviderHolder.java
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
mService.enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider " + name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
final int callingUid = Binder.getCallingUid();
if (callingPackage != null && mService.mAppOpsService.checkPackage(
callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
throw new SecurityException("Given calling package " + callingPackage
+ " does not match caller's uid " + callingUid);
}
// 分析 getContentProviderImpl 方法
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
```
* [**ContentProviderHelper**](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ContentProviderHelper.java)#getContentProviderImpl 方法
1. 檢查 Caller 進程是否正常運行
2. 透過傳入的 name 檢查是否已經有 AMS 是否已有 ContentProviderRecord
> name 就是 Uri 的 authority,也就是 provider 路徑
3. **透過 PKMS#resolveContentProvider** 找到相對的 **ProviderInfo**
:::success
PKMS 會在安裝時分析 AndroidManifest,所以會有 APK 的 provider 訊息
:::
4. 透過 ProviderInfo 創建 ComponentName
5. 查找 ProviderInfo 的進程,並根據不同狀況作相對行為:
5-1. Provider 所屬進程已經啟動,呼叫 IApplicationThread#**scheduleInstallProvider** 方法
> 
5-2. Provider 所屬進程未啟動,呼叫 AMS#**startProcessLocked** 方法,先啟動 Provider 進程
> 
```java=
// ContentProviderHolder.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
// 目標 ContextProvider
ContentProviderRecord cpr;
ProviderInfo cpi = null;
boolean providerRunning = false;
...
synchronized (mService) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord r = null;
// 1. 檢查呼叫者進程
if (caller != null) {
// 取得使用者進程
r = mService.getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid() + ") when getting content provider "
+ name);
}
}
// 2. 檢查目標 Provider 是否已有 (name 就是 Uri 的 authority)
cpr = mProviderMap.getProviderByName(name, userId);
// 如果沒有緩存,嘗試透過 USER_SYSTEM 取得
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
// 並驗證 provider 是否是 singleton
if (mService.isSingleton(
cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
&& mService.isValidSingletonCall(
r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
// 第一次啟動, Provider 並不存在
if (!providerRunning) {
try {
...
// 3. 透過 PKMS 取得對應的 ProviderInfo
cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.GET_URI_PERMISSION_PATTERNS,
userId);
...
} /* 省略 catch */
if (cpi == null) {
return null;
}
... 省略部分
// 4. 透過 ProviderInfo 創建 ComponentName
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
... 省略部分
// 這是單個進程,並且 APP 正在向該 Provider 進行連線,準備啟動該 Provider
final int numLaunchingProviders = mLaunchingProviders.size();
int i;
for (i = 0; i < numLaunchingProviders; i++) {
// 抓取正在啟動的 Provider
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// 如果 Provider 尚未準備啟動,那就讓它啟動
if (i >= numLaunchingProviders) {
final long origId = Binder.clearCallingIdentity();
try {
...
// 5. 查找 ProviderInfo 的進程
ProcessRecord proc = mService.getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid);
IApplicationThread thread;
// Provider 進程已經啟動
if (proc != null && (thread = proc.getThread()) != null
&& !proc.isKilled()) {
... debug 訊息
final ProcessProviderRecord pr = proc.mProviders;
if (!pr.hasProvider(cpi.name)) {
pr.installProvider(cpi.name, cpr);
try {
// 呼叫到 Provider 進程,要求該進程安裝 Provider
thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
// 先啟動 Provider 所在的進程
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
new HostingRecord("content provider",
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
// Provider 進程啟動失敗
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider " + name
+ ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
return cpr.newHolder(conn, false);
}
```
## App 啟動 Provider
以下分兩種情況,^1.^ Provider 進程尚未啟動、^2.^ Provider 已經啟動,接續啟動 Provider
### [AMS](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/am/ActivityManagerService.java) 啟動 Provider 所屬進程
* Provider 所屬進程尚未啟動,那會先呼叫 AMS#**startProcessLocked** 方法啟動指定進程
```java=
// ActivityManagerService.java
final ProcessList mProcessList;
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
boolean isolated) {
// @ 追蹤 startProcessLocked 方法
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
```
* ProcessList#startProcessLocked 是啟動新 Process 的入口,經過以下以系列的調用,最終會到達 Process#start 方法,接續 Process#start 請參考 [**Process.start - 啟動 ActivityThread**](https://hackmd.io/w-pdtemSTWeqAAc8PWzQzw?view#Processstart---%E5%95%9F%E5%8B%95-ActivityThread)
* 先跳過中間調用,這裡我們簡單知道順序是
1. [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java)#main 呼叫 attach 方法
2. 透過 AcitivtyManager 取得代理類,訪問 AMS#attachApplication 方法
```java=
// ActivityThread.java
public static void main(String[] args) {
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
// 1. 呼叫 attach 方法
thread.attach(false, startSeq);
...
Looper.loop();
}
private void attach(boolean system, long startSeq) {
...
if (!system) {
...
final IActivityManager mgr = ActivityManager.getService();
try {
// @ 透過 AcitivtyManager 取得代理類,訪問 AMS#attachApplication 方法
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
```
* AMS 中間的調用我們就不特別分析了 (上面有分析過很多案例),這邊我們直接跳轉到 APP 進程,我們的 **目的是要看 Provider 適合時被啟動的**
```java=
// ActivityThread.java
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public void handleMessage(Message msg) {
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
// @ 分析 handleBindApplication
handleBindApplication(data);
break;
}
}
private void handleBindApplication(AppBindData data) {
... 省略部分
Application app;
try {
app = data.info.makeApplication(data.restrictedBackupMode, null);
... 省略部分
if (!data.restrictedBackupMode) {
// 若不是 Backup 模式,則啟動該進程中所有的 Provider
if (!ArrayUtils.isEmpty(data.providers)) {
// installContentProviders 我們下個小節分析
installContentProviders(app, data.providers);
}
}
}
}
```
:::warning
* **若正在 backup 過程,就不啟動 Provider**
:::
> 
### 處理 Provider 創建 - scheduleInstallProvider
* Provider 所屬進程已經啟動,那 AMS 就會透過 Binder 機制呼叫到 [**ActivityThread**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java)#Application 的 **scheduleInstallProvider 方法**
* AMS 透過 scheduleInstallProvider 方法,傳遞給 App 進程,並透過 Handler 傳遞 `INSTALL_PROVIDER` 呼叫 handleInstallProvider 方法
```java=
// ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
private class ApplicationThread extends IApplicationThread.Stub {
@Override
public void scheduleInstallProvider(ProviderInfo provider) {
sendMessage(H.INSTALL_PROVIDER, provider);
}
}
class H extends Handler {
public static final int INSTALL_PROVIDER = 145;
public void handleMessage(Message msg) {
... 省略其他 case
switch (msg.what) {
case INSTALL_PROVIDER:
handleInstallProvider((ProviderInfo) msg.obj);
break;
}
}
}
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
installContentProviders(mInitialApplication, Arrays.asList(info));
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
}
```
* installContentProviders:
1. 透過 installProvider 方法創建 Provider 對象
2. 創建完 Provider 透過 AMS#publishContentProviders 通知 AMS Provider 創建完畢
```java=
// ActivityThread.java
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
// 所有要安裝的 Provider
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 回復 AMS#publishContentProviders 已經完成 Provider 安裝
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
```
> 
### LoadedApk 創建 Provider 對象
* 最終會透過 LoadedApk (其實是 AppComponentFactory 類) 創建 Provider 對象
```java=
// ActivityThread.java
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 尚未創建 Provider 對象
if (holder == null || holder.provider == null) {
ApplicationInfo ai = info.applicationInfo;
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
// 透過 LoadedApk#instantiateProvider 創建 ContentProvider 對象
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
localProvider.attachInfo(c, info);
} /* 省略 catch */
} else {
...
}
... 省略部分
return retHolder;
}
```
1. 透過 [**LoadedApk**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/LoadedApk.java) 創建 Provider 對象
```java=
// LoadedApk.java
private AppComponentFactory mAppComponentFactory;
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode, boolean registerPackage) {
mActivityThread = activityThread;
// Default ApplicationInfo
mApplicationInfo = new ApplicationInfo();
...
// 透過 createAppFactory 方法創建 Factory
mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
}
public AppComponentFactory getAppFactory() {
return mAppComponentFactory;
}
private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
// 若有自訂 Factory 則透過反射創建
if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
try {
return (AppComponentFactory)
cl.loadClass(appInfo.appComponentFactory).newInstance();
} /* 省略部分 */
}
// 預設 Factory
return AppComponentFactory.DEFAULT;
}
```
2. 預設工廠 [**AppComponentFactory**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/AppComponentFactory.java):透過 ClassLoader#loadClass 讀取指定 Class,並透過反射創建指定類
```java=
// AppComponentFactory.java
public class AppComponentFactory {
... 省略部分
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}
// 預設 Factory
public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Android Framework`