Chapter Eight Performance Optimization App Startup Optimization (2)

Chapter Eight Performance Optimization App Startup Optimization (2)

(1) Start screen white screen / black screen resolution

1. Phenomenon

When you open the app, you will often pause for a while before entering the startup page (Splash)

2. Reason

In the onCreate () method of starting Acitivty, the system draws the form first, and then executes setContentView (R.layout.activity_splash). After the form is drawn, the layout resources have not been loaded, so the default background color is used.
If the theme uses Theme.AppCompat.Light (light color system), it displays a white splash screen, if it uses ThemeOverlay.AppCompat.Dark (dark color system), it displays a black splash screen.

3. Solve

Step 1: Set the startup image bg_splash as the background of the form, to avoid the appearance when the App is just started, black / white screen
Step 2: When set as the background bg_splash display, the background is responsible for loading resources, and download the advertising image, advertising image download Show the true appearance of SplashActivity when success or timeout
Step 3: Then enter MainAcitivity

       <style name="ThemeSplash" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:background">@mipmap/bg_splash</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        </style>

(2) Optimization of starting speed

1. Android Application startup process analysis

(1) App basic theory

Each Android App is in a separate space, which means that it runs in a separate process, has its own VM, and is assigned a unique user ID by the system. The
Android App consists of many different components, which can also start other App components. Therefore, the Android App does not have a main () method similar to the program entry. The
Android process is the same as the Linux process. By default, each apk runs in its own Linux process. In addition, there is only one in the default process. Thread-the main thread. There is a Looper instance in this main thread, and the message is retrieved from the Message queue by calling Looper.loop () for corresponding processing. The
process is started when it is needed. At any time, when the user or other components When you call any of the components in your apk, if your apk is not running, the system will create a new process for it and start it. Usually, this process will continue to run until it is killed by the system.

(2) App startup process

Insert picture description here
When the user clicks an App icon on the Home to start an application:
Click event will call startActivity (Intent), Launcer will use Binder IPC mechanism, and finally notify ActivityManagerService (AMS is a process of Android system, used to manage the operation of the four major components of the system Status) to start the Activity.
The Service will perform the following operations: The
first step: to collect the pointing information of this intent object through PackageManager's resolveIntent (). The pointing information is stored in an intent object. The
second step: verify whether the user has enough by grantUriPermissionLocked () Permission to call the Activity pointed to by the intent object.
If there is permission, ActivityManagerService will check and start the target activity in a new task
Step 3: Check if the ProcessRecord of this process exists. If it exists, start the activity directly, if ProcessRecord is null , ActivityManagerService will create a new process to instantiate the target activity
. Step 4: ActivityManagerService calls the startProcessLocked () method to create a new process. This method will pass parameters to the Zygote process through the socket channel mentioned above. Zygote incubates itself and calls ZygoteInit.main () method to instantiate the ActivityThread object and finally return the pid of the new process
ActivityThread then call Looper.prepareLoop () and Looper.loop () in turn to start the message loop
Step 5: Bind the process with the specified Application
Step 6: Call realStartActivity () in the existing process to start the Activity

2. App startup method

(1) Cold start

The App has not been started or the App process has been killed. The App process does not exist in the system. At this time, starting the App is a cold start. The
cold start process is the entire process of the App startup process described in Section 2. An App process needs to be created , Load related resources, start Main Thread, initialize the first screen Activity, etc.
During this process, the screen will display a blank window (the color is based on the theme) until the first screen Activity is fully started.
Cold start timeline:
Insert picture description here

(2) Hot start

Hot start means that your App process is only in the background, the system just takes it from the background to the foreground and shows it to the user.
Similar to the cold start, during this process, the screen will display a blank window (color based on theme), Until the activity is rendered.

(3) Warm start

Between cold start and hot start, generally occurs in the following two situations:
a. The user back quits the App, and then starts again. The App process may still be running, but the activity needs to be rebuilt.
B. The user quits the App After that, the system may kill the App due to memory, and both processes and activities need to be restarted, but the saved instance state can be restored in onCreate.

Through the description of the three startup states, we can see that the startup optimization we are going to do is actually for cold startup. Both hot and warm startup are relatively fast.

3. Reasons for slow App startup

According to the cold start time chart, we can see that for the App, we can control the start time line point nothing more than:
(3.1) Application onCreate
(3.2) the rendering of the first screen Activity
and our current App is integrated A lot of third-party services need to check advertisements, registration status, etc. at startup.A series of interfaces are done in onCreate of Application or onCreate of the first screen.

4. Case analysis

(1) Code analysis

Because this app integrates services such as Bugly, Push, Feedback and so on, Application onCreate has a lot of initialization work for third-party platforms:

public class GithubApplication extends MultiDexApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        // init logger.
        AppLog.init();

        // init crash helper
        CrashHelper.init(this);

        // init Push
        PushPlatform.init(this);

        // init Feedback
        FeedbackPlatform.init(this);

        // init Share
        SharePlatform.init(this);

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(GithubApplication.this, uri, imageView);
            }
        });
    }
}

(2) Using Traceview to analyze application onCreate takes time

Next, we combine our theoretical knowledge above and the introduced Traceview tool to analyze the time-consuming Application onCreate. Mark
trace at the beginning and end of onCreate.

Debug.startMethodTracing("GithubApp");
...
Debug.stopMethodTracing();

Running the program will generate a "GithubApp.trace" file on the sdcard.

Note: You need to add the permission to write storage to the program:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Export it to local via adb pull

adb pull /sdcard/GithubApp.trace ~/temp

Open the DDMS analysis trace file.
ddms_open_trace
Analyze the trace file
Insert picture description here
. Click "Real Time / Call" in the method area below, and sort the time in descending order according to the method call. It
takes more than 500ms to pay attention.
Looking at the method name on the left, you can see The time-consuming big user is the initialization method of several major platforms we use, especially Bugly, it also loads the native lib, operates with ZipFile, etc.
Click each method, you can see its parent method (call it) and all its child methods (It is called). When you
click a method, the execution time axis of the method above will flash, you can see the execution thread and the relative duration of the method.

(3) OnCreate optimization of Application

Put the third-party SDK initialization in a separate thread. Here an InitializeService IntentService is used to do the initialization work. (IntentService is different from Service, it works in the background thread.)
InitializeService.java code is as follows:

public class InitializeService extends IntentService {

    private static final String ACTION_INIT_WHEN_APP_CREATE = "com.anly.githubapp.service.action.INIT";

    public InitializeService() {
        super("InitializeService");
    }

    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
                performInit();
            }
        }
    }

    private void performInit() {
        AppLog.d("performInit begin:" + System.currentTimeMillis());

        // init Drawer image loader
        DrawerImageLoader.init(new AbstractDrawerImageLoader() {
            @Override
            public void set(ImageView imageView, Uri uri, Drawable placeholder) {
                ImageLoader.loadWithCircle(getApplicationContext(), uri, imageView);
            }
        });

        // init crash helper
        CrashHelper.init(this.getApplicationContext());

        // init Push
        PushPlatform.init(this.getApplicationContext());

        // init Feedback
        FeedbackPlatform.init(this.getApplication());

        // init Share
        SharePlatform.init(this.getApplicationContext());

        AppLog.d("performInit end:" + System.currentTimeMillis());
    }
}

Change the onCreate of GithubApplication to:

public class GithubApplication extends MultiDexApplication {

    @Override
    public void onCreate() {
        super.onCreate();

        // init logger.
        AppLog.init();

        InitializeService.start(this);
    }
}

(4) Startup interface optimization

Step 1: Create a theme with background

<style name="SplashTheme" parent="AppTheme">
    <item name="android:windowBackground">@drawable/logo_splash</item>
</style>

Step 2: Use an Activity that does not render the layout as the startup screen and add the theme

public class LogoSplashActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 注意, 这里并没有setContentView, 单纯只是用来跳转到相应的Activity.
        // 目的是减少首屏渲染
        
        if (AppPref.isFirstRunning(this)) {
            IntroduceActivity.launch(this);
        }
        else {
            MainActivity.launch(this);
        }
        finish();
    }
}
<activity
  android:name=".ui.module.main.LogoSplashActivity"
  android:screenOrientation="portrait"
  android:theme="@style/SplashTheme">
  <intent-filter>
      <action android:name="android.intent.action.MAIN"/>
      <category android:name="android.intent.category.LAUNCHER"/>
  </intent-filter>
</activity>

5. Summary

(1) Do not do too much in Application onCreate.
(2) The first screen Activity should be simplified as much as possible.
(3) Make good use of performance analysis tools for analysis.

Published 74 original articles · won 15 · views 6255

Guess you like

Origin blog.csdn.net/qq_29966203/article/details/90473664