In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article is about how to optimize your layout using android. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.
Preface
In fact, Android rendering optimization can be divided into two parts, namely, UI optimization and Catton optimization. The core problem of layout optimization is to solve the problem of application stutter caused by poor layout rendering performance, so it can be regarded as a subset of Catton optimization.
Why layout optimization?
Why layout optimization? The answer is obvious, if the layout is too deeply nested, or other reasons lead to poor rendering performance of the layout, it may lead to the application of stutter, so how on earth does the layout lead to poor rendering performance? First of all, we should understand the principles of android drawing and layout loading.
Principle of android drawing
The three most important concepts are involved in Android's screen refresh (here is a brief introduction to make it easier to understand)
CPU: perform measure, layout, draw and other operations in the application layer, and submit the data to GPU after drawing
GPU: further processing the data and caching it
Screen: consists of pixels that are filled with data taken from the buffer at a fixed frequency (16.6ms, that is, 60 frames per second).
To sum up, the data is submitted after CPU is drawn, the data is further processed and cached by GPU, and the final screen reads the data from the buffer and displays it.
Double buffering mechanism
After reading the flow chart above, it is easy to think of a problem. The screen is refreshed at a fixed frequency of 16.6ms, but the time when our application layer triggers the drawing is completely random (for example, we can touch the screen to trigger the drawing at any time). What happens if the screen reads data to the buffer while GPU writes to it? It is possible that one part of the previous frame and another frame will appear on the screen, which is obviously unacceptable, so how to solve this problem?
Therefore, in the screen refresh, the Android system introduces the double buffering mechanism.
GPU only writes drawing data to Back Buffer, and GPU periodically exchanges Back Buffer and Frame Buffer, which is also 60 times per second, which is synchronized with the refresh rate of the screen.
Although we introduce the double buffering mechanism, we know that when the layout is more complex, or the device performance is poor, CPU can not guarantee to complete the calculation of drawing data in 16.6ms, so here the system has done another processing. When your application is filling the Back Buffer with data, the system will lock the Back Buffer. If it is time for GPU to exchange two Buffer and your application is still filling the Back Buffer with data, GPU will find that Back Buffer is locked and it will abandon the exchange.
The consequence of this is that the phone screen still displays the original image, which is what we often call dropped frames.
Layout loading principle
As can be seen from the above, the reason for the frame drop is that CPU can not complete the calculation of drawing data in 16.6ms. The reason why layout loading may cause frame drop is precisely because it takes time to operate on the main thread, which may cause CPU to fail to complete the data calculation on time.
Layout loading is mainly implemented through setContentView, so let's not post the source code here. Let's take a look at its timing diagram.
As we can see, there are two main time-consuming operations in setContentView
Parse xml and get XmlResourceParser, which is the IO process
With createViewFromTag, you create a View object, using reflection
The above two points are the reasons why layout loading may lead to stutter, and it is also the performance bottleneck of layout.
Get the time-consuming method for loading layout files
If we need to optimize the layout stutter problem, the most important thing is to determine the quantitative standard, so we first introduce several methods to obtain the loading time of layout files.
Conventional acquisition
First of all, let's introduce the general methods.
Val start = System.currentTimeMillis () setContentView (R.layout.activity_layout_optimize) val inflateTime = System.currentTimeMillis ()-start
This method is very simple, because setContentView is a synchronous method, if you want to calculate time-consuming, you can directly subtract the front and back time to get the result.
AOP (Aspectj,ASM)
Although the above method is simple, it is not elegant enough, and the code is intrusive. If you want to measure all Activity, you need to overwrite the relevant methods in the base class. It is more troublesome to introduce a way to calculate AOP that takes time.
@ Around ("execution (* android.app.Activity.setContentView (..)") Public void getSetContentViewTime (ProceedingJoinPoint joinPoint) {Signature signature = joinPoint.getSignature (); String name = signature.toShortString (); long time = System.currentTimeMillis (); try {joinPoint.proceed ();} catch (Throwable throwable) {throwable.printStackTrace ();} Log.i ("aop inflate", name + "cost" + (System.currentTimeMillis ()-time));}
The Aspectj used above is relatively simple, and the above note means to call the getSetContentViewTime method we have written inside the execution of the setContentView method, so that we can get the corresponding time-consuming log, and we can take a look at the printed log.
I/aop inflate: AppCompatActivity.setContentView (..) Cost 69
I/aop inflate: AppCompatActivity.setContentView (..) Cost 25
In this way, non-intrusive monitoring of the time-consuming source code loaded by each page layout can be seen at the end of the article.
It takes time to get any control
Sometimes in order to know exactly which control takes time to load, for example, when we add a custom View, we need to monitor its performance. We can use setFactory2 to monitor the load time of each control. First of all, let's review the setContentView method.
Public final View tryCreateView (@ Nullable View parent, @ NonNull String name,... View view; if (mFactory2! = null) {view = mFactory2.onCreateView (parent, name, context, attrs);} else if (mFactory! = null) {view = mFactory.onCreateView (name, context, attrs);} else {view = null;}. Return view;}
Before actually instantiating the xml node by reflection, the onCreateView method of mFactory2 is called so that if we rewrite the onCreateView method and add time statistics before and after it, we can get the load time of each control.
Private fun initItemInflateListener () {LayoutInflaterCompat.setFactory2 (layoutInflater, object: Factory2 {override fun onCreateView (parent: View?, name: String, context: Context, attrs: AttributeSet): View? {val time = System.currentTimeMillis () val view = delegate.createView (parent, name, context Attrs) Log.i ("inflate Item", name + "cost" + (System.currentTimeMillis ()-time)) return view} override fun onCreateView (name: String, context: Context, attrs: AttributeSet): View? {return null}})
As shown above: the real way to create View is still to call delegate.createView, but we just pay attention to it before and after it. InitItemInflateListener needs to be called before onCreate so that it is more convenient to listen to the loading time of each control.
Introduction of some methods of layout loading optimization
There are two main reasons for slow layout loading, one is IO, the other is reflection, so we generally have two optimization ideas.
Side relief (asynchronous loading)
Fundamental solution (no IO, reflection process, such as X2C, Anko, compose, etc.)
AsyncLayoutInflater scheme
AsyncLayoutInflater is used to help load layout asynchronously. After the inflate (int, ViewGroup, OnInflateFinishedListener) method ends, OnInflateFinishedListener returns View; in the main thread callback, which is intended for lazy loading of UI or high response to user actions.
To put it simply, we know that by default, the setContentView function is executed in the UI thread, in which there are a series of time-consuming actions: Xml parsing, View reflection creation and other processes are also executed in the UI thread. AsyncLayoutInflater is to help us to execute these processes asynchronously, keeping the UI thread highly responsive.
The use is as follows:
@ Override protected void onCreate (@ Nullable Bundle savedInstanceState) {super.onCreate (savedInstanceState); new AsyncLayoutInflater (AsyncLayoutActivity.this) .propagate (R.layout.async_layout, null, new AsyncLayoutInflater.OnInflateFinishedListener () {@ Override public void onInflateFinished (View view, int resid, ViewGroup parent) {setContentView (view) }}); / / other actions}
The advantage of this is that the UI loading process is migrated to the child thread, which ensures the high response of the UI thread at the expense of ease of use, and it may cause a crash if UI is called during initialization.
X2C scheme
X2C is a set of open source layout loading framework. Its main idea is to generate the corresponding java file from the layout translation that needs to be translated during the compilation time, so that for developers to write the layout or write the original xml, but for the program, the runtime loads the corresponding java file. This shifts the runtime overhead to the original xml file as shown at compile time:
Java files generated by X2C
Public class X2C_2131296281_Activity_Main implements IViewCreator {@ Override public View createView (Context ctx, int layoutId) {Resources res = ctx.getResources (); RelativeLayout relativeLayout0 = new RelativeLayout (ctx); relativeLayout0.setPadding ((int) (TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP,10,res.getDisplayMetrics (), 0memo (0); View view1 = (View) new X2C_2131296283_Head () .createView (ctx,0) RelativeLayout.LayoutParams layoutParam1 = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); view1.setLayoutParams (layoutParam1); relativeLayout0.addView (view1); view1.setId (R.id.head); layoutParam1.addRule (RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE); ImageView imageView2 = new ImageView (ctx) RelativeLayout.LayoutParams layoutParam2 = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, (int) (TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP,1,res.getDisplayMetrics (); imageView2.setLayoutParams (layoutParam2); relativeLayout0.addView (imageView2); imageView2.setId (R.id.ccc); layoutParam2.addRule (RelativeLayout.BELOW,R.id.head); return relativeLayout0;}}
Use X2C.setContentView instead of the original setContentView as shown below.
/ / this.setContentView (R.layout.activity_main); X2C.setContentView (this, R.layout.activity_main)
X2C advantages
While retaining xml, it also solves the performance problems caused by it.
According to X2C statistics, the loading time can be reduced to the original 1max 3.
X2C problem
Some properties cannot be set by code, and Java is not compatible.
The loading time is transferred to the compilation time, which increases the compilation time.
Do not support kotlin-android-extensions plug-ins, at the expense of some ease of use
Anko scheme
Anko is a powerful library developed by JetBrains that supports writing UI using kotlin DSL, as shown below
Class MyActivity: AppCompatActivity () {override fun onCreate (savedInstanceState: Bundle?, persistentState: PersistableBundle?) {super.onCreate (savedInstanceState, persistentState) MyActivityUI (). SetContentView (this)} class MyActivityUI: AnkoComponent {override fun createView (ui: AnkoContext) = with (ui) {verticalLayout {val name = editText () button ("Say Hello") {onClick {ctx.toast ("Hello") ${name.text}! ")}
As shown above, Anko uses kotlin DSL to implement layouts, which is much more convenient than we use Java to dynamically create layouts, mainly more concise. It and the hierarchical relationship of creating layouts with xml make it easier for us to read. At the same time, it removes IO and reflection processes, and has better performance. Here is the performance comparison between Anko and XML.
However, since the maintenance of AnKo has been stopped, it is not recommended to use it here. Just understand the principle and AnKo suggests that you use Jetpack Compose instead of using it.
Compose scheme
Compose is a new member of Jetpack, is a new UI library announced by the Android team at the 2019 UI O conference, Compose is currently in the Beta stage using pure kotlin development, simple and convenient to use, but it is not like Anko to encapsulate ViewGroup Compose is not to View and ViewGroup this system to do an upper packaging to make it easier to write, but completely abandoned the system, their own rendering mechanism from the inside out to do a new.
To be sure, Compose is the official solution to replace XML.
The main advantage of Compose is its simplicity and ease of use. Specifically, it has two points.
Its declarative UI
Xml is removed and only Kotlin is used
Since this article does not introduce Compose, we will not continue to introduce Compose. Generally speaking, Compose is the direction of future android UI development, and readers can consult the relevant materials on their own.
Some conventional optimization methods
In fact, we also have some conventional methods to optimize layout loading, such as optimizing layout levels, avoiding over-drawing, and so on. These simple methods may be applied to the project.
Optimize layout level and complexity
With ConstraintLayout, you can achieve a completely flattened layout and reduce hierarchy
RelativeLayout itself should try not to use nesting.
In nested LinearLayout, try not to use weight, because weight will re-measure twice
It is recommended to use merge tags to reduce one level.
Use ViewStub to delay loading
Avoid overdrawing
Remove the excess background color and reduce the use of complex shape
Avoid hierarchical overlay
Custom View draws with clipRect masked View
Thank you for reading! This is the end of this article on "how to optimize the layout using android". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.