Android client performance optimization (without reservation and dedication from meizu senior engineers)
This article is the first original article published by degao in the embedded penguin circle, written by degao, Senior Android Development Engineer of meizu Technology Co., Ltd, without reservation, I would like to sum up and share my experience in optimizing the performance of Android clients leading meizu's development in multiple projects, which is of great practical value!
From now on, embedded penguin circle will be included in the previous five columns (Linux kernel driver scenario analysis, resource shortage SOC embedded architecture design, embedded cross tool chain and its applications, embedded design and programming, hardware platform and IOT solutions) solution) added the Android development column! For more information about Android, Linux, embedded, and IOT technologies, please pay attention to the public account: Embedded penguin circle.
As we all know, in addition to powerful functions, good performance is essential for a good product. According to a survey, nearly 90% of respondents unload apps due to poor performance, which is also the top cause of APP users' frustration.
What are the performance metrics of the Android client? How can I discover and locate client performance problems? Based on the development practices of multiple projects, this article provides important indicator projects to be concerned with, as well as general steps for locating and solving performance problems.
Performance Optimization should run through all the cycles of function development, rather than focusing on it once. Before each release, it is best to check whether the performance meets the standards.
Remember: Product = performance x function!
I. Performance Check items
1. startup speed
1) the startup speed here refers to the cold start speed, that is, the restart speed after the application is killed. This is mainly compared with your competing products.
2) You should not perform any time-consuming operations in the Application and Activity lifecycle callbacks. The specific metric is probably the total time you spent in onCreate, onResume, onStart and other Callbacks. It is best not to exceed 400 ms, otherwise, the user will feel stuck after clicking your application icon on the desktop.
2. interface switching
1) during application operations, the interface and animation should not be stuck obviously;
2) You can choose Settings> developer Options> debug GPU over-rendering on your phone, and then operate the application to check if the gpu is overdrawn for preliminary judgment;
3. Memory leakage
1) the back exit should not cause memory leakage. A simple check method is to run the 'adb shell dumpsys meminfo application package name' command to check whether 'activities view' is zero after exiting the application;
2) The memory usage 'Total' after multiple exits should not change too much;
4. onTrimMemory callback
1) The application responds to this callback to release non-essential memory;
2. Verify that you can run the command 'adb shelldumpsys gfxinfo application package name-cmd trim 5' and then run the command 'adb shell dumpsys meminfo application package name' to check the memory size.
5. Over-draw
1) enable the GPU over-drawing switch in the settings, and the over-drawing of each interface should not exceed 2.5x. That is to say, after enabling this debug switch, the interface is light-colored, especially complex, the red area should not exceed 1/4 of the full screen;
6. lint check:
1) Use Analyze-> Inspect Code in Android Studio to perform static scanning on the project Code, locate potential problematic codes, and modify them;
2) 0 error & 0 warning. If it cannot be solved, the cause must be given.
7. Reflection optimization:
1) Reduce reflection calls in the Code;
2) Cache the frequently called return values;
8. Stability:
1) The monkey should not crash for 48 consecutive hours. anr problems should occur.
2) If the application is connected to sdks for data tracking, such as Baidu statistics sdk and umeng statistics sdk, these sdks will report the application crash information back, developers should pay attention to these statistical crash logs every day and strictly control the application crash rate;
9. Power Consumption:
1) The application should not consume power abnormally after entering the background;
2) After the application is operated, exit the application and leave the application in the background. After a while, use 'adb shell dumpsysbatterystats' to check the power consumption log to see if an exception exists.
Ii. Common Causes of performance problems
Generally, performance problems are classified into three types:
1. Choppy UI and stability: users can directly perceive such problems and are most important;
2. Memory problems: memory problems are mainly caused by memory leakage or memory Jitter Caused by improper memory usage. If memory leakage exists, the application will continuously consume the memory, which may cause frequent gc to cause system freezing or OOM errors. Memory jitter may also cause UI freezing.
3. Power Consumption problems: it will affect the endurance, and the performance is caused by unnecessary auto-start. The system cannot sleep normally due to improper locking, and the system will frequently wake up after sleep;
Iii. Common Causes and analysis methods of choppy UI
The following describes the common causes of these problems and the general steps for analyzing these problems.
1. Common Causes of choppy videos
1) The UI thread gets stuck due to a slightly time-consuming operation on the UI thread;
2) Layout is too complex to complete rendering within 16 ms;
3) Excessive animation execution times at the same time, leading to excessive CPU or GPU load;
4) excessive View rendering, resulting in some pixels being drawn multiple times within the same frame time, so that the CPU or GPU load is too heavy;
5) View triggers measure and layout frequently, resulting in too much time consumption in measure and layout and frequent re-rendering of the entire View;
6) Frequent GC triggering by memory (frequent memory creation within the same frame), resulting in temporary blocking of rendering operations;
7) redundant resources and logic lead to slow loading and execution;
8) the worker thread priority is not set to Process. THREAD_PRIORITY_BACKGROUND. As a result, background threads can seize the cpu time slice of the UI thread and block rendering operations;
9) ANR;
2. The general steps to solve the problem of choppy analysis:
1) solve the problem of over-rendering
> Choose Settings> developer Options> debug GPU over-painting to enable debugging and check whether the corresponding interface has been over-drawn. If yes, resolve the problem first:
> Locate the transition drawing Area
> Use the tools provided by Android to confirm and modify the location (HierarchyView, Tracer for OpenGL ES)
> Locate the specific View (xml file or View)
> Analyze the cause of transition drawing using code and xml files
> Optimization Based on specific conditions
> Use Lint tools for further optimization
2) check whether the main thread has performed time-consuming operations:
StrictMode is a runtime detection mechanism provided by Android. It is used to detect some nonstandard operations during code execution, the most common scenario is to discover the IO operations of the main thread. Applications can use StrictMode to discover some code omissions as much as possible.
> Enable StrictMode:
> For applications, Android provides a best practice:
Android. app. application or android. app. the StrictMode can be enabled for the Activity life cycle. The onCreate () method is the best time. The earlier the StrictMode is enabled, the more code execution paths can be used to find illegal operations.
> Monitoring code
Public voidonCreate (){
If (cmd_mode ){
StrictMode. setThreadPolicy (newStrictMode. ThreadPolicy. Builder ()
. DetectAll (). penaltyLog (). build ());
StrictMode. setVmPolicy (new StrictMode. VmPolicy. Builder ()
. DetectAll (). penaltyLog (). build ());
}
Super. onCreate ();
}
If the main thread has network or disk read/write operations, there will be log output of the "D/StrictMode" tag in logcat to locate the time-consuming operation code.
3) if the main thread has no time-consuming operations and choppy operations, it is very likely that some logic that must be operated on the UI thread has problems, such as the control measure and layout time-consuming, etc, in this case, Traceview and systrace can be used for analysis.
4) Traceview: Traceview is mainly used for hotspot analysis to find out the most optimization points.
> Open DDMS, select a process, click the "Start Method Profiling" button above (the red dot turns black to Start running), and then operate our choppy UI, click "Stop Method Profiling" to open the following interface:
The figure shows the call relationship, number of calls, and time consumption ratio of each method during the Trace process. Through analysis, suspicious time-consuming functions can be found and optimized;
5) systrace: capture trace:
> Run the following command:
$ Cd android-sdk/platform-tools/systrace
$ Python systrace. py -- time = 10-o mynewtrace.html sched gfx view wm
> After you operate the appkey, A mynewtrace.html file will be generated and opened in Chrome:
> The figure is as follows:
By analyzing the figure above, we can find out the obvious layout, measure, and draw timeout problems.
6) import the following plug-in. You can add @ DebugLog to the method to print the time consumed by the method:
Build. gradle:
Buildscript {
Dependencies {
// Print plug-in for convenience of debugging performance problems. Add @ DebugLog to the access method to output the call parameters and execution time of the method;
Classpath 'com. jakewharton. hugo: hugo-plugin: 1.2.1'
}
}
// Print plug-in for convenience of debugging performance problems. Add @ DebugLog to the access method to output the call parameters and execution time of the method;
Apply plugin: 'com. jakewharton. hugo'
Java:
@ DebugLog
Public void test (int ){
Int B = a *;
}
Iv. Memory Performance Analysis and Optimization
1. Memory leakage
Currently, leakcanary can be basically used in projects, and the configuration is quite simple:
Build. gradle:
Dependencies {
DebugCompile 'com. squareup. leakcanary: leakcanary-android: 1.3.1 '// or 1.4-beta1
ReleaseCompile 'com. squareup. leakcanary: leakcanary-android-no-op: 1.3.1 '// or 1.4-beta1
TestCompile 'com. squareup. leakcanary: leakcanary-android-no-op: 1.3.1 '// or 1.4-beta1
}
Java:
Public class ExampleApplication extends Application {
@ Overridepublic void onCreate (){
Super. onCreate ();
LeakCanary. install (this );
}
}
Once a memory leak occurs, a notification will be generated in the notification bar. Click to view the leaked object and Reference Path:
2. Memory Jitter
If an object is allocated in the code that has been executed multiple times, such as onDraw or for loop, the gc count increases during the running process and the ui smoothness is affected. Generally, these problems can be detected through the lint tool.
5. Power Consumption Optimization suggestions
Power Optimization is mainly to ensure that the phone enters sleep, that is, to correctly apply for and release WakeLock, and not to wake up the phone frequently, that is, to use Alarm correctly.
6. Some good code practices
1. Use Service in a controlled manner
2. release memory when the interface is invisible
3. Release the memory when the memory is insufficient
4. Avoid wasting memory on Bitmap
For large images, first obtain the image size information, calculate the inSampleSize according to the actual display size, and finally decode;
Public static BitmapdecodeSampledBitmapFromFile (String filename,
Int reqWidth, int reqHeight ){
// First decode with inJustDecodeBounds = true to checkdimensions
Final BitmapFactory. Options options = newBitmapFactory. Options ();
Options. inJustDecodeBounds = true;
BitmapFactory. decodeFile (filename, options );
// Calculate inSampleSize
Options. inSampleSize =
ReqHeight );
CalculateInSampleSize (options,
ReqWidth,
// Decode bitmap with inSampleSize set
Options. inJustDecodeBounds = false;
Return BitmapFactory. decodeFile (filename, options );
}
Public static intcalculateInSampleSize (BitmapFactory. Options options,
Int reqWidth, int reqHeight ){
// Raw height and width of image
Final int height = options. outHeight;
Final int width = options. outWidth;
Int inSampleSize = 1;
If (height> reqHeight | width> reqWidth ){
If (width> height ){
InSampleSize = Math. round (float) height/(float) reqHeight );
} Else {
InSampleSize = Math. round (float) width/(float) reqWidth );
}
}
Return inSampleSize;
}
5. Use optimized data sets
6. Exercise caution when using abstract Programming
7. Avoid using the dependency injection framework whenever possible
Many dependency injection frameworks are based on reflection principles. Although code can look concise, they are compromising performance.
8. Use externallibraries with caution
9. Optimized Overall Performance
10. Use ProGuard to remove unnecessary code
Android {
BuildTypes {
Release {
MinifyEnabled true
ShrinkResources true
ProguardFiles getDefaultProguardFile('proguard-android.txt '), 'src/main/proguard-project.txt'
SigningConfig signingConfigs. debug
}
}
11. Use exceptions with caution. Exceptions are detrimental to performance.
To throw an exception, you must first create a new object. The constructor of the Throwable interface is named
The local method of fillInStackTrace (). The fillInStackTrace () method checks the stack and collects Call trace messages.
. As long as an exception is thrown, the VM must adjust the call stack because
New object.
Exceptions can only be used for error handling and should not be used to control program processes.
The following is a poor example:
Try {
StartActivity (intentA );
} Catch (){
StartActivity (intentB );
}
The following statement should be used to judge:
If (getPackageManager (). resolveActivity (intentA, 0 )! = Null)
Do not use the try/catch statement in recycling. Place it in the outermost layer and use System. arraycopy () instead of for loop replication.