Comprehensive Summary of Four Aspects of Android App Performance Optimization

Comprehensive Summary of Four Aspects of Android App Performance Optimization

Hot Article Guide | Click Title to Read

Lessons Learned After 10 Years of Work, If Only I Knew My Position Would Be Two Levels Higher

Awesome! Complete Source Code for 74 Apps!

A 300,000 Annual Salary Android Interview Guide, with Answers

Author: Slash Allen

Original Article: http://www.jianshu.com/p/9755da0f4e8f

When it comes to Android system phones, most people’s impression is that they become a bit sluggish after a period of use, and some programs inexplicably crash during operation. When opening the system folder, you find many extra files, and after constantly cleaning and optimizing with a phone manager app, the running speed feels slightly improved. Even if the phone scores high in various performance benchmarking software, it still feels that no matter how much memory space there is, it is far from enough. I believe every Android user has had similar experiences. Indeed, the Android system is not as smooth as the iOS system. Why is that? Clearly, when looking at the hardware configuration of the phone, Android devices do not lose to iOS devices and may even be stronger. The key lies in the software. The reasons for this phenomenon are multifaceted, and here are a few points listed simply:

  • In recent years, with continuous iterations of Android versions, the Android system provided by Google has become increasingly smooth. The latest version released is Android Oreo (8.0). However, most users in China use Android phones that are customized versions from various manufacturers, often not the latest native system kernel, and the majority are still stuck on Android 5.0, with even fewer on Android 6.0 and above, resulting in delays in updates.

  • Since the Android system source code is open, anyone who follows the corresponding protocol can modify the source code. Domestic manufacturers have transformed the Android source code into their own systems for external release, such as the familiar Xiaomi MIUI system, Huawei EMUI system, Oppo ColorOS system, etc. Since each manufacturer has modified the Android native system source code, this leads to the well-known Android fragmentation problem, which essentially means different Android systems have different application compatibility and cannot achieve consistency.

  • Due to various Android fragmentation and compatibility issues, Android developers need to adapt applications for different systems during development. At the same time, the development levels of each Android developer vary, and the performance of the applications they write also has different types of problems, leading to different user experiences. Some issues may be interpreted by users as problems with the Android system, which in turn affects the evaluation of Android phones.

Performance Optimization

Today, the focus is on Android APP performance optimization, which refers to the points that should be noted when developing applications and how to better improve user experience. A good application should not only have attractive features and interactions but also have high performance requirements. Instant applications may attract some users in the early stages of the product, but if the user experience is poor, it can lead to a bad reputation for the product. So how should a good application be defined? It mainly has the following three aspects:

  • Business/Functionality

  • Logical Interaction

  • Excellent Performance

As we all know, the Android system, as an operating system primarily for mobile devices, has certain hardware configuration limitations. Although configurations are becoming more advanced, they still cannot be compared to PCs. When using the CPU and memory inefficiently or consuming too many resources, issues such as insufficient memory leading to stability problems and excessive CPU consumption leading to sluggishness can occur.

When facing problems, everyone thinks of contacting users and checking logs. However, little do they know that feedback regarding performance issues is also very difficult to locate, as logs are mostly of little use. Why? Because performance issues are mostly non-deterministic, making it hard to reproduce the problem, and without key logs, it is naturally impossible to find the cause. These issues greatly affect user experience and functionality, so understanding some performance optimization solutions becomes very important, allowing us to optimize our applications in actual projects and thus improve user experience.

Four Aspects

User experience performance issues can be mainly summarized into four categories:

  • Smoothness

  • Stability

  • Power Saving and Data Saving

  • Small Installation Package

The main reasons for performance issues can be similar or different, but fundamentally, they boil down to memory usage, code efficiency, appropriate strategy logic, code quality, and installation package size. They can be organized and categorized as follows:

Comprehensive Summary of Four Aspects of Android App Performance Optimization

From the diagram, we can see that creating a high-quality application should target four directions: Fast, Stable, Economical, and Small.

  • Fast: Avoid stuttering during use, respond quickly, reduce user waiting time, and meet user expectations.

  • Stable: Reduce crash rates and ANR rates, avoiding crashes and unresponsiveness during user operation.

  • Economical: Save data and power, reduce user costs, and avoid causing the phone to overheat during use.

  • Small: A smaller installation package can lower the installation cost for users.

To achieve these four goals, the specific implementation involves the issues on the right side: stuttering, unreasonable memory usage, poor code quality, chaotic code logic, and oversized installation packages. These are also the most common issues encountered during the development process. While fulfilling business requirements, these aspects also need to be considered. More time should be spent thinking about how to avoid optimization after functionality is completed; otherwise, the maintenance costs will increase after functionality is implemented.

Stuttering Optimization

Slow Android application startup and frequent stuttering during use greatly affect user experience and should be avoided as much as possible. There are many scenarios where stuttering occurs, which can be categorized into four types: UI rendering, application startup, page transitions, and event responses, as shown in the diagram:

Comprehensive Summary of Four Aspects of Android App Performance Optimization

The fundamental reasons for these four types of stuttering can be divided into two major categories:

  • UI Rendering. The main reasons include deep rendering hierarchy, complex pages, and unreasonable refreshing, leading to stuttering more often occurring in UI and the initial screen after startup, as well as during page transitions.

  • Data Processing. The reasons for stuttering in these scenarios include excessive data processing volume, generally divided into three situations: data being processed on the UI thread, high CPU usage during data processing preventing the main thread from getting a time slice, and increased memory leading to frequent GC, thus causing stuttering.

There are many reasons for stuttering, but regardless of the cause and scenario, it ultimately reaches the user through the display on the device screen. The root cause is display issues. Therefore, to solve stuttering, one must first understand the display principles of the Android system.

Android System Display Principles

The Android display process can be simply summarized as follows: Android applications cache data after measurement, layout, and rendering, and render the data onto the display screen through SurfaceFlinger, refreshing the data via Android’s refresh mechanism. In other words, the application layer is responsible for drawing, and the system layer is responsible for rendering, passing the data that the application layer needs to draw to the system layer service through inter-process communication.

As we know, there are three core steps in the rendering of each View in Android: Measure, Layout, and Draw. The specific implementation begins execution from the performTraversals() method of the ViewRootImp class. Measure and Layout both recursively obtain the size and position of the View, prioritizing depth, meaning the deeper the hierarchy and the more elements, the longer the time taken.

To actually render the data that needs to be displayed onto the screen, this is accomplished through the SurfaceFlinger service in the system-level process. What exactly does the SurfaceFlinger service do? As follows:

  • Respond to client events and create a Layer to establish a connection with the client’s Surface.

  • Receive client data and attributes, modifying Layer attributes such as size, color, transparency, etc.

  • Refresh the created Layer content onto the screen.

  • Maintain the sequence of Layers and perform clipping calculations on the final output of Layers.

Since they are two different processes, a cross-process communication mechanism is needed to achieve data transmission. In the Android display system, Android’s anonymous shared memory is used: SharedClient. Each application and SurfaceFlinger creates a SharedClient, and within each SharedClient, a maximum of 31 SharedBufferStacks can be created, with each Surface corresponding to a SharedBufferStack, i.e., a Window.

A SharedClient corresponds to an Android application, and an Android application may contain multiple windows, i.e., Surfaces. This means that SharedClient contains a collection of SharedBufferStacks, and the display refresh mechanism utilizes double-buffering and triple-buffering technology. In summary, the overall display process is divided into three modules: application layer drawing to the cache, SurfaceFlinger rendering the cached data to the screen. Since they are different processes, Android’s anonymous shared memory SharedClient is used to cache the data that needs to be displayed to achieve this purpose.

In addition, we need to know about a term: FPS. FPS refers to the number of frames transmitted per second. Ideally, 60 FPS means no stuttering, indicating that each drawing duration should be within 16ms. However, the Android system may not be able to complete complex page rendering operations in a timely manner. The Android system issues a VSYNC signal every 16ms, triggering UI rendering. If every rendering is successful, this can achieve the required 60 FPS for a smooth picture. If an operation takes 24ms, when the system receives the VSYNC signal, it cannot perform normal rendering, leading to frame drops. Thus, what users see within 32ms will be the same frame, a phenomenon that is common during animations or scrolling lists, and may also be due to overly complex layouts with too many overlapping drawing units that cannot complete rendering within 16ms, ultimately causing refresh delays.

Fundamental Causes of Stuttering

Based on the display principles of the Android system, the fundamental reasons affecting rendering are as follows:

  • Heavy rendering tasks, where rendering a frame takes too long.

  • The main thread is too busy; when the VSYNC signal from the system arrives, the data is not ready, leading to frame drops.

There are tools available to help locate issues where rendering takes too long. The main thread needs to be cautious, as its key responsibilities include handling user interactions, drawing pixels on the screen, and loading and displaying related data. Therefore, any tasks in the main thread should be avoided as much as possible so that the application can maintain immediate responses to user operations. In summary, the main thread primarily performs the following tasks:

  • UI lifecycle control

  • System event handling

  • Message processing

  • UI layout

  • UI rendering

  • UI refreshing

In addition, any other processing should be avoided in the main thread, especially complex data calculations and network requests.

Performance Analysis Tools

Performance issues are not easy to reproduce or locate, but when problems do arise, they still need to be resolved. Analyzing and confirming whether issues have been resolved requires corresponding debugging tools, such as viewing Layout hierarchy through Hierarchy View, the GPU Profile tool built into the Android system, and static code inspection tool Lint, etc. These tools play a very important role in performance optimization, so it is essential to be familiar with them and know which tools to use for analysis in what scenarios.

1. Profile GPU Rendering

In developer mode on the phone, there is a stuttering detection tool called: Profile GPU Rendering, as shown:

Comprehensive Summary of Four Aspects of Android App Performance Optimization

Its features are as follows:

  • A graphical monitoring tool that can reflect the current rendering time in real-time

  • The horizontal axis represents time, and the vertical axis represents the time taken for each frame

  • As time progresses, the refresh appears from left to right

  • Provides a standard duration; if it exceeds the standard duration, it indicates that the current frame is lost

2. TraceView

TraceView is a tool that comes with the Android SDK, used to analyze function call processes. It can perform performance analysis on both Android applications and Framework layer code. It is a graphical tool that ultimately produces a chart to explain performance analysis, allowing analysis of the execution time of each method, including statistics on the number of method calls, recursion counts, actual duration, and other parameters, making it very intuitive and convenient for performance analysis.

3. Systrace UI Performance Analysis

Systrace is a performance data sampling and analysis tool provided in Android 4.1 and above. It returns some information from the system’s perspective. It helps developers collect runtime information from key subsystems of Android, such as Surfaceflinger, WindowManagerService, and other key modules, services, and View systems, allowing developers to analyze system bottlenecks more intuitively and improve performance. Systrace’s functions include tracking system I/O operations, kernel work queues, CPU load, etc., providing good data for UI display performance analysis, especially for issues like animation playback being unsmooth and rendering stutters.

Optimization Suggestions

1. Layout Optimization

The rationality of the layout mainly affects the measurement time of the page display. We know that the display measurement and rendering process of a page are completed through recursion. The time for multi-branch tree traversal is related to the height of the tree (h), with a time complexity of O(h). If the hierarchy is too deep, each additional layer will increase the display time of the page, so the rationality of the layout becomes very important.

So what methods are there for layout optimization? Primarily, it can be approached from three aspects: reducing hierarchy, reducing measurement and rendering time, and improving reusability. Summarized as follows:

  • Reduce hierarchy. Use RelativeLayout and LinearLayout reasonably, and use Merge appropriately.

  • Improve display speed. Use ViewStub, which is an invisible view object that does not occupy layout space and consumes very little resources.

  • Layout reuse. You can improve reusability through the tag.

  • Avoid using wrap_content as much as possible. wrap_content increases the calculation cost during layout measurement; when width and height are known to be fixed values, do not use wrap_content.

  • Remove unnecessary attributes from controls.

2. Avoid Overdrawing

Overdrawing refers to a pixel on the screen being drawn multiple times within the same frame. In a multi-layer overlapping UI structure, if invisible UIs are also performing drawing operations, it will lead to certain pixel areas being drawn multiple times, wasting excess CPU and GPU resources.

How to avoid overdrawing? As follows:

  • Layout optimization. Remove unnecessary backgrounds in XML, remove default backgrounds from Windows, and display placeholder background images as needed.

  • Custom View optimization. Use canvas.clipRect() to help the system identify visible areas, ensuring that only the areas within this region are drawn.

3. Startup Optimization

By monitoring startup speed, identify issues affecting startup speed, optimize startup logic, and improve application startup speed. Startup mainly involves three tasks: UI layout, rendering, and data preparation. Therefore, startup speed optimization requires optimizing these three processes:

  • UI layout. Applications generally have splash pages; optimizing the UI layout of the splash page can be done using Profile GPU Rendering to detect frame drops.

  • Startup loading logic optimization. Strategies such as distributed loading, asynchronous loading, and delayed loading can be adopted to improve application startup speed.

  • Data preparation. Analyze data initialization; loading data can consider strategies such as thread initialization.

4. Reasonable Refresh Mechanism

During application development, due to data changes, refreshing the page to display new data is necessary. However, frequent refreshes increase resource overhead and may lead to stuttering. Therefore, a reasonable refresh mechanism is needed to improve overall UI smoothness. Reasonable refreshing needs to pay attention to the following points:

  • Minimize the number of refreshes.

  • Avoid having high CPU threads running in the background.

  • Narrow the refresh area.

5. Others

When implementing animation effects, it is necessary to choose the appropriate animation framework based on different scenarios. In some cases, hardware acceleration can be used to provide smoothness.

Memory Optimization

In the Android system, there is a garbage memory recycling mechanism that automatically allocates and releases memory at the virtual machine layer. Therefore, there is no need to allocate and release a certain block of memory in the code, making it less likely to encounter memory leaks and memory overflow issues from the application level. However, memory management is still required. The Android system has a Generational Heap Memory model for memory management. Most of the pressure for memory recycling does not need to be a concern at the application layer. The Generational Heap Memory has its own management mechanism, and when memory reaches a threshold, the system automatically releases memory that it deems can be freed according to different rules. Because the Android program entrusts memory control to the Generational Heap Memory, once memory leaks and overflow issues arise, troubleshooting errors can become exceptionally challenging. In addition, some Android application developers do not pay special attention to reasonable memory usage during development and do not optimize memory significantly. As applications run more tasks simultaneously and business requirements become increasingly complex, completely relying on Android’s memory management mechanism can lead to a series of performance issues gradually emerging, significantly affecting application stability and performance. Therefore, solving memory issues and optimizing memory reasonably is very necessary.

Android Memory Management Mechanism

Android applications run on the Android virtual machine, and memory allocation and garbage collection for applications are handled by the virtual machine. In the Android system, the virtual machine operates in two modes: Dalvik and ART.

1. Java Object Lifecycle

Comprehensive Summary of Four Aspects of Android App Performance Optimization

Generally, Java objects in the virtual machine go through seven stages:

Creation Stage -> Application Stage -> Invisible Stage -> Unreachable Stage -> Collection Stage -> Finalization Stage -> Object Space Reallocation Stage

2. Memory Allocation

In the Android system, memory allocation is essentially about allocating and releasing heap memory. When an Android program starts, the application process is derived from a process called Zygote. After the Zygote process starts, to launch a new application process, the system derives a new process from the Zygote process, then loads and runs the application code in the new process. Most of the RAM pages are allocated to Framework code, allowing RAM resources to be shared among all application processes.

However, for the sake of overall memory control, the Android system imposes a hard limit on the maximum Dalvik Heap Size for each application. This threshold varies across different devices due to differences in RAM sizes. If the memory space occupied by the application approaches the entire threshold, attempting to allocate memory may easily lead to memory overflow errors.

3. Memory Recycling Mechanism

We need to know that in Java, memory is divided into three areas: Young Generation, Old Generation, and Permanent Generation. Recently allocated objects are stored in the Young Generation area. Objects trigger GC garbage collection at certain times, and those that are not collected may be moved to the Old Generation according to different rules, eventually accumulating over time in the Permanent Generation area. The system executes different GC operations based on the different types of memory data in memory. GC determines whether to collect an object by checking if it is referenced by active objects, thereby dynamically reclaiming the memory space occupied by unreferenced objects. However, it is essential to note that frequent GC can increase application stuttering, affecting the application’s smoothness. Therefore, efforts should be made to minimize system GC behavior to enhance application smoothness and reduce the likelihood of stuttering.

Memory Analysis Tools

Before performing memory optimization, it is necessary to understand the current memory usage status of the application, analyzing which data types have issues, how the distribution of various types looks, and how to identify specific objects causing problems after discovering issues. This requires relevant tools to assist us.

1. Memory Monitor

Memory Monitor is a straightforward graphical tool that can effectively monitor the memory usage of the system or application, with the following main functions:

  • Displays available and used memory, reflecting memory allocation and recycling status in real-time over time.

  • Quickly determine whether the application is running slowly due to excessive memory recycling.

  • Quickly determine whether the application crashes due to insufficient memory.

2. Heap Viewer

Heap Viewer primarily functions to observe the usage of different data types in memory, allowing visibility into the current heap size of the process, what types of data are present, and the proportion of each type. By analyzing this data, one can identify large memory objects, further analyze these large objects, and optimize to reduce memory overhead. It can also help discover memory leaks through changes in data.

3. Allocation Tracker

While Memory Monitor and Heap Viewer can intuitively and in real-time monitor memory usage and discover memory issues, they cannot further identify the reasons for the issues or distinguish whether a block of memory is normal. Moreover, after discovering a problem, they cannot locate the specific class and method causing the issue. This is when another memory analysis tool, Allocation Tracker, is needed for more detailed analysis. Allocation Tracker can track and record the memory allocation of an application and list their call stacks, allowing one to view the memory allocation cycles of all objects.

4. Memory Analyzer Tool (MAT)

MAT is a fast, feature-rich Java heap analysis tool that analyzes memory snapshots (HPROF) of Java processes, quickly calculating the size of objects occupying memory and identifying which objects cannot be reclaimed by the garbage collector. It provides a visual view of the objects that may be causing this result.

Common Memory Leak Scenarios

If memory leaks occur, finding and fixing the reasons afterward increases development costs. It is best to consider memory issues while writing code to produce higher quality code. Here are some common memory leak scenarios to avoid in future development:

  • Resource objects not closed. For example, Cursor, File files, etc., often use some buffers and should be closed in a timely manner when not in use.

  • Registered objects not unregistered. For example, if an event is registered but not unregistered, it will lead to objects being maintained in the observer list.

  • Static variables of classes holding large data objects.

  • Static instances of non-static inner classes.

  • Temporary memory leaks from Handlers. If a Handler is non-static, it may lead to the Activity or Service not being reclaimed.

  • Memory leaks caused by uncleaned objects in containers.

  • WebView. WebView has memory leak issues; once used in an application, memory will not be released.

In addition, memory leaks can be monitored. A common tool is the LeakCanary third-party library, which is an open-source library that detects memory leaks, is easy to use, and can alert when a memory leak occurs while generating a leak trace to analyze the leak location. It can also provide a Dump file for analysis.

Optimizing Memory Space

Having no memory leaks does not mean that memory does not need optimization. On mobile devices, due to limited physical storage space, the Android system allocates limited heap memory for each application process. Therefore, using minimal memory objects or resources can reduce memory overhead while allowing GC to reclaim no longer needed objects more efficiently, keeping the application’s heap memory adequately available for stable and efficient operation. Common practices include:

  • Object references. Use strong references, soft references, weak references, and phantom references according to business needs.

  • Reduce unnecessary memory overhead. Be cautious of auto-boxing, increase memory reuse, and effectively utilize system-provided resources, view reuse, object pools, and Bitmap object reuse.

  • Use optimal data types. For example, use ArrayMap data structures for data class container structures, avoid using enumeration types, use caching like LRU cache, etc.

  • Image memory optimization. Set bitmap specifications, compress based on sampling factors, and manage images using various image caching methods.

Stability Optimization

The definition of Android application stability is broad, and many factors can affect stability, such as unreasonable memory usage, insufficient consideration of exceptional code scenarios, and illogical code, all of which can impact application stability. The two most common scenarios are: Crash and ANR, which will render the program unusable. Common solutions include:

  • Improve code quality. For example, code reviews during development, examining code design logic, business rationality, etc.

  • Static code scanning tools. Common tools include Android Lint, Findbugs, Checkstyle, PMD, etc.

  • Crash monitoring. Timely record crash information and exception information for subsequent analysis and resolution.

  • Crash upload mechanism. After a crash, try to save logs locally first, then upload log information when the network is stable.

Power Consumption Optimization

In mobile devices, the importance of batteries cannot be overstated; without power, nothing can be accomplished. For operating systems and device manufacturers, power consumption optimization has never ceased, aiming for longer standby times. For an application, it is an issue that cannot be ignored, especially for those classified as “battery killers”; the ultimate result is uninstallation. Therefore, application developers need to minimize power consumption while fulfilling requirements.

Before Android 5.0, testing power consumption within applications was complicated and inaccurate. After 5.0, a dedicated API for obtaining power consumption information on devices was introduced: Battery Historian. Battery Historian is a power analysis tool provided by Google for the Android system, similar to Systrace, it is a graphical data analysis tool that visually displays the power consumption process of a phone. By inputting power analysis files, it shows consumption status and offers some reference methods for power optimization.

In addition, there are some common solutions available:

  • Calculation optimization, avoiding floating-point operations, etc.

  • Avoid improper use of WakeLock.

  • Use Job Scheduler.

Installation Package Size Optimization

The size of the application installation package does not affect application usage, but the larger the installation package, the higher the user download threshold, especially in mobile network situations. Users have higher requirements for installation package size when downloading applications. Therefore, reducing installation package size can encourage more users to download and experience the product.

The common composition of application installation packages is shown in the diagram:

Comprehensive Summary of Four Aspects of Android App Performance Optimization

From the diagram, we can see:

  • assets folder. Stores configuration files, resource files, assets do not automatically generate corresponding IDs but are accessed through the AssetManager class interface.

  • res. res is short for resource; this directory stores resource files, which automatically generate corresponding IDs and are mapped to the .R file for direct access using resource IDs.

  • META-INF. Stores application signature information, which can verify the integrity of the APK file.

  • AndroidManifest.xml. This file describes the configuration information of the Android application, including registration information for components, permissions used, etc.

  • classes.dex. Dalvik bytecode program, allowing the Dalvik virtual machine to execute. Generally, during packaging, the Android application converts Java bytecode into Dalvik bytecode using the dx tool in the Android SDK.

  • resources.arsc. Records the mapping relationship between resource files and resource IDs, used to find resources based on resource IDs.

Common solutions to reduce installation package size:

  • Code obfuscation. Use the ProGuard code obfuscator tool, which includes compression, optimization, and obfuscation functions.

  • Resource optimization. For example, use Android Lint to delete redundant resources and minimize resource files.

  • Image optimization. For example, use the AAPT tool to compress PNG format images, reduce image color depth, etc.

  • Avoid libraries with duplicate functionalities, use WebP image format, etc.

  • Pluginization. For example, placing functional modules on the server for on-demand downloading can reduce installation package size.

Conclusion

Performance optimization is not something that can be resolved in one or two updates; it is a continuous demand, requiring continuous integration, iteration, and feedback. In actual projects, at the beginning, due to manpower and project completion time constraints, the priority for performance optimization is relatively low. Once the project enters the usage phase, the priority needs to be increased. However, in the early stages of the project, when designing architectural plans, performance optimization points also need to be considered in advance, reflecting a programmer’s technical foundation.

When the need for performance optimization arises, it often begins with discovering issues, analyzing the reasons and background for the problems, and then seeking optimal solutions to resolve them. This is also a common approach used in daily work.

If you have good articles to share, please submit them directly to me by sending the article link.

Java and Android Architecture

Scan the QR code below or click to receive advanced resources for Android, Python, AI, Java, etc.

Follow and reply with “Baidu”, “Ali”, “Tencent”, “Resources” for surprises.

Comprehensive Summary of Four Aspects of Android App Performance Optimization

Public account:JANiubility

Welcome to join our Java and Android architecture circle, with nearly 1000 people joining for learning and communication, and more learning resources updated for further exchanges and progress.

Comprehensive Summary of Four Aspects of Android App Performance Optimization

For more learning materials, click “Read the original text” below to obtain.

Comprehensive Summary of Four Aspects of Android App Performance Optimization

Leave a Comment