ContentProviderまとめ(上)

10572 ワード

概要
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メソッドになります.