從App啓動理解ContentProvider的建立

ActivityThread.main

咱們知道app的啓動是從ActivityThread.main方法開始的,因此咱們先從main看起java

public static void main(String[] args) {
     ...
    //建立Looper
    Looper.prepareMainLooper();
    //建立ActivityThread
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

   if (sMainThreadHandler == null) {
          sMainThreadHandler = thread.getHandler();
     }
    ...
    //開啓Looper循環
    Looper.loop();
    ...
    }
複製代碼

main方法裏主要作了三件事android

  • 建立主線程Looper,並開啓循環
  • 建立ActivityThread
  • 調用attach方法

ActivityThread.attach

接下來咱們看ActivityThread.attach方法bash

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            ...
        }
        ...
    }
    ...
}
複製代碼

attach方法裏主要作裏就是調用ActivityManager.getService() 方法返回IActivityManager 類型的 Binder 對象, 具體實現是在AMS裏實現attachApplicationapp

ActivityManagerService.attachApplication

public final void attachApplication(IApplicationThread thread) {
    ...
    attachApplicationLocked(thread, callingPid);
    ...
}

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);
    ...
    if (normalMode) {
        try {
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } catch (Exception e) {
            ...
        }
    }
    ...
}
複製代碼

能夠發現attachApplication實際調用了attachApplicationLocked,而在attachApplicationLocked裏又經過IApplicationThread (其類型是ApplicationThreadActivityThread的內部類,繼承自IApplicationThread.Stub) 的bindApplication方法async

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) {
    ...
    sendMessage(H.BIND_APPLICATION, data);
}
複製代碼
private void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}
複製代碼
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    if (DEBUG_MESSAGES) Slog.v(
        TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
        + ": " + arg1 + " / " + obj);
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}
複製代碼

bindApplication最終調用了mH.sendMessage方法,由於AMS經過ApplicationThread回調到咱們的進程,這也是一次跨進程過程,而ApplicationThread就是一個binder,回調邏輯是在binder線程池中完成的,因此須要經過Handler 將其切換到ui線程.因此這裏調用mh.sendMessage(mhActivityThread 的內部類 H 的一個實例)來通知處理BIND_APPLICATION事件ide

H.handleMessage

public void handleMessage(Message msg) {
    ...
    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;
        ...
    }
}
複製代碼

能夠看到Handler.H收到BIND_APPLICATION事件後實際調用來handleBindApplication方法來完成Application的建立以及ContentProvider的建立oop

ActivityThread.handleBindApplication

private void handleBindApplication(AppBindData data) {
     ....
     final InstrumentationInfo ii;
     ....
     
    if (ii != null) {
       //1.建立ContentImpl
       final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }

        //2.建立Instrumentation
      final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
        ....
    
        //3.建立Application對象
         Application app;
         app = data.info.makeApplication(data.restrictedBackupMode, null);

         // Propagate autofill compat state
            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);

            mInitialApplication = app;

        ...
        //4.啓動當前進程中的ContentProvider和調用其onCreate方法

        if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.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);
                }
            }

        //5.調用Application的onCreate方法
        try {
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
    }
 }
複製代碼
  • 3.1 LoadedApk.makeApplication Application的建立
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
        ...

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }
        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                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)) {
                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)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        ...

        return app;
}
複製代碼
  • 3.1.1 Instrumentation.newApplication
public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        app.attach(context);
        return app;
    }
複製代碼

AppComponentFactory.instantiateApplicationui

public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
            @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Application) cl.loadClass(className).newInstance();
    }
複製代碼

通過 上面的5個步驟就完成了Application的建立和啓動,能夠看到在第5步調用ApplicationonCreate方法以前,第4步進行了ContentProvicer的建立和調用其onCreate,這裏就解釋到了爲何能夠在ContentProvicer.onCreate裏初始化sdk,下面介紹下ContentProvicer的建立和調用其onCreatethis

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

        for (ProviderInfo cpi : providers) {
        
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
複製代碼

installContentProviders完成了ContentProvider的啓動,它首先遍歷當前進程的ProviderInfo列表並一一調用其installProvder方法來啓動它,接着將已啓動的ContentProvider發佈到AMS上,AMS會把它們存在ProviderMap裏,這樣外部調用者就能夠直接從AMS中獲取到ContentProviderspa

  • 4.1.1 ActivityThread.installProvider
private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ....

        final java.lang.ClassLoader cl = c.getClassLoader();
        LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
        if (packageInfo == null) {
            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;
        }

         localProvider.attachInfo(c, info);

        ....
}
複製代碼

這裏經過類加載器完成了ContentProvider的建立

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

        /*
         * 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.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); } ContentProvider.this.onCreate(); } } 複製代碼

installProvider方法裏除了完成了ContentProvider的建立還掉用了attachInfo,在改方法裏來調用它的onCreate方法。到此位置ContentProvider的建立已經完成,而且它的onCreate也被調用

相關文章
相關標籤/搜索