源码分析之ContentProvider 启动

作者: 不正经的创作者 | 来源:发表于2020-05-07 19:33 被阅读0次

前言

我们熟知一般 Android 工程师都是在应用层上开发,不会涉及系统源码,但是如果你想往底层发展,或者深入插件化、Framework 系统层等开发工作,如果不了解 Android 源码可是不行的,那么接下来我基于自己的理解跟学习来记录跟 Android 开发息息相关的源码分析,大概从 Android 中的 SystemServer 启动、四大组件启动、AMS、PMS 等几个维度来介绍,下面是我的计划,当然在未来也有可能改变。

介绍

ContentProvider (内容提供者) 属于四大组件之一,可以说它是在四大组件中开发者使用率最少的一个,它的作用就是进程间进行数据交互,底层采用 Binder 机制进行进程间通信。使用方式我这里就不在具体说明了,想要了解使用的可以参考 [Android:关于ContentProvider的知识都在这里了!(https://blog.csdn.net/carson_ho/article/details/76101093) 下面我们就以分析 ContentProvider 工作流程为主来进行全面分析。

源码分析

query 到 AMS 调用过程

下面先来看一个代码示例,代码如下:

    fun getContactsLists(): MutableList<String> {
        var contactsLists: MutableList<String> = mutableListOf<String>()
        lateinit var cursor: Cursor
        try {
        //使用 getContentResolver() 查询联系人列表
        cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)
        //对 cursor 进行遍历
        if (cursor != null) {
            while (cursor.moveToNext()) {
        //获取姓名
            val displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
        //电话号码
            val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
            contactsLists.add("姓名:$displayName 电话:$number")
            }
        }
    cursor.close()
        } catch (error: Exception) {
            logger.error("error:$error")
        }
        return contactsLists
    }

    //测试
    val contactsLists = getContactsLists()
    contactsLists.forEach { it -> println("通过ContentResolver获取联系人: $contactsLists") }

上面代码就是通过内容提供者来获取手机中的通讯录列表,输出为:

I/System.out: 通过ContentResolver获取联系人: [姓名:Xiao 电话:110, 姓名:ming 电话:112, 姓名:华为客服 电话:4008308300]

那么这一流程内部是怎么运行的,这才是我们这篇文章主要分析内容,有没有发现上面代码示例是用 kotlin 代码写的例子

老规矩,还是先看一下该小节分析流程,这里以时序图为主

通过上面代码示例想要 query 数据先要拿到 contentResolver 对象,通过父类 getContentResolver()方法获得,代码如下:

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

这里的 mBase 是 Context 对象,根据前面几篇文章我们知道,它的实现类就是 ContextImpl 我们直接看它具体实现,代码如下:

//ContextImpl.java
    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

这里的 getContentResolver 方法中返回了 ApplicationContentResolver 对象,它是 ContextImpl 的静态内部类,继承自 ContentResolver ,它在 ContextImpl 的构造方法中被创建,这说明当我们调用它的 query、insert、update 方法的时候,就会启动 ContentProvider,这里以上面我们示例 query 来进行分析,我们看它的具体实现,代码如下:

//ContentResolver.java
    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        /**
         * 1. 拿到 IContentProvider 对象,它是 ContentProvider 的本地代理
         */
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            
            try {
                /**
                 * 2. 调用 IContentProvider 的 query 函数来进行 query 
                 */
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                

            //...
            return wrapper;
        } catch (RemoteException e) {
            return null;
        } finally {
            ...
        }
    }

在注释处通过 acquireUnstableProvider 方法拿到 ContentProvider 的本地代理对象,然后注释 2 调用本地代理对象的 query 方法返回一个 Cursor 对象,我们先来看下注释 1 的 acquireUnstableProvider 方法怎么拿到 ContentProvider 本地代理对象,代码如下:

   //ContentResolver.java 
   public final IContentProvider acquireUnstableProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
            return null;
        }
        String auth = uri.getAuthority();
        if (auth != null) {
            //调用内部抽象方法
            return acquireUnstableProvider(mContext, uri.getAuthority());
        }
        return null;
    }

    /** @hide */
    protected abstract IContentProvider acquireUnstableProvider(Context c, String name);

通过上面代码 acquireUnstableProvider 返回的是一个抽象函数,具体实现交于子类实现,这里的子类也就是 ApplicationContentResolver 对象,我们看下它的具体实现,代码如下:

//ContextImpl.java
        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }

通过该方法内部 return 返回的对象,首先拿到 mMainThread 对象,然后调用它内部的 acquireProvider 方法,具体我们看下它的实现,代码如下:

//ActivityThread.java

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        /**
         * 1. acquireExistingProvider 方法主要检查 ActivityThread 全局变量 mProviderMap 中是否有目标 ContentProvider 存在,有就返回,没有就通过注释 2 处获取,
         */
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }


        ContentProviderHolder holder = null;
        try {
            /**
             * 2. 调用 IAcitivityManager 获取 ContentProviderHolder 对象
             */
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
      
            ...

        /**
         * 3. 用来安装 ContentProvider
         */
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

注释 1 处的 acquireExistingProvider 方法内部会检查 ActivityThread 的全局变量 mProviderMap 中是否有 ContentProvider 存在,如果有则返回,没有就调用注释 2 的 IActivityManager 的 getContentProvider 方法与 AMS 进行通信来获取,注释 3 是安装 ContentProvider,并将 ContentProvider 相关数据存储在 mProviderMap 中,起到缓存作用,这样使用相同的内容提供者时,就不需要每次调用 AMS 来获取了,AMS 的 getContentProvider 方法具体实现,代码如下:

//AMS.java
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        enforceNotIsolatedCaller("getContentProvider");
       ...
        //调动内部 getContentProviderImpl 方法
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
//AMS.java


    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;

        ...

                        /**
                         * 1. 获取目标 ContentProvider  应用程序进程的信息,如果进程已经启动就调用注释 2 ,否则调用注释 3 
                         */
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    /**
                                     * 2. 调用 IApplicationThread scheduleInstallProvider 函数
                                     */
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            /**
                             * 3. 启动新进程
                             */
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            checkTime(startTime, "getContentProviderImpl: after start process");
                            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);
                    }
                }

              ....
    }

上面的代码工作流程就是先判断 ContentProvider 所在的应用程序进程是否启动,如果启动就调用注释 2 ,如果没有启动就调用注释 3 ,因为前面 3 个组件都是直接走的应用程序进程启动的情况,那么最后这个组件我们就以应用程序进程没有启动的情况下,看怎么执行的。注释 2 我这里就大概概括下执行流程,首先调用 ActivityThread 的内部类 IApplication 的 scheduleInstallProvider 函数,然后通过 H sendMessage 通知进行安装 ContentProvider 。下面我们直接看注释 3 吧,代码如下:

//AMS.java


    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                null /* crashHandler */);
    }

下面创建进程代码分析我就不在继续跟了,最后会执行 ActivityThread 的 main 函数,这里启动进程源码执行流程可以看我另一篇文章 Android 8.0 源码分析 (三) 应用程序进程创建到应用程序启动的过程 ,我们直接看 ActivityThread main 函数,代码如下

//ActivityThread.java


    //通过反射调用执行的
    public static void main(String[] args) {
                ....
        //主线程消息循环
        Looper.prepareMainLooper();
        //创建 ActivityThread 对象
        ActivityThread thread = new ActivityThread();
        //Application,Activity 入口
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主要看 ActivityThread attach 函数具体实现,代码如下:

//ActivityThread.java
    private void attach(boolean system) {
     ...
       //1. 
     final IActivityManager mgr = ActivityManager.getService();
     try {
          //2. 关联 Application
          mgr.attachApplication(mAppThread);
     } catch (RemoteException ex) {
      throw ex.rethrowFromSystemServer();
     }
     ...
      
    }

注释 1 得到 AMS 的代理类 IActivityManager ,在注释 2 处调用 AMS 的 attachApplication 函数,并将 IApplicationThread 对象传入 AMS 保持应用进程和 AMS 跨进程通信,应用程序调用 AMS 的过程就分析完了,下面我们分析 AMS 到应用程序进程的 ContentProvider 安装过程。

AMS 启动 ContentProvider 的过程

首先的话看来看一下时序图:

上一小节我们知道,应用程序进程启动成功之后会通知 AMS 进程 调用 attachApplication 方法,代码如下:

//AMS.java

  private final boolean attachApplicationLocked(IApplicationThread thread,
              int pid) {
    ...
  
     thread.bindApplication(processName, appInfo, providers,
                          app.instr.mClass,
                          profilerInfo, app.instr.mArguments,
                          app.instr.mWatcher,
                          app.instr.mUiAutomationConnection, testMode,
                          mBinderTransactionTrackingEnabled, enableTrackAllocation,
                          isRestrictedBackupMode || !normalMode, app.persistent,
                          new Configuration(getGlobalConfiguration()), app.compat,
                          getCommonServicesLocked(app.isolated),
                          mCoreSettingsObserver.getCoreSettingsLocked(),
                          buildSerial);
    
    
    ...
  }

在 attachApplicationLocked 函数中调用了 thread.bindApplication 方法,thread 是 IApplicationThread ,这里和 IActivityManager 一样采用了 aidl 进行进程间传输数据,我们回到 ActivityThread 内部类 ApplicationThread 的 bindApplication 方法,代码如下:

//ActivityThread.java

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            //发送消息给 H 类
            sendMessage(H.BIND_APPLICATION, data);
        }

ActivityThread 内部类 H 收到消息,开始处理 BIND_APPLICSTION消息,代码如下:

//ActivityThread.java
...
  public void handleMessage(Message msg) {
  ...
   case BIND_APPLICATION:
    AppBindData data = (AppBindData)msg.obj;
    handleBindApplication(data);
                    
    break;    
  ...
}
...

调用内部 handleBindApplication 方法,代码如下:

  //ActivityThread.java

  private void handleBindApplication(AppBindData data) {
    ...
    
       /**
           * 1. 创建 ContentImpl
           */
          final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
  
    ...
    
       /**
                   * 2.
                   */
                  final ClassLoader cl = instrContext.getClassLoader();
                  mInstrumentation = (Instrumentation)
                      cl.loadClass(data.instrumentationName.getClassName()).newInstance();
              } catch (Exception e) {
                 ...
              }

              final ComponentName component = new ComponentName(ii.packageName, ii.name);
              /**
               * 3.
               */
              mInstrumentation.init(this, instrContext, appContext, component,
                      data.instrumentationWatcher, data.instrumentationUiAutomationConnection);

  ....
  
     try {

              /**
               * 4.
               */
              Application app = data.info.makeApplication(data.restrictedBackupMode, null);
              mInitialApplication = app;


              if (!data.restrictedBackupMode) {
                  if (!ArrayUtils.isEmpty(data.providers)) {
                      /**
                       * 5.
                       */
                      installContentProviders(app, data.providers);
                      mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                  }
              }


              try {
                  mInstrumentation.onCreate(data.instrumentationArgs);
              }
             catch (Exception e) {
            ...
              }

              try {
                  /**
                   * 6.
                   */
                  mInstrumentation.callApplicationOnCreate(app);
              } catch (Exception e) {
                 ...
              }
  }

上面代码我们按照我标注的注释来分析,注释 1 创建了 Context 的实现类 ContentImp了 这个类相信大家都很熟悉了吧,它就是四大组件中父类 Context 的实现类。注释 2 处通过反射创建 Instrumentation 并在注释 3 处初始化 Instrumentaion, 在注释 4 处创建 Application 并在注释 6 处调用 Application onCreate 生命周期方法,这说明 ContentProvider 已经在中间启动了,我们直接看上面注释 5 看 ContentProvider 是如何启动的,代码如下:

  //ActivityThread.java

        private void installContentProviders(
              Context context, List<ProviderInfo> providers) {
          final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        /**
         * 1.
         */
        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());
            }
            /**
             * 2.
             */
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            /**
             * 3.
             */
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

可以看到上面代码主要分为 3 个步骤,注释 1 遍历当前应用程序进程的 ProviderInfo 列表,得到每个 ContentProvicer 的存储信息,并在注释 2 调用 installProvider 方法来启动这些 ContentProvider .注释 3 处是将启动了的 ContentProvider 存入 AMS 的 mProviderMap 中,这个全局变量在上一小节介绍过,就是用来缓存启动过的 ContentProvidec,下面我们来看上面的注释 2 ,代码如下:

  //ActivityThread.java

   private ContentProviderHolder installProvider(Context context,
              ContentProviderHolder holder, ProviderInfo info,
              boolean noisy, boolean noReleaseNeeded, boolean stable) {
     
   
   ....
         try {
                  final java.lang.ClassLoader cl = c.getClassLoader();
                  /**
                   * 1.
                   */
                  localProvider = (ContentProvider)cl.
                      loadClass(info.name).newInstance();
                  provider = localProvider.getIContentProvider();
         
                  ...
               
                  /**
                   * 2.
                   */
                  localProvider.attachInfo(c, info);
              } catch (java.lang.Exception e) {
                  if (!mInstrumentation.onException(null, e)) {
                      throw new RuntimeException(
                              "Unable to get provider " + info.name
                              + ": " + e.toString(), e);
                  }
                  return null;
              }
   
     ....
     
   }

注释 1 通过反射实例化 ContentProvider 对象,并在注释 2 处调用它的 attachInfo 方法,代码如下:

  //ContentProvider.java
      public void attachInfo(Context context, ProviderInfo info) {
          attachInfo(context, info, false);
      }

      private void attachInfo(Context context, ProviderInfo info, boolean testing) {
          mNoPerms = testing;
  

          if (mContext == null) {
              mContext = context;
              if (context != null) {
                  mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                          Context.APP_OPS_SERVICE);
              }
              mMyUid = Process.myUid();
              if (info != null) {
                  setReadPermission(info.readPermission);
                  setWritePermission(info.writePermission);
                  setPathPermissions(info.pathPermissions);
                  mExported = info.exported;
                  mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
                  setAuthorities(info.authority);
              }
              /**
               * 安装成功,调用生命周期函数 onCreate
               */
              ContentProvider.this.onCreate();
          }
      }

      public abstract boolean onCreate();

可以看到最后在 ContentProvider 的 attachInfo 函数中进行调用了抽象方法 onCreate, 那么它的子类就会进行实现 onCreate 达到启动成功的通知。

相关文章

网友评论

    本文标题:源码分析之ContentProvider 启动

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