美文网首页Android开发Android开发
ContentProvider初始化过程

ContentProvider初始化过程

作者: 巴黎没有摩天轮Li | 来源:发表于2021-01-12 23:14 被阅读0次

    前言

    记录一次电面的问题,ContentProvider中的onCreate()方法是在Application的onCreate()方法之前还是之后?根据这个问题我想屡一下ContentProvider的初始化过程。

    先说下结论:

    Application#onBaseContextAttach() -> ContentProvider#onCreate() -> Application#onCreate()

    源码跟进

    首先我们知道应用的主线程入口是在ActivityThread中的main()方法中开始执行的。

    public static void main(String[] args) {
        // 准备主线程的Looper
        Looper.prepareMainLooper();
        // 创建ActivityThread
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        // 死循环跑起来!
        Looper.loop();
    }
    
    private void attach(boolean system, long startSeq) {
        // ...省略部分代码
        // 1...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            // 2...
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
    

    代码1处 通过IBinder进程间通讯调用AMS,并且调用AMS中的attachApplication()方法。因此,我们来看下AMS中看下这个方法。

    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            // 1....
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }
    

    代码1处调用了attachApplicationLocked();

    @GuardedBy("this")
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        //... 省略超长代码
                if (app.isolatedEntryPoint != null) {
                    // This is an isolated process which should just call an entry point instead of
                    // being bound to an application.
                    thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
                } else if (instr2 != null) {
                    thread.bindApplication(processName, appInfo, providers,
                            instr2.mClass,
                            profilerInfo, instr2.mArguments,
                            instr2.mWatcher,
                            instr2.mUiAutomationConnection, testMode,
                            mBinderTransactionTrackingEnabled, enableTrackAllocation,
                            isRestrictedBackupMode || !normalMode, app.isPersistent(),
                            new Configuration(app.getWindowProcessController().getConfiguration()),
                            app.compat, getCommonServicesLocked(app.isolated),
                            mCoreSettingsObserver.getCoreSettingsLocked(),
                            buildSerial, autofillOptions, contentCaptureOptions);
                } else {
                    thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                            null, null, null, testMode,
                            mBinderTransactionTrackingEnabled, enableTrackAllocation,
                            isRestrictedBackupMode || !normalMode, app.isPersistent(),
                            new Configuration(app.getWindowProcessController().getConfiguration()),
                            app.compat, getCommonServicesLocked(app.isolated),
                            mCoreSettingsObserver.getCoreSettingsLocked(),
                            buildSerial, autofillOptions, contentCaptureOptions);
                }
    }
    

    attachApplication()方法特别长,不过我们仔细看到方法的尾部返现最终的实际绑定Applictaion的方法最终又回调到ActivityThread中进行处理了。我们回头看下bindApplication()方法。

    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, AutofillOptions autofillOptions,
            ContentCaptureOptions contentCaptureOptions) {
        if (services != null) {
            if (false) {
                // Test code to make sure the app could see the passed-in services.
                for (Object oname : services.keySet()) {
                    if (services.get(oname) == null) {
                        continue; // AM just passed in a null service.
                    }
                    String name = (String) oname;
                    // See b/79378449 about the following exemption.
                    switch (name) {
                        case "package":
                        case Context.WINDOW_SERVICE:
                            continue;
                    }
                    if (ServiceManager.getService(name) == null) {
                        Log.wtf(TAG, "Service " + name + " should be accessible by this app");
                    }
                }
            }
            // 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;
        data.autofillOptions = autofillOptions;
        data.contentCaptureOptions = contentCaptureOptions;
        // Handler发送消息 绑定Application之后得到的消息
        sendMessage(H.BIND_APPLICATION, data);
    }
    

    几经波折,我们回顾一下调用路径:
    ActivityThread#attach() -> AMS#attachApplication()->ActivityThread#bindApplication(),ok 接着往下看
    在bindApplication()方法中,使用Handler发送了一条message消息,BIND_APPLICATION,并且组装了一些必要的信息。

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            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()方法。Ok,到了重点部分,答案就几种于这个方法之中了。

    @UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
            // Allow disk access during application and provider setup. This could
            // block processing ordered broadcasts, but later processing would
            // probably end up doing the same disk access.
            Application app;
            final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
            final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
            try {
                // If the app is being launched for full backup or restore, bring it up in
                // a restricted environment with the base application class.
                // 1... 
                app = data.info.makeApplication(data.restrictedBackupMode, null);
    
                // Propagate autofill compat state
                app.setAutofillOptions(data.autofillOptions);
    
                // Propagate Content Capture options
                app.setContentCaptureOptions(data.contentCaptureOptions);
    
                mInitialApplication = app;
    
                // don't bring up providers in restricted mode; they may depend on the
                // app's custom Application class
                if (!data.restrictedBackupMode) {
                    if (!ArrayUtils.isEmpty(data.providers)) {
                        // 2...
                        installContentProviders(app, data.providers);
                    }
                }
    
                // 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 {
                    // 3...
                    mInstrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!mInstrumentation.onException(app, e)) {
                        throw new RuntimeException(
                          "Unable to create application " + app.getClass().getName()
                          + ": " + e.toString(), e);
                    }
                }
            } finally {
                // If the app targets < O-MR1, or doesn't change the thread policy
                // during startup, clobber the policy to maintain behavior of b/36951662
                if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                        || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
                    StrictMode.setThreadPolicy(savedPolicy);
                }
            }
    }
    

    1、2、3处代码分别证明了我们之前所说的最终的答案。我们再来走一下。

    LoadedApk

    @UnsupportedAppUsage
        public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
            if (mApplication != null) {
                return mApplication;
            }
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
            Application app = null;
            // 1...
            String appClass = mApplicationInfo.className;
            if (forceDefaultAppClass || (appClass == null)) {
                appClass = "android.app.Application";
            }
            try {
                // 2...
                java.lang.ClassLoader cl = getClassLoader();
                if (!mPackageName.equals("android")) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                            "initializeJavaContextClassLoader");
                    initializeJavaContextClassLoader();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                }
                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
                appContext.setOuterContext(app);
            } catch (Exception e) {
                if (!mActivityThread.mInstrumentation.onException(app, e)) {
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    throw new RuntimeException(
                        "Unable to instantiate application " + appClass
                        + ": " + e.toString(), e);
                }
            }
            mActivityThread.mAllApplications.add(app);
            mApplication = app;
            if (instrumentation != null) {
                try {
                    instrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!instrumentation.onException(app, e)) {
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                            + ": " + e.toString(), e);
                    }
                }
            }
            // Rewrite the R 'constants' for all library apks.
            SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
            final int N = packageIdentifiers.size();
            for (int i = 0; i < N; i++) {
                final int id = packageIdentifiers.keyAt(i);
                if (id == 0x01 || id == 0x7f) {
                    continue;
                }
                rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
            }
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            return app;
        }
    

    显然代码1处 会根据清单文件配置拿到Application名,若没有就默认android.app.Application,代码2处获取ClassLoader,最终在Instrumentation做类加载过程,我们看下:

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
          //1...
        Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
        // 2...
        app.attach(context);
        return app;
    }
    

    代码1处最终会调用,将application创建出来。

    Application app = (Application) cl.loadClass(className).newInstance();
    

    然后调用了Application#attach()方法。

    /**
     * @hide
     */
    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
    

    ok, Application#attachBaseContext()我们找到了,回顾一下调用链。
    ActivityThread#handleBindApplication() -> LoadedApk#makeApplication() -> Instrumentation#newApplication() -> Application#attach()
    回到handleBindApplication()方法中的代码2 我们来看下ContentProvider的创建过程。

    installContentProvider

    @UnsupportedAppUsage
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
          try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                Slog.e(TAG, "Failed to instantiate class " +
                      info.name + " from sourceDir " +
                      info.applicationInfo.sourceDir);
                return null;
            }
            if (DEBUG_PROVIDER) Slog.v(
                TAG, "Instantiating local provider " + info.name);
            // XXX Need to create the correct context for this provider.
            // 1.... 
            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;
        }
    }
    

    似曾相识的感觉,和Application创建有异曲同工之妙啊,也是拿到ClassLoader,执行针对ContentProvider的类加载,看下代码1处

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;
        mCallingPackage = new ThreadLocal<>();
        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null && mTransport != 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();
        }
    }
    

    这里强调一下哈,Application 与 ContentProvider 的加载器和Context类加载器是同一个。
    ok 到这里证明了,Application#attachBaseContext 之后调用了ContentProvider#onCreate().
    之后我们再回到handleBindApplication()的代码3处

    Instrumentation#callApplicationOnCreate()

    public void callApplicationOnCreate(Application app) {
        app.onCreate();
    }
    

    代码如此简单,直接调用了Application#onCreate()方法。
    至此,我们的ContentProvider 与 Application 生命周期的加载顺序大致的讲完了。

    相关文章

      网友评论

        本文标题:ContentProvider初始化过程

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