ContentProviderまとめ(上)
10572 ワード
概要
ContentProviderはコンテンツ共有型コンポーネントであり、下位層はBinderを介して他のプロセスと通信する.ContentProviderは一般的に独立したプロセスで実行され、各Content Providerはシステムに1つのインスタンスしか存在しません.他のアプリケーションはまずこのインスタンスを見つけてから、そのデータにアクセスすることができます.ContentProviderは、インストール時にContentProviderをメモリにロードするのではなく、最初の使用時にロードされ、2回目の使用時に直接戻ります.
プロセス分析
ContentProviderのデータにアクセスするには、まずContextのgetContentResolver()メソッドを呼び出してContentResolverオブジェクトを取得する必要があります.ここから、ContextImplのgetContentResolver()メソッドは次のように実装されています.
ContentResolverにドリルダウンする初期化手順は、次のとおりです.
トレースコードにより、ContentResolverは抽象クラスであり、私たちが使用しているアプリケーションContentResolverはこの抽象クラスの実装クラスであることがわかります.パラメータmainThreadの1つは、Activity Threadのインスタンスです.アプリケーションが起動すると、エントリメソッドは、Activity Threadのインスタンスが作成され、プライマリ・スレッドのメッセージ・キューが作成され、Activity Threadのattachメソッドが呼び出されるstaticメソッドであるActivity Threadのmainメソッドです.
Activity Threadのattach()メソッドは、AMSのattachApplication()メソッドをリモートで呼び出します.パラメータmAppThreadは、Activity ThreadとAMSとの間の通信に主に使用されるApplicationThreadオブジェクト(Binderオブジェクト)です.
AMSのattachApplication()メソッドは、プロセス間でApplicationThreadのbindApplicationメソッドを呼び出します.
ApplicationThreadはActivity Threadの内部クラスです.bindApplicationは最終的にHクラスのインスタンスmHからActivity Threadに切り替えて実行される.
handleBindApplicationでは、Activity ThreadがApplicationオブジェクトを作成し、ContentProviderをロードします.次のコードから分かるように、Activity ThreadはContentProviderをロードしてからApplicationを呼び出すonCreateメソッドです.
以上がContentProviderの起動過程であり,起動後,外部からContentProviderのデータを添削して調べることができる.ContentProviderが存在するプロセスが起動していない場合、初めてアクセスするとContentProviderの作成がトリガーされ、ContentProviderが存在するプロセスが引き上げられます.ContentResolverを呼び出すqueryから分析します.
ApplicationContentResolverのacquireUnstableProviderは自分で何もせず、最終的にActivity ThreadのacquireProviderに処理されます.
Activity Threadは、IContentProviderが存在するかどうかをacquireExistingProviderで問い合せ、存在する場合は直接返します.AcquireExistingProviderのソースコードは次のとおりです.
ターゲットContentProviderが起動していない場合、Activity Threadはプロセス間通信でAMSにContentProviderを起動させます.ContentProviderを起動するには、AMSのstartProcessLockedを介してプロセスを開始する必要があります.startProcessLockedは最終的にProcess.startに呼び出され、最後のタスクはZygoteに渡されます.
プロセスが開始されると、エントリはActivity Threadのmainメソッドになります.
ContentProviderはコンテンツ共有型コンポーネントであり、下位層はBinderを介して他のプロセスと通信する.ContentProviderは一般的に独立したプロセスで実行され、各Content Providerはシステムに1つのインスタンスしか存在しません.他のアプリケーションはまずこのインスタンスを見つけてから、そのデータにアクセスすることができます.ContentProviderは、インストール時にContentProviderをメモリにロードするのではなく、最初の使用時にロードされ、2回目の使用時に直接戻ります.
プロセス分析
ContentProviderのデータにアクセスするには、まずContextのgetContentResolver()メソッドを呼び出してContentResolverオブジェクトを取得する必要があります.ここから、ContextImplのgetContentResolver()メソッドは次のように実装されています.
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
ContentResolverにドリルダウンする初期化手順は、次のとおりです.
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
トレースコードにより、ContentResolverは抽象クラスであり、私たちが使用しているアプリケーションContentResolverはこの抽象クラスの実装クラスであることがわかります.パラメータmainThreadの1つは、Activity Threadのインスタンスです.アプリケーションが起動すると、エントリメソッドは、Activity Threadのインスタンスが作成され、プライマリ・スレッドのメッセージ・キューが作成され、Activity Threadのattachメソッドが呼び出されるstaticメソッドであるActivity Threadのmainメソッドです.
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
Activity Threadのattach()メソッドは、AMSのattachApplication()メソッドをリモートで呼び出します.パラメータmAppThreadは、Activity ThreadとAMSとの間の通信に主に使用されるApplicationThreadオブジェクト(Binderオブジェクト)です.
//AMS ActivityManagerNative IActivityManager
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
AMSのattachApplication()メソッドは、プロセス間でApplicationThreadのbindApplicationメソッドを呼び出します.
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
ApplicationThreadはActivity Threadの内部クラスです.bindApplicationは最終的にHクラスのインスタンスmHからActivity Threadに切り替えて実行される.
sendMessage(H.BIND_APPLICATION, data);
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
handleBindApplicationでは、Activity ThreadがApplicationオブジェクトを作成し、ContentProviderをロードします.次のコードから分かるように、Activity ThreadはContentProviderをロードしてからApplicationを呼び出すonCreateメソッドです.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
List providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
以上がContentProviderの起動過程であり,起動後,外部からContentProviderのデータを添削して調べることができる.ContentProviderが存在するプロセスが起動していない場合、初めてアクセスするとContentProviderの作成がトリガーされ、ContentProviderが存在するプロセスが引き上げられます.ContentResolverを呼び出すqueryから分析します.
//ContentResolver.java
IContentProvider unstableProvider = acquireUnstableProvider(uri);
......
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
//ApplicationContentResolver.java
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
}
ApplicationContentResolverのacquireUnstableProviderは自分で何もせず、最終的にActivity ThreadのacquireProviderに処理されます.
//ActivityThread
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;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// 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;
}
Activity Threadは、IContentProviderが存在するかどうかをacquireExistingProviderで問い合せ、存在する場合は直接返します.AcquireExistingProviderのソースコードは次のとおりです.
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
ターゲットContentProviderが起動していない場合、Activity Threadはプロセス間通信でAMSにContentProviderを起動させます.ContentProviderを起動するには、AMSのstartProcessLockedを介してプロセスを開始する必要があります.startProcessLockedは最終的にProcess.startに呼び出され、最後のタスクはZygoteに渡されます.
// Use existing process if already started
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
if (DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
} else {
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
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);
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
プロセスが開始されると、エントリはActivity Threadのmainメソッドになります.