📜 ⬆️ ⬇️

Development of fast mobile applications on Android. Part two

In Edison, we often encounter optimization of mobile applications and want to share material that we consider extremely useful if you solve one of two tasks: a) you want the application to slow down less; b) want to make a beautiful, soft and smooth interface for the mass user.

We bring to your attention the first part of the translation of the article Udi Cohen, which we used as a tool for teaching young colleagues to optimize for Android.

(Read the first part )
')


General tips for working with memory


Here are some simple guidelines that I use when writing code.

GPU profiling


A new addition to Android Studio 1.4 is GPU profiling rendering.
Under the Android window, go to the GPU tab, and you will see a graph showing the time it took to make each frame on the screen:

image

Each panel on the chart represents one frame drawn, and the color represents the various stages of the process.


More colors have been added to Marshmallow to display more steps, such as Measure / Layout, Input Handing, and others:
image

EDIT 09/29/2015: John Reck, a framework engineer from Google, added the following information about some of these colors:
“The exact definition of the word“ animation ”is all that is registered with Choreographer as CALLBACK_ANIMATION. This includes the Choreographer # postFrameCallback and View # postOnAnimation, which are used by view.animate (), ObjectAnimator, Transitions, and others ... And yes, this is the same thing that is labeled “animation” in systrace.

“Misc” is the delay between the vsync timestamp and the current timestamp when it was received. If you have already seen the logs from Choreographer "Missed vsync by blabla ms skipping blabla frames", now it will be represented as "misc". There is a difference between INTENDED_VSYNC and VSYNC in framestats dump (https://developer.android.com/preview/testing/performance.html#timing-info). ”


But before starting to use this feature, we need to enable the GPU rendering mode in the developer menu:
image

It can be (!), Using:

adb shell dumpsys gfxinfo <PACKAGE_NAME> 


we can get the information and build the schedule yourself. The command will display useful information, such as the number of elements in the hierarchy, the size of all display lists, and so on. In Marshmallow we get more information.

image

If we have automated UI testing of our application, we can build an assembly that runs this command after certain actions (list scrolling, heavy animation, etc.) and see if there are changes in values, such as “Janky Frames”, over time . This can help determine the decline in performance after several code changes, allowing us to fix the problem before the application has entered the market. We can get even more accurate information when we use the “framestats” keyword, as shown here .

But this is not the only way to see such a schedule!

As you can see in the developer menu of the Profile GPU Rendering, there is also the option “On screen as bars”. His choice will show a graph for each window on your screen along with a green line indicating a 16 ms threshold.

image

In the example to the right, we can see that some frames cross the green line, which means that they require more than 16ms to draw. Since the blue color seems to dominate, we understand that there were many representations to draw, or they were complex. In this case, I scroll the tape, which supports different types of presentation. Some items become invalid, and some become more complex than others. Perhaps the reason why some frames crossed this line is because an element is difficult to display.

Hierarchy viewer


I love this tool, and I am very sorry that many do not use it!
Using the Hierarchy Viewer, we can get performance statistics, see the complete view hierarchy on the screen, and access all the properties of an item. You can also dump all topic data, see all parameters used for each style attribute, but this is possible only if the Hierarchy Viewer is running standalone, not from Android Monitor. I use this tool when I create application layouts and I want to optimize them.

image

In the center we can see a tree reflecting the hierarchy of the view. The presentation hierarchy may be broad, but if it is too deep (~ 10 levels), it can cost us a lot. Each time a view dimension occurs in View # onMeasure (), or when its descendants are placed in View # onLayout (), these commands apply to the descendants of these views, doing the same. Some layouts will do each step twice, for example, RelativeLayout and some LinearLayout configurations, and if they are nested, the number of passes increases exponentially.

In the lower right corner we can see the “plan” of our layout, noting where each presentation is located. We can choose a view here, or in a tree and see all the properties on the left. When designing a layout, I'm sometimes not sure why a particular view ends where it ends. Using this tool, I can track the tree, select it and see where it is in the preview window. I can make interesting animations by looking at the final dimensions of the views on the screen, and use this information to precisely place elements around. I can find lost views that were overlaid by other ideas unintentionally.

image

For each view, we have the time it took to measure / create a layout / render it and all its descendants. The colors reflect how these representations are performed in comparison with other representations in the tree; this is a great way to find the weak link. Since we also see a preview of the view, we can go through the tree and follow the instructions that create them, finding extra steps that we could remove. One of the things that affects performance is called Overdraw.

Overdraw


As can be seen in the GPU profiling section, the Execute phase represented in yellow on the graph may take longer to complete if the GPU has to draw a lot of elements, increasing the time required to draw each frame. Overdraw occurs when we draw something on top of something else, such as a yellow button on a red background. The GPU needs to draw, first, a red background and then a yellow button on top of it, which makes inevitable overdraw. If we have too many overdraw layers, this may be the reason why the GPU works more intensively and does not have time to 16 ms.

image

Using the “Debug GPU Overdraw” setting in the developer menu, all overdraw will be tinted to demonstrate the degree of overdraw in a given area. If overdraw has a 1x / 2x degree, this is normal, even small red areas are not bad either, but if we have too many red elements on the screen, we probably have a problem. Let's look at a few examples:

image

In the example on the left there is a list colored green, which is usually normal, but there is an overdraw at the top, which makes the screen red, and this is becoming a problem. In the example to the right, the entire list is light red. In both examples there is an opaque list with a 2x / 3x overdraw layer. These can happen if there is a background color on the whole screen in a window that overlaps your activit or fragment, as well as on the list view and the presentation of each of its elements.

Note: the default theme sets the background color for your entire window. If you have an activation with an opaque layout that covers the entire screen, you can remove the background for this window to remove one overdraw layer. This can be done in a thread or in code by calling getWindow (). SetBackgroundDrawable (null) in onCreate ().

Using the Hierarchy Viewer, you can export all your hierarchy layers to a PSD file to open it in Photoshop. As a result of researching different layers, all overdraw in the layout will be revealed. Use this information to remove redundant overdraws, and do not settle for green, aim for the blue!

Alpha


Using transparency can affect performance, and in order to understand why, let's see what happens when setting the alpha parameter in a view. Consider the following structure:

image

We see a layout that contains 3 ImageViews that overlap each other. In a direct implementation, setting alpha with setAlpha () will cause the command to be propagated to all representations of the children, ImageView in this case. These ImageViews will then be drawn with the alpha parameter in the frame buffer. Result:

image

This is not what we want to see.

Since each ImageView was rendered with an alpha value, all overlapping images will merge. Fortunately, the OS has a solution to this problem. The layout will be copied to a separate off-screen buffer, alpha will be used in this buffer as a whole and the result will be copied to the frame buffer. Result:

image

But ... we will pay our price for it.

Additional drawing of the presentation in the off-screen buffer before it is done in the frame buffer adds another invisible overdraw layer. The OS does not know exactly when to use this or a direct approach before, so by default it always uses one integrated one. But there are still ways to set alpha and avoid difficulties with the addition of off-screen buffer:

Hardware acceleration


When hardware acceleration was enabled in Honeycomb, we got a new rendering model to display our application on the screen. This includes a DisplayList structure that records view rendering commands for faster image acquisition. But there is another interesting feature that developers usually forget about - presentation levels.

Using them, we can draw the presentation in the off-screen buffer (as we saw earlier when using the alpha channel) and manipulate it as we wish. This is great for animation because we can animate complex views faster. Without levels, the animation of the representations would become inaccessible after changing the properties of the animation (such as x-coordinate, scale, alpha value, etc.). For complex representations, this inability extends to descendants, and they in turn will redraw themselves, which is an expensive operation. Using the presentation levels, relying on hardware, the texture for our presentation is created in the graphics processor. There are several operations that we can apply to the texture without having to replace it, such as changing the position along the x / y axes, rotation, alpha, and others. All this means that we can animate complex presentations on our screen without replacing them in the process of animation! This makes the animation more smooth. Here is a sample code showing how this can be done:

 // Using the Object animator view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 20f); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(View.LAYER_TYPE_NONE, null); } }); objectAnimator.start(); // Using the Property animator view.animate().translationX(20f).withLayer().start(); 


Just right?

Yes, but there are a few points to keep in mind when using the hardware level.


For the second case, there is a way to visualize this update. Using developer settings, we can activate “Show hardware layers updates”.

image

After that, the view will be highlighted in green when upgrading at the hardware level. I used it some time ago when I had a ViewPager that did not flip through the pages with the smoothing that I was expecting. After I turned on this option, I went back and flipped through the ViewPager, and this is what I saw:

image

Both pages were green throughout the entire scroll!

This means that the hardware level was used to optimize the pages, and the pages became unusable when we looked through the ViewPager. I updated the pages and flipped through them using the parallax effect in the background and gradually revived the elements on the page. What I didn't do was not create a hardware layer for the ViewPager pages. After examining the ViewPager code, I discovered that when the user starts scrolling, both pages are created at the hardware level and deleted after the scrolling ends.

While it makes sense to use hardware levels for scrolling through pages, in my case this did not work. Usually the pages do not change when we flip through the ViewPager, and since they can be quite complex, the hardware level helps to render the drawing much faster. In the application I was working on, this was not the case, and I removed this hardware layer using a small hack, which I wrote myself.

The hardware layer is not a silver bullet. It is important to understand how it works, and to use it properly, otherwise you may face a big problem.

Do it yourself


In preparing for all these examples shown here, I wrote a lot of code to simulate these situations. You can find them on the Github repository , as well as on Google Play . I shared the scenarios for various activations, and tried to document them as much as possible, to help understand what kind of problems you might encounter using activations. Read the activation documentation, open the tools and play with the app.

More information


As the Android OS evolves, the ways in which you can optimize your applications are evolving. New tools are introduced in the Android SDK, and new functions are added to the OS (for example, hardware levels). It is very important to learn new things and study compromises before you decide to change something.

There is a great YouTube playlist called Android Performance Patterns , with a lot of short videos from Google explaining various performance related things. You can find comparisons between different data structures (HashMap vs ArrayMap), Bitmap optimization, and even optimization of network requests. I highly recommend watching all of them.

Join the Android Performance Patterns Google+ community and discuss performance with other contributors, including Google employees, to share ideas, articles, and questions.

Other interesting links



I hope you now have enough information and more confidence to start optimizing your applications today!

Start tracing, or turn on some of the other available developer options, and just start with them. I propose to share your thoughts in the comments.

Source: https://habr.com/ru/post/271811/


All Articles