TextView in Android is one of the most complex controls in the entire framework, responsible for most of the work of displaying text in Android, and many of the controls in Framwork are directly or indirectly inherited from TextView, such as button, EditText and so on. Its internal implementation is also quite complex, the single line of code, Android-22 TextView has a full 9509 lines, in addition, TextView many operations are very heavy, such as settext operation, need to set Spanwatcher, Or you need to recreate the creation of a spannablestring, and then recreate the text Layout, which adds up to a time-consuming settext operation. In order to improve the rendering efficiency of TextView, we have recently studied the method of pre rendering, and then explained the principle.
TextView Rendering Fundamentals
First of all, introduce the basic rendering principle of TextView, in general, the TextView is responsible for rendering the text is mainly the three classes:
Boringlayout
It is primarily responsible for displaying single-line text and provides a isboring method to determine whether the criteria for a single line of text are met.
Dynamiclayout
When the text is spannable, TextView will use it to be responsible for the display of the text, set the Spanwatcher internally, and Reflow and recalculate the layout when span changes are detected.
Staticlayout
When the text is not a single line of text, and is not spannable, the use of staticlayout, the internal does not listen to span changes, so the efficiency will be higher than the dynamiclayout, only once the layout is created, But in fact the interior can also show spannablestring, but not after span changes after the layout.
In addition, all of the above three classes inherit from the layout class, in which the specific drawing of the text is unified, and in the Layout.draw method, a line of text is rendered:
Textline tl = Textline.obtain ();
Draw the lines, one at a time.
The baseline is the "top" of the following line minus the current line ' s descent.
for (int i = Firstline i <= lastline; i++) {
....
. Directions Directions = Getlinedirections (i);
if (directions = = Dirs_all_left_to_right &&!mspannedtext &&!hastaboremoji) {
//Xxx:assumes there ' s nothing additional to being done
Canvas.drawtext (buf, start, end, X, Lbaseline, paint);
} else {
tl.set (paint, BUF, start, end, dir, directions, Hastaboremoji, tabStops);
Tl.draw (canvas, X, Ltop, Lbaseline, Lbottom);
}
Textline.recycle (TL);
Can be seen for spannble, or contains emoji text, the actual rendering operation is to the textline to draw, otherwise directly using canvas.drawtext,textline responsible for the single line of complex text, which spannable, Drawing logic such as emoji is contained inside, and Textline's rendering logic is not very efficient, and the following will continue to explain how it should be optimized.
Textlayoutcache
Canvas in DrawText, if you need to calculate the size of the font every time, such as margin, etc., it will be very time-consuming, resulting in drawtext time will pull a long, in order to improve efficiency, Android introduced the Textlayoutcache after 4.0, using LRU cache cached data such as glyphs, margins, and so on, raising the DrawText speed, In 4.4, this cache size is 0.5M, global use, and will be in the activity of configurationchanged, Onresume, Lowmemory, updatevisibility and other time, Canvas.freetextlayoutcache is invoked to free this part of memory. Because this part of the cache is the bottom of the system control, we can not do specific control.
Pre-rendering optimization of TextView
From the TextView rendering principle, if we simply display the text, we do not need to set the Spanwatcher to monitor span changes, so we can directly use Boringlayout or staticlayout to directly display the text content, But Boringlayout can only display single-line text, so the best option here is to use the staticlayout directly
We chose a custom view and wanted to end up with an interface like this:
public class Staticlayoutview extends View {
private Layout Layout = null;
public void setlayout (Layout Layout) {
this.layout = Layout;
Requestlayout ();
}
@Override
protected void OnDraw (Canvas Canvas) {
super.ondraw (Canvas);
Canvas.save ();
if (layout!= null) {
Layout.draw (canvas, NULL, NULL, 0);
}
Canvas.restore ();
}
We can draw the text directly by setting the view's layout and use the layout object directly in the OnDraw method to draw the text. Here we discard the SetText method, draw the text directly through the layout, and the layout object here, we can set it up after the creation (this can be created in a separate thread), in contrast to the normal TextView SetText method, We reduce the number of settext in the consumption, can greatly improve the efficiency.
Staticlayout is very simple to create, just a given text, width, etc. can be created directly. In addition, in order to populate the Textlayoutcache, we can also draw it in a dummy canvas after creating the Staticlayout object in advance:
Staticlayout layout = new Staticlayout (testspan.getspanstring (i), Textpaint, Hardcodewidth, Alignment, 1.0f, 0f, true);
Layout.draw (Dummycanvas);
Performance comparison
Next we test the specific performance, where the testcase are placed on the GitHub: Staticlayoutview
TestCase's content is, in a ListView, display 300 items, each item is a piece of plain text, which contains a large number of Imagespan spannablestring, on both sides of the comparison, One side is the direct use of staticlayout, one side is the use of ordinary TextView, and the 300 pieces of text is not the same, the length of different, randomly generated, in Staticlayout testcase, Staticlayout are set up in advance after another thread is created, and the spannablestring is also generated beforehand.
In addition, in order to simulate the heavy background work of real app, another 3 threads were created to try to preempt CPU resources by doing floating-point budgeting.
The measurement performance of the index, ListView continuous downward scrolling, measuring the average frame rate, measured five times, the average, the final performance test results are as follows:
Testing the machine here is MX3, the left side is the direct use of the Staticlayout scheme, the right is the system's default, y-axis is fps, you can see that the use of optimized after the scheme, the frame rate has increased a lot.
References
Improving Comment Rendering on Android
This article describes how Instagram optimizes the efficiency of their textview rendering, which is also the source of the optimization method here, Instagram is also the direct use of staticlayout and the creation of layout by the method to reduce the ListView rolling process of the drop frame rate, and the effect is very significant. This article gives an explanation of the principles here and a simple implementation
Above is to the Android TextView pre-rendering data collation, follow-up continue to supplement the relevant information, thank you for your support of this site!