Highly recommended article: Welcome to collect Android dry goods to share
##### Read five minutes, 10 o ‘clock every day, and learn for life with you. This is Android programmer
Android App response is slow, technically speaking, UI rendering is slow.
UI rendering is the act of generating a frame from your application and displaying it on the screen. To ensure smooth user interaction with your application, your application should render 60 frames per second (why 60 FPS?) in 16ms. . If your application suffers from slow UI rendering, the system will be forced to skip frames, and users will feel stuttering in your application. Let’s call this jank.
This article covers some of the basics of Android development. Here’s what you’ll learn:
1.UI rendering introduction 2. Identifying Jank 3. Causes Jank generic problem examples
1. Introduction to UI rendering
To help you improve the quality of your application, Android automatically monitors whether your application is available and displays information in the Android Health Dashboard. For information on how to collect data, see the Play Console documentation.
If there is a problem with your application, this page provides guidance for diagnosing and resolving the problem.
The Android Health Dashboard and the Android system track render time statistics for applications that use UI Toolkit (the user-visible part of the application is drawn from Canvas or View Hierarchy).
If your application does not use UI Toolkit, like an application built with Vulkan, Unity, Unreal, or OpenGL, time statistics are not provided in the Android Vitals dashboard.
You can determine whether your device is recording your application’s render time metrics by running adb shell Dumpsys gfxinfo
2. Identify Jank
It can be difficult to locate the code causing Jank in your application. This section introduces three ways to identify Jank:
- 1.Visual inspection
Visual inspection allows you to quickly browse through all the use-cases in your application in a few minutes, but does not provide the same level of detail as Systrace.
- 2.Systrace
Systrace provides more detail, but if you run Systrace to handle all the use cases in your application, you will be overwhelmed with data that is difficult to analyze.
- 3.Custom performance monitoring
Both Visual Inspection and Systrace will be detected on your local device.
If it cannot be reproduced locally on the device, you can build Custom Performance Monitoring to measure a specific part of the application on the device running in the field.
##1. Visual inspection
Visual inspection can help you identify use cases that are producing results. To perform a visual inspection, open your application and manually examine different parts of the application, then look at the very crude UI. Here are some tips for visual inspection:
- 1. Run the Release version
Run the version of your Release application (or at least the undebugable version). The ART runtime has disabled some important optimizations in order to support debugging, so make sure you’re looking for something similar to what users will see.
-
- Enable GPU Rendering
To enable it: Settings –>Developer options –>Profile GPU Rending
Turn on GPU rendering of the configuration file, and a bar graph will appear on the screen, which can quickly and intuitively show the time it takes to benchmark render UI window frames against 16 milliseconds per frame. Each bar has colored components mapped to a stage in the render pipeline, so you can see which section takes the most time. For example, if the framework is spending a lot of time processing input, you should look at the application code that handles user input.
-
- Watch out for special components
Some components, such as RecyclerView, are popular sources of Jank. If your application uses these components, running these parts of your application is a good idea.
-
- App cold startup
Sometimes, jank can be copied only when the application is started from Clod Start.
- 5. Jank is easy to appear in low memory
Once you discover the use case that generated Jank, you’ll probably have a good idea of what led to the results of your application. However, if you need more information, you can use Systrace to delve further.
##2. Systrace
Systrace is a tool that shows what the entire device is doing, and it can be used to identify Jank in an application. Systrace has very low system overhead, so you will feel app lag during the use of the instrument.
Record tracing with Systrace while executing janky use cases on the device. For instructions on how to use Systrace, see the Systrace Walkthrough. Systrace is decomposed by processes and threads. The process of finding an application in Systrace should look like this.
The above three annotation points are explained
- When the frame freezes, a frame drop occurs, as shown in Figure 1
Systrace shows when each frame is drawn, and color codes each frame to highlight slower render times. This can help you find individual Janky frames that are more accurate than visual inspection. For more information, see Inspecting Frames.
-
Frame drop prompts, as shown in Figure 2, Systrace detects problems in the application and displays alerts in various frames and alert panels. The following instructions in the alert are your best bet.
-
The Systrace Timeline is shown in Figure 3
Parts of the Android framework and libraries, such as RecyclerView, contain trace tokens. Therefore, the Systrace timeline shows when and for how long these methods are executed on the UI thread.
If Systrace does not show you detailed information about working with UI threads for long periods of time, you need to use the Android CPU Profiler to record method traces for sampling or detection. In general, method method traces are not suitable for identifying queues because it is too expensive to generate false Janks and there is no way to see when a thread is blocked. However, the Method method trace can help you identify the methods that spend the most time in your application. After identifying the methods, add Trace markers and reroute systrace to see if they are causing confusion. When recording systrace, each Trace mark (beginning of execution trace.beginSection (); And end the Trace. EndSection (); Yes) adds about 10μs of overhead. To avoid fake Jank endings, don’t add trace markers to methods that are called dozens of times in a frame, or less than 200us or so.
For more information, see Systrace
##3. Custom performance monitoring
If you cannot reproduce an outbreak on a local device, you can build custom performance monitoring in your application to help identify the source of the outbreak on a field device.
To do this, use FrameMetricsAggregator to collect frame rendering times from specific parts of your application, and use Firebase performance monitoring to record and analyze the data.
For more information, see Using Firebase Performance Monitoring with Android Vitals.
#3.Fix Jank
To fix this, examine which frames were not completed in 16.7ms and look for what went wrong. Record View#draw draws abnormal lengths in some frames, or maybe a Layout? See below for 4 common sources of these problems, as well as others.
To avoid garbled characters, long-running tasks should be run asynchronously outside of the UI thread. Be careful which thread your code is running on, and be careful when publishing unimportant tasks to the main thread.
If your application has a complex and important main UI (perhaps a central scrolling list), consider writing test tests that automatically detect slow rendering times, and running tests frequently to prevent regression. For more information, see the Automated Performance Testing Code Lab.
#4. Examples of generic issues causing Jank
The following sections explain the sources of common Jank problems in applications and the best solutions to solve them.
Slide the List
ListView and RecyclerView in particular are used for complex scrolling lists that are easiest to ignore. They all contain the Systrace tag, so you can use Systrace to figure out if they help in your application Jank. Be sure to pass the command line argument -a
to get the trace part of RecyclerView (and any trace tags added) to display. If available, follow the guidance of the alerts generated in the Systrace output. In Systrace you can click recyclerView-Traced section to see an explanation of the work RecyclerView is doing.
RecyclerView: notifyDataSetChanged
If you see every item in RecyclerView being bounced in a framework (and therefore rearranged and redrawn), make sure you don’t call notifyDataSetChanged (), setAdapter (Adapter), or swapAdapter (Adapter, Boolean) is a minor update. These methods indicate that the entire list has changed and will be displayed as RV FullInvalidate in Systrace. Instead, use SortedList or DiffUtil to generate minimal updates when content changes or is added.
For example, consider a new version of an application that receives a list of news content from the server. When you publish this information to the adapter, you can call notifyDataSetChanged (), as follows:
RecyclerView
It is best to use DiffUtil, which calculates and allocates the smallest updates for you.
Simply define your MyCallback as the DiffUtil.Callback implementation to tell DiffUtil how to check your list.
RecyclerView: Nested RecyclerViews
Nested recyclerViews are common, especially vertical lists of horizontally scrolling lists (such as the grid of apps on the Play Store home page). This can work well, but there are also a lot of accidents moving around. If the scroll down the page for the first time to see a lot of internal expansion project, you may need to check whether the internal (horizontal) RecyclerViews Shared between RecyclerView. RecycledViewPools.
By default, each RecyclerView will have its own pool of items. Displaying a dozen itemViews on the screen at the same time can be problematic if all rows display similar types of views when itemViews cannot be shared by different horizontal lists.
If you want to further optimize, can also call on the internal RecyclerView LinearLayoutManager setInitialPrefetchItemCount (int). For example, if you are always in a row visible in item 3.5, please call innerLLM. SetInitialItemPrefetchCount (4); This tells RecyclerView that when a horizontal row is about to appear on the screen, it should try to prefetch internal items if there is free time on the UI thread
RecyclerView: Too much inflation / Create taking too long
The UI thread is in the idle state, and the prefetch function in RecyclerView should help solve the cost problem of inflation Layout by completing work ahead of time in most cases. If you see inflation Layout in a frame (as opposed to the part marked RV Prefetch), make sure you are testing the most recent device (Prefetch is currently only supported on Android 5.0 API Level 21 and above), And use the latest version of the Support Library.
If inflation Layout often causes a new Jank to appear on the screen, verify the problem and remove the superfluous View. The fewer view types in RecyclerView content, the less inflation Layout needs to be completed when a new item type appears on the screen.
If possible, merge view types into appropriate locations – if only ICONS, colors, or text blocks change between types, this can be done at binding time, avoiding inflation Layout (and reducing the application’s memory footprint).
If your view type looks good, consider reducing the cost of Inflation Layout. Reducing unnecessary container and structural views can help – consider building an itemView with ConstraintLayout, which can easily reduce structural views. If you want to really optimize performance, your project hierarchy is simple, and you don’t need complex theming and style functionality, consider calling the constructor yourself — but note that it’s often not worth the trade-off between simplicity and functionality in XML.
RecyclerView: Bind taking too long
The binding (that is, onBindViewHolder (VH, int)) should be very simple, taking less than a millisecond for all but the most complex items. It simply fetches the POJO project from the Adapter’s internal project data and calls the setter on the view in the ViewHolder. If RV OnBindView takes a long time, make sure you do the least work in the binding code.
If you use simple POJO objects to hold Data in the adapter, you can completely avoid using the Data Binding L library to write Binding code to onBindViewHolder.
RecyclerView or ListView: layout / draw taking too long
For drawing and Layout questions, see Layout and Rendering Performance.
ListView: Inflation
Listviews can easily be recycled accidentally if you’re not careful. If you see Inflation Layout every time your project is displayed on the screen, check that the adapter.getView () implementation is in use, rebind and return the convertView parameter. If your getView () implementation always inflation Layout, your application will not benefit from ListView recycling. The structure of your getView () almost always looks like the following implementation:
Layout performance
If Systrace shows that Choreographer# doFrame’s layout section is working too much, or too often, it means that you have a layout performance problem. The layout performance of your application depends on which part of the View hierarchy has change layout parameters or inputs.
Layout performance: Cost
If the segment is longer than a few milliseconds, it may be for RelativeLayouts or weighted-Linearlayouts. Worst nesting performance.
Each of these layouts can trigger multiple measure/layout passes for its children, so nesting them results in O (n ^ 2) behavior at nesting depth. Try to avoid using the weight feature of a RelativeLayout or LinearLayout for all but the lowest leaf node of the hierarchy. There are several ways to do this:
- Optimize View structure
- Use custom Views
- Try converting to ConstraintLayout, which provides similar functionality without performance drawbacks.
Layout performance: Frequency
A new Layout occurs when new content appears on the screen, for example, when a new item is scrolled in RecyclerView. If a significant layout occurs on each frame, it may be animated on the layout, which is likely to result in frame loss. In general, animations should run on the View’s drawing properties (e.g., setTranslationX/Y/Z (), setRotation (), setAlpha (), etc.). These can be changed better than Layout properties such as padding or margins. The drawing properties of a view are usually changed by calling the setter that triggers invalidate () and then drawing (Canvas) in the next frame. This will re-record the drawing operation of invalid views, and is usually much better than layout.
Rendering performance
The Android UI works in two phases — Record View#draw on the UI thread and draw the DrawFrame on the RenderThread. The first run draws a Canvas on each invalid View and may call custom views or code. The second runs on the local RenderThread, but will operate based on the work generated in the Record View#draw phase.
Rendering performance: UI Thread
If Record View#draw takes a long time, it is usually the case that bitmaps are drawn on the UI thread. Drawing bitmaps requires CPU rendering and should generally be avoided in the main thread. You can use the Android CPU profiler method trace to see if this is a problem.
Drawing a bitmap is usually done when an application wants to decorate a bitmap before displaying it. Sometimes like a rounded corner ornament:
Drawable or View
The above code can be optimized as follows:
Note that this can often also be used for background protection (drawing graditions at the top of bitmaps) and image filtering (using ColorMatrixColorFilter), as well as two other common operations for modifying bitmaps.
If for other reasons (it may be used as a cache), try to draw hardware-accelerated hardware that is passed directly to the View or Drawable, and if necessary consider using LAYER_TYPE_HARDWARE to call setLayerType () to cache complex render output, And still use GPU rendering.
Rendering performance: RenderThread
Some canvas operations are less costly, but trigger RenderThread expensive calculations. Systrace usually notifies these.
Canvas.saveLayer()
Avoid Canvas.savelayer () – it can trigger an expensive, uncached, off-screen rendering of every frame. Despite the performance improvements in Android 6.0 (when optimized to avoid switching rendering targets on ggpu), it’s still a good thing to avoid using this expensive API if possible, or at least make sure you pass CLIP_TO_LAYER_SAVE_FLAG (or call a variant without flags).
Animating large Paths
When hardware-accelerated Canvas is passed to Views, Canvas.drawPath () is called, and Android first draws these paths on the CPU and then uploads them to the GPU. If the path is large, avoid frame-by-frame editing in favor of caching and drawing. DrawPoints (), drawLines () and drawRect/Circle/Oval/RoundRect () are more efficient – even if you end up using more draw calls, it’s better to use them.
Canvas.clipPath
ClipPath (Path) triggers expensive clipping behavior and should generally be avoided. If possible, choose to draw shapes instead of cropping to non-rectangles. It performs better and supports anti-aliasing. For example, the following clipPath call:
Bitmap uploads
Android displays the bitmap as an OpenGL texture and uploads it to the GPU the first time a bitmap is displayed in a frame. You can think of this as uploading width x height texture in Systrace. This may take a few milliseconds (see figure below), but it is necessary to display the image with a GPU.
If these take a long time, first check the width and height numbers in the track. Ensure that the bitmap being displayed is not much larger than the area of the screen being displayed. If so, upload time and memory are wasted. Usually bitmap loading libraries provide a simple way to request a bitmap of the appropriate size.
In Android 7.0, bitmap loading code (usually done by libraries) can call prepareToDraw () before it is needed to trigger an upload early. This upload happens early and the RenderThread is idle. This can be done after decoding or while binding the bitmap to the View, as long as the bitmap is known. Ideally, your bitmap loading library will do this for you, but if you’re managing your own, or want to make sure you don’t hit upload on a new device, you can call prepareToDraw () in your own code.
Thread scheduling delays
The thread scheduler is the part of the Android operating system that determines which threads in the system should run, when they should run, and for how long. Sometimes Jank occurs because your application’s UI thread is blocked or not running. Systrace uses different colors to indicate that the thread is sleeping (gray), Runnable (blue: it can run, but the scheduler has not yet selected it to run), running (green), or interrupted (red or orange). This is useful for debugging Jank’s problems caused by thread scheduling delays.
Note: Older versions of Android experience scheduling problems more frequently that are not application glitches. Continuous improvements have been made in this area, so consider more debug thread scheduling issues on recent operating system versions, where the scheduled thread is more likely to be an application error.
UI threads or RenderThreads are not expected to run when there is a part of the framework. For example, the UI thread blocks while the RenderThread’s syncFrameState is running and the bitmap is being uploaded — this is because the RenderThread can safely copy the data used by the UI thread. As another example, RenderThread can be blocked when using IPC: fetching buffers at the beginning of a frame, querying information from them, or passing buffers back to the synthesizer via eglSwapBuffers.
There are often long pauses in the execution of your application, which are made possible by the interprocess communication (IPC) mechanism on Android. In recent versions of Android, this is one of the most common reasons UI threads stop running. In general, fixes are made to avoid calling functions to binder; If this is unavoidable, you should cache the value or move the work to the background thread. As the code base gets bigger, it’s easy to accidentally add a binder call by calling low-level methods if you’re not careful, but it’s also easy to use tracing to find and fix them.
If you have a bound transaction, you can use the following ADB command to capture its call stack:
Sometimes harmless surface calls like getRefreshRate () can trigger bound transactions and cause serious problems when called frequently. Regular tracking can help you find and resolve these issues quickly.
If you don’t see the binding Activity but still don’t see your UI thread running, make sure you’re not waiting for a lock or other action from another thread. In general, UI threads should not wait for results from other threads – other threads should post messages to them.
Object allocation and garbage collection
Object allocation and garbage collection (GC) has been an issue since ART was introduced by default in Android 5.0, but it’s still possible to lighten your threading load with this extra work. It’s good practice to allocate for rare events that don’t happen more than once per second (such as a user clicking a button), but remember that each allocation costs something. If it is in a tight loop of frequent calls, consider avoiding allocation to lighten the load on the GC.
Systrace will tell you how often GC is running, and the Android Memory Profiler will show you where your allocations are coming from. If you can avoid allocation, especially in tight loops, you should have no problem.
On the latest versions of Android, GC usually runs on a background thread called HeapTaskDaemon. Note that large allocations can mean more CPU resources being spent on GC.
At this point, this has ended, if there is wrong place, welcome your suggestion and correction. Meanwhile, I look forward to your attention. Thank you for reading. Thank you!