Android 8.0 源码分析 (七) ContentProvider 启动

前言

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

尚未关注的小伙伴,能够先关注一波,系列文章会持续更新。git

Android 8.0 源码分析 (一) SystemServer 进程启动github

Android 8.0 源码分析 (二) Launcher 启动缓存

Android 8.0 源码分析 (三) 应用程序进程建立到应用程序启动的过程app

Android 8.0 源码分析 (四) Activity 启动ide

Android 8.0 源码分析 (五) Service 启动函数

Android 8.0 源码分析 (六) BroadcastReceiver 启动oop

Android 8.0 源码分析 (七) ContentProvider 启动源码分析

介绍

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

源码分析

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 代码写的例子,若是感兴趣的能够经过我开源的 KotlinGithub APP 来进行学习 (ps: 在 11 月 10 号左右进行开源,能够先关注个人 GitHub,后续会更新地址)

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

经过上面代码示例想要 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 达到启动成功的通知。

到这里四大组件都已经讲解完成了,但愿这几篇文章能带给阅读者一些收获,感谢你们阅读。

推荐阅读

相关文章
相关标签/搜索