Article Directory
- Chapter Eight Performance Optimization App Startup Optimization (2)
- (1) Start screen white screen / black screen resolution
- (2) Optimization of starting speed
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
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:
(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.
Analyze the trace file
. 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.