美文网首页
ContentProvider总结(上)

ContentProvider总结(上)

作者: 队长只有一个 | 来源:发表于2016-12-11 20:33 被阅读0次
    简介

    ContentProvider是一种内容共享型组件,底层通过Binder和其他进程进行通信。ContentProvider一般是运行在独立的进程中的,每一个Content Provider在系统中只有一个实例存在,其它应用程序首先要找到这个实例,然后才能访问它的数据。ContentProvider采取懒加载的形式,即安装的时候并不会把ContentProvider加载到内存中来,而是会等到第一次使用的时候才加载,第二次使用的时候就直接返回了。

    过程分析

    要访问ContentProvider的数据,首先需要调用Context的getContentResolver()方法获取ContentResolver对象,从这里入手,我们找到ContextImpl的getContentResolver()方法实现如下:

     @Override
        public ContentResolver getContentResolver() {
            return mContentResolver;
        }
    

    追溯到ContentResolver 的初始化过程如下:

    mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    

    通过跟踪代码知道,ContentResolver是一个抽象类,我们使用的ApplicationContentResolver是这个抽象类的一个实现类。其中一个参数mainThread是是ActivityThread的一个实例。
      一个应用启动时,入口方法为ActivityThread的main方法,该方法是一个static方法,其中创建了ActivityThread的实例并创建了主线程的消息队列,而且调用了ActivityThread的attach方法。

            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();
    

    ActivityThread的attach()方法远程调用了AMS的 attachApplication()方法。参数mAppThread是一个ApplicationThread对象(Binder对象),该对象主要用于ActivityThread和AMS之间的通信。

               //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是ActivityThread的一个内部类。bindApplication最终会通过H类的实例mH切换到ActivityThread中去执行。

     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中,ActivityThread会创建Application对象并加载ContentProvider。由以下代码可知,ActivityThread是先加载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<ProviderInfo> 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自己不做任何事情,最终交由ActivityThread的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;
        }
    

    ActivityThread 会先通过acquireExistingProvider查询IContentProvider 是否存在,存在则直接返回。acquireExistingProvider源码如下:

                final ProviderKey key = new ProviderKey(auth, userId);
                final ProviderClientRecord pr = mProviderMap.get(key);
                if (pr == null) {
                    return null;
                }
    
                IContentProvider provider = pr.mProvider;
    

    如果目标ContentProvider未启动,ActivityThread会通过进程间通信让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);
             }
         }
    

    进程启动后,入口为ActivityThread的main方法。

    相关文章

      网友评论

          本文标题:ContentProvider总结(上)

          本文链接:https://www.haomeiwen.com/subject/enxpmttx.html