App Startup Optimization of Android Performance Optimization Series

Keywords: Android Fragment network xml

Reproduced in: http://blog.csdn.net/u012124438/article/details/56340949

Layout optimization of Android performance optimization series

In-memory optimization of Android performance optimization series

apk Slimming in Android Performance Optimizer Series

The slow start-up speed of apps is a problem we often encounter in the development process, such as black screen and white screen problems caused by slow start-up. This blog will introduce the relevant knowledge of App startup optimization.

Application Startup Mode

Generally speaking, there are two kinds of start-up modes: cold start and hot start.

1. Cold Start: When the application is started, the background does not have the process of the application. At this time, the system will create a new process to allocate to the application. This way of starting is cold start.

2. Hot Start: When the application is started, the background has the process of the application (e.g. pressing back and home keys, the application will exit, but the process of the application will remain in the background and can be viewed in the task list). Therefore, in the case of the existing process, this kind of start will start the application from the existing process, which is called hot start.

Startup process of App

The optimization referred to in this paper is for cold start. Simply explain the startup process of App:

1. Click Launcher to start the program and notify Activity Manager Service

2. Activity Manager Service notifies zyget process to hatch application process, allocate memory space, etc.

3. Execute the main() method of the application ActivityThread

4. The application notifies the Activity Manager Service that it has started, and the Activity Manager Service stores a proxy object for the application, through which the Activity Manager Service can control the application process.

5. Activity Manager Service notifies the application process to create an Active instance of the entry and execute its lifecycle

The lifecycle methods of Application and Entry Activity are invoked in the following order during startup:

1.Application Construction Method

2.attachBaseContext()

3.onCreate()

4. Object Construction of Entry Activity

5.setTheme() Setting Topics, etc.

6. onCreate() of Entry Activity

7. onStart() of Entry Activity

8. onResume() of Entry Activity

9. onAttachToWindow() of Entry Activity

10. onWindows FocusChanged () for Entry Activity

Start-up time statistics

In theory, when you call back to the onResume () method of the Entry Activity, App is officially launched, but in this sense it is not rigorous, because at this point in time.

In fact, only subject information and view tree are completed. The real process of view drawing, measure layout draw ing, does not exist, so it is not "visible" in the real sense. It's closer to the truth at the onWindows FocusChanged () callback. So we confirm that the startup time can be recorded from the Application's construction method, and then to onWindows FocusChanged () of the entry Activity for another time. The difference between the two is the startup time of App. Of course, this is a dumb method. The adb command has the corresponding record command: adb shell am start -W package name / full path name of the entry class.

adb shell am start -W [PackageName]/[PackageName.MainActivity]
  • 1
  • 1

After successful execution, three measured times will be returned:

There are three times involved, ThisTime, Total Time and WaitTime. WaitTime is a time-consuming call to the method startActivityAndWait, and ThisTime is the last Activity start time during the call to the end of the Activity's startActivityAndWait call. TotalTime refers to the start time of the first Activity in the invocation process to the end of the startActivity AndWait of the last Activity. If there is only one Activity in the process, Total Time It's equal to ThisTime.

Using TraceView to Analyse Startup Time

trace at the beginning and end of onCreate.

Debug.startMethodTracing("TestApp");
...
Debug.stopMethodTracing();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

Running the program generates a "TestApp.trace" file on the sdcard.
Note: You need to add write storage permissions to your program:

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

Export it locally through adb pull

adb pull /sdcard/TestApp.trace ~/testSpeed.trace
  • 1
  • 1

Open the DDMS analysis trace file and the following interface will appear

After expansion, most of them fall into the following two categories:

Parents: The parent method that calls this method
Children: A subclass method called by this method
If the method contains recursive calls, there may be two more categories:
Parents while recursive: The parent method involved in a recursive call
Children while recursive: A subclass method involved in recursive invocation

The data that developers are most concerned about are:
Important indicators: Calls + Recur Calls / Total, the most important indicators: Cpu Time / Call
Because we are most concerned about two points, one is that the number of calls is small, but each call takes a long time. This can be reflected in Cpu Time / Call. The other is functions that take up very little time but call very frequently. This can be reflected in Calls + Recur Calls / Total.

Then we can use this analysis to see which time-consuming operations are performed at startup, and then do the corresponding processing, such as not doing in the main thread, putting into the sub-thread, and some lazy loading processing, such as some applications have done the initialization of payment SDK, users will not pay once they open App, lazy loading.

Start page optimization

Usually when we develop App, we set up a startup page SplashActivity, then 2 or 3 seconds later, and SplashActivity can do some preloading of MainActivity data, and then need to pass through the intention to MainActivity.  
Advantages: Start up faster.
Disadvantage: Eventually, you have to go to the home page. When you go to the home page, the complex View rendering and business logic that must be executed in the UI thread still slow down the start-up speed. Start page is simple and fast to execute, home page is complex and slow to execute.

Idea: Can the View of the home page be loaded and the business logic of the home page be executed while the display of the page is started?

Optimizing direction:
Change SplashActivity to SplashFragment. The entry of the application is MainActivity. SplashFragment is displayed in MainActivity first. When SplashFragment is displayed, it is remove d. At the same time, the network data is cached in the friendly time of Plash Fragment 2S. After the window is loaded, we load the layout of activity_main, considering that this layout may be compared. Complex, delaying the analysis time of View, lazy loading in the form of ViewStub. So at the beginning, just load the layout shown by Splash Fragment and Ok.

Code:
MainActivity .Java

public class MainActivity extends FragmentActivity {

    private  MyHandler mHandler=new MyHandler(this);

    public static final String TAG="MainActivity";

    private ProgressBar mNetLoadingBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
        setContentView(R.layout.activity_main);
        final SplashFragment splashFragment = new SplashFragment();
        final ViewStub mainLayout = (ViewStub) findViewById(R.id.content_viewstub);
        //1. Show the startup page first
        FragmentManager supportFragmentManager = getSupportFragmentManager();
        if (supportFragmentManager != null) {
            FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();
            if (fragmentTransaction != null) {
                fragmentTransaction.replace(R.id.container, splashFragment);
                fragmentTransaction.commit();
            }
        }

        //2. If the homepage has time-consuming operations such as network, you can start now.
        new Thread(new Runnable() {
            @Override
            public void run() {
                //Time consuming 3500
                SystemClock.sleep(3000);
                mHandler.sendEmptyMessage(0);
            }
        }).start();

        //3. After rendering, load the homepage layout immediately
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG," getWindow().getDecorView().post");
                View mainView = mainLayout.inflate();
                initView(mainView);
            }
        });

        //4. There is animation on the start page. Delay a little, play the animation and remove it.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                mHandler.postDelayed(new DelayRunnableImpl(MainActivity.this, splashFragment), 2000);
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"onResume");
    }

    /**
     * Initialize Home View
     */
    private void initView(View pMainView) {
        if (pMainView != null) {
            mNetLoadingBar = (ProgressBar) pMainView.findViewById(R.id.progressbar);
            mNetLoadingBar.setVisibility(View.VISIBLE);
        }
    }


    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> wRef;

        private MyHandler(MainActivity pActivity) {
            this.wRef = new WeakReference<MainActivity>(pActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = wRef.get();
            if (mainActivity != null) {
                mainActivity.mNetLoadingBar.setVisibility(View.GONE);
            }
        }
    }

    private class DelayRunnableImpl implements Runnable {
        WeakReference<Context> contextWref;
        WeakReference<Fragment> fragmentWref;

        private DelayRunnableImpl(Context pContext, Fragment pFragment) {
            this.contextWref = new WeakReference<>(pContext);
            this.fragmentWref = new WeakReference<>(pFragment);
        }

        @Override
        public void run() {
            FragmentActivity context = (FragmentActivity) contextWref.get();
            if (context != null) {
                FragmentManager supportFragmentManager = context.getSupportFragmentManager();
                if (supportFragmentManager != null) {
                    FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();
                    SplashFragment fragment = (SplashFragment) fragmentWref.get();
                    if (fragment != null) {
                        fragmentTransaction.remove(fragment);
                        fragmentTransaction.commit();
                    }
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123

SplashFragment .java

public class SplashFragment extends Fragment {

    public SplashFragment() {

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View ret = inflater.inflate(R.layout.fragment_splash, container, false);
        initView(ret);
        return ret;
    }

    private void initView(View ret) {
        if (ret != null) {
            ImageView imageView = (ImageView) ret.findViewById(R.id.laucher_logo);
            playAnimator(imageView);
        }
    }

    private void playAnimator(ImageView pView) {
        if (pView != null) {
            PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat("alpha", 1f, 0.7f, 0.1f);
            //  PropertyValuesHolder pvhR= PropertyValuesHolder.ofFloat("rotationX", 0.0F, 360.0F);
            ObjectAnimator.ofPropertyValuesHolder(pView, pvhA).setDuration(2000).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

activity_mian.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="zhangwan.wj.com.myshare.activity.MainActivity">

    <ViewStub
        android:id="@+id/content_viewstub"
        android:layout="@layout/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

fragment.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="zhangwan.wj.com.myshare.fragment.SplashFragment">

    <ImageView
        android:id="@+id/laucher_logo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/launch" />

</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

After adopting the above methods, we can find that the starting speed has been greatly improved.

Start up some ideas of optimization

1. Avoid excessive rendering of page UI, reduce the time of repeated rendering of UI, turn on GPU excessive rendering switch in setting, the whole interface presents light color, especially complex interface, and the red area should not exceed one fourth of the full screen;
2. Whether all Shared Preferences in the main thread can be implemented in non-UI threads or not, the application function of Shared Preferences needs to be noticed, because Commit function blocks IO. Although this function executes quickly, the system will have another thread responsible for writing operations. When the application frequency is high, the thread will occupy more CPU resources. Similarly, there are statistical buried points, and so on. When the main thread buries but asynchronous threads submit, such problems will occur when the frequency is high.  
3. For the first black screen, for the "black screen" can be designed to replace A. 9 picture, indirectly reducing the waiting time of users.  
4. For the network error interface, friendly prompt interface, using ViewStub to reduce the pressure of one-time UI rendering.  
5. Load lazily in the following way

  getWindow().getDecorView().post(new Runnable() {
   @Override
   public void run() {
       myHandler.post(mLoadingRunnable);
    }
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6. The use of Multidex is also the culprit of slowing down the start-up speed. It must be optimized.

Posted by ScOrPi on Wed, 03 Apr 2019 16:48:30 -0700