Android APP性能优化之启动流程分析

1.背景

APP启动时长会很大影响用户的体验,对于产品的评价以及用户的心情都有不少的影响。对优路教育APP进行启动优化检测,通过相关检测,排查出启动耗时较长的逻辑,进行优化,想要进一步降低启动时间对用户的影响。

2.目标

对应用的启动流程进行分析,找到可以优化的地方,结合当前项目进行优化。

3.过程

在说应用的启动流程之前,得先了解一下Android系统的启动流程,因为Application的启动是离不开系统的处理的。

1.Android系统的启动过程分析

  1. BootLoader启动内核和init进程;
  2. init进程分裂出过个守护进程,如Android Debug Damon,USB Damon,这些守护进程会处理一些与硬件相关的接口;
  3. init进程启动一个Zygote进程Zygote进程初始化了第一个VM,并预加载了Framework和一些通用资源。
    zygote进程会开启一个Socket接口,用来监听请求。一旦收到请求,Zygote会基于自身预先加载的VM来孵化一个新的VM,并创建一个新的进程。
  4. 启动Zygote之后,init进程会启动Runtime进程。Zygote会孵化出一个超级管理进程-System Server。System Server 会启动所有系统核心的服务,如Activity Manager Service以及硬件相关的Service。
  5. 这个时候就开始准备启动它的第一个App进程-Home进程了。

Android系统已经启动完毕,一些核心的服务也已经启动完毕,然后启动Launcher应用。

2.App的启动过程分析

App进程什么时候被创建?
当一个应用调用另一个应用中的页面之时,如果目标进程不存在,则会创建一个新的进程并启动。
Application启动流程
先看下流程图
在这里插入图片描述

  1. 点击桌面Icon或者在前台应用中点击跳转另一个应用
  2. 然后调用StartActivity(Intent intent)方法;
    这个方法最终会通过Binder IPC的方式调用ActivityManagerService,这里简称AMS。
  3. AMS会执行以下操作:
    1. 最终会通过PackageManager的resolveIntent()方法收集这个Intent对象的指向信息(中间会经历很多类,方法的调用)。
    2. 通过grantUriPermissionLocked()方法验证用户是否具有足够的权限去调用目标Activity;
    3. 查询ProcessRecord是否存在
      如果不存在,AMS会创建新的进程来实例化目标Activity。

接下来就说App进程的创建过程了。

App进程的创建

  1. 调用startProcessLocked()方法创建新的进程通过上面所说的Socket通道传递参数给Zygote进程,Zygote进程孵化自身,并调用ZygoteInit.main()方法来实例化ActivityThread对象,并最终返回新进程的pid。
  2. ActivityThread依次调用Looper.prepare()和Looper.loop()方法来开启消息循环。

这个时候进程已经创建完毕,但是如何与应用自身的Application联系起来呢?

Application的绑定

  1. 调用ActivityThread中的bindApplication()方法发送一个BIND_APPLICATION的消息到消息队列中.
  2. 通过handleBindApplication()方法处理之前的绑定消息;
  3. 调用makeApplication()方法来加载Application的class 到内存中。

以下是部分源码片段
ActivityThread.java
sendMessage(H.BIND_APPLICATION, data);

@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
                                  ProviderInfoList providerList, 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, long[] disabledCompatChanges,
                                  SharedMemory serializedSystemFontMap) {
    
    
    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 = providerList.getList();
    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;
    data.disabledCompatChanges = disabledCompatChanges;
    data.mSerializedSystemFontMap = serializedSystemFontMap;
    sendMessage(H.BIND_APPLICATION, data);
}

handleMessage(msg)

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

    @UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
    
    
        ...
        try {
    
    
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            // Propagate autofill compat state
            app.setAutofillOptions(data.autofillOptions);

            // Propagate Content Capture options
            app.setContentCaptureOptions(data.contentCaptureOptions);
            sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);

            mInitialApplication = app;
            final boolean updateHttpProxy;
            synchronized (this) {
    
    
                updateHttpProxy = mUpdateHttpProxyOnBind;
                // This synchronized block ensures that any subsequent call to updateHttpProxy()
                // will see a non-null mInitialApplication.
            }
            if (updateHttpProxy) {
    
    
                ActivityThread.updateHttpProxy(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)) {
    
    
                    installContentProviders(app, data.providers);
                }
            }
            ...
        }
        
        ...
    }

注意其中的data.info.makeApplication(data.restrictedBackupMode, null)和installContentProviders(app, data.providers)
data.info类型为LoadedApk
LoadedApk.java

@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
                                   Instrumentation instrumentation) {
    
    
    if (mApplication != null) {
    
    
        return mApplication;
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

    Application app = null;

    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
    
    
        appClass = "android.app.Application";
    }

    try {
    
    
        final 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);
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
            false, false);
        for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
    
    
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
    
    
                continue;
            }

            rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
        }

        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // The network security config needs to be aware of multiple
        // applications in the same process to handle discrepancies
        NetworkSecurityConfigProvider.handleNewApplication(appContext);
        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
                + " package " + mPackageName + ": " + 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);
            }
        }
    }

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    return app;
}

其中mApplicationInfo.className为App自定义的Application,如果为空,就会使用默认的appClass = “android.app.Application”,initializeJavaContextClassLoader()则为初始化类加载器的逻辑调用,还可以继续跟踪其逻辑,可以发现sharedUserId设置为一样后,可以公用一个虚拟机的逻辑
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext)这个时候,可以看到是在Instrumentation中对Application的实例化

再看一些节选的代码
ActivityThread.java

mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

...
// Restore instance state
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState);

...
// Call postOnCreate()
mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState);

...
mInstrumentation.callActivityOnNewIntent(r.activity, intent);

...
 mInstrumentation.callActivityOnPause(r.activity);

...
mInstrumentation.callActivityOnDestroy(r.activity);

...
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

...
mInstrumentation.callApplicationOnCreate(app);


其中:callActivityOnCreate,callApplicationOnCreate,newActivity,callActivityOnNewIntent等在ActivityThread类中通过Instrumentation对application和activity的所有生命周期调用中,都会先调用Instrumentation的相应方法,去实现相应的功能,Instrumentation类中还有更对的相关方法。

继续进入Instrumentation.java

 static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
    
    
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

这里就是Application类创建时机
Application.java

final void attach(Context context) {
    
    
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

终于找到了我们最熟悉的attachBaseContext方法。
让我们再找一下ContentProvider的onCreate时机,先回到上述的ActivityThread.java的publishContentProviders方法,可以看到有个
installContentProviders方法在前面执行,查看该方法
ActivityThread.java

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

        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());
            }
            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();
        }
    }
private IActivityManager.ContentProviderHolder installProvider(Context context,IActivityManager.ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {
    
    
    ...
    final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                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.
                localProvider.attachInfo(c, info);
    ...

}

这里从ClassLoader里加载ContentProvider类实例,并调用attachInfo方法
让我们查看ContentProvider.java

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    
    
    ...
    ContentProvider.this.onCreate();
    ...
}

简单总结以上启动流程:

  1. ActivityThread.attath()
  2. AMS.attachApplication()
  3. ActivityThread.handleBindApplication()
  4. Application.attachBaseContext()
  5. ActivityThread.installContentProviders
  6. ContentProvider.onCreate()
  7. AMS.publishContentProviders()
  8. Application.onCreate()

所以对于App的启动优化,我们能够介入的要从Application.attachBaseContext()、ContentProvider.onCreate()、Application.onCreate()三个方面入手。

猜你喜欢

转载自blog.csdn.net/u011106915/article/details/124515690