ListView optimization

ListView optimization


Familiar with listview optimization, asynchronous loading of network pictures, batch loading, paging display, picture caching and other optimization methods

1. let's understand the working principle of ListView (see ), as shown in the figure:
ListView For each item, the adapter is required to "return a view" (getView), which means that the ListView is in When starting to draw, the system first calls the getCount() function to get the length of the ListView according to its return value, and then calls getView() to draw each item of the ListView line by line according to this length. If your getCount() returns a value of 0, no one row of the list will be displayed. If it returns 1, only one row will be displayed. Return a few to display a few lines. What if we have tens of thousands or even more items to display? Create a new View for each Item? impossible! ! ! In fact, Android has already cached these views. You can take a look at the screenshot below to understand. This picture is the most classic picture explaining the working principle of ListView. You can save it and take a look when you don t understand it to deepen your understanding. In fact, there is a component called Recycler in Android. By the way, I will list the things related to Recycler that have been optimized by Google. For example: AbsListView.RecyclerListener, ViewDebug.RecyclerTraceType, etc., friends who want to know check it out by themselves. It is not difficult to understand, the following figure shows the working principle of ListView loading data (click on the schematic diagram to see the big picture):

![How ListView loads data]( 60723184023200)

1. If you have tens of thousands or even more options (item), only visible items exist in the memory (memory memory, the optimization is the optimization in the memory!!!), the others are In Recycler
2, ListView first requests a type1 view (getView) and then requests other visible items. convertView is null in getView
3. When item1 rolls out of the screen and a new item comes up from the lower end of the screen, ListView requests another type1 view. convertView is not empty at this time, its value is item1. You only need to set new data and then return to convertView, without having to re-create a view

1. Reuse convertView to reduce the number of findViewById
1. Optimization 1: Reuse convertView The
Android system itself considers the optimization of ListView for us. In the duplicated Adapter class, the two more important methods are getCount() and getView (). How many bars are displayed on the interface, how many times the getView() method will be called; therefore, if the method of View.inflate( .) is used every time if optimization is not performed every time it is called, the method of View.inflate( .) will be used. The xml file is parsed and displayed on the interface, which is very resource intensive: because new content is generated, the old content will be destroyed, so the old content can be reused.
In the getView() method, the system provides us with a historical cache object convertView that reuses views. When the first screen is displayed, each item will create a new view object, and these views can be copied. Used; if you have to create a view every time you display it, it is very memory consuming; so in order to save memory, you can reuse it when the convertView is not null.
2. Optimization 2: Cache the item entry reference ViewHolder
findViewById() This method is a more performance-consuming operation, because this method needs to find the specified layout file and continuously parse each node: from the top node, perform a layer-by-layer analysis and query, and find the next one. Layer by layer, if it is not found on the left, it will then parse the right and perform corresponding queries until the location is found (as shown in the figure). Therefore, findViewById can be optimized. Note that:
"""" Features: When the xml file is parsed, as long as it is created, the child's id will not change. According to this feature, the child id can be stored in the specified collection, and the corresponding element in the collection can be directly taken out every time.
When creating a view object, reduce the number of times that the layout file is converted into a view object; that is, when creating a view object, find all the children and
store the references of the children
. Define the class ViewHolder that stores the control reference here. ViewHolder The class does not need to be defined as static. It depends on the actual situation. If there are not many items, you can use it. In this way, you can only load it once during initialization, and you can get a little optimization. However, if there are too many items, it is recommended not to use it. Because static is a keyword in Java, when it is used to modify a member variable, then the variable belongs to the class, not an instance of the class. Therefore, a variable modified with static has a very long life cycle. If you use it to refer to some instances that consume too much resources (such as the case of Context the most), you should try to avoid using it at this time.
class ViewHolder{
//Define the corresponding controls in the item
Create a custom class: ViewHolder holder = null; Add
the child view to the holder: when creating a new listView, create a new ViewHolder, all children Find and save the reference of the child. Set the reference to the view through view.setTag(holder)
. Set the child view to this holder through the holder, thereby reducing the number of subsequent queries.
Reusing the items in the listView At that time, through view.getTag(), the view object is converted into holder, that is, converted into the corresponding reference, which is convenient for storing in the collection when it is used next time.
Get the reference through view.getTag(holder) (requires forced conversion)

2. Batch and paging loading of data in ListView:
Requirements: ListView has ten thousand pieces of data, how to display it; if one hundred thousand pieces of data are loaded into memory, it consumes memory.
Optimize the data to be queried: get a few pieces of data first Displayed on the interface
for batch processing optimized user experience
, paging processing optimized memory space
Generally, data is obtained from the database. To load data in batches (paging), you need to load data in the corresponding DAO There are corresponding methods to obtain data in batches (paged), such as findPartDatas ()
1. Prepare data:
add a method to load data in batches in dao: findPartDatas ()
load the first batch of data when adapting data When the second batch of data needs to be loaded, set the listener to detect when the second batch is loaded.
2. Set the scroll listener of ListView: setOnScrollListener(new OnScrollListener{....})
. There are two methods in the listener: scroll state The method that changes (onScrollStateChanged) and the method that is called when the listView is scrolled (onScroll)
. In the method of changing the scrolling state, there are three states: the state where the
finger is pressed and moved:
inertial scrolling (gliding) (Flgin) state):
static state: 3. Deal with different states:

load data in batches, only care about the static state: care about the last visible item, if the last visible item is the last one in the data adapter (collection), more data can be loaded at this time . At each load, the number of scrolls is calculated. When the number of scrolls is greater than or equal to the total number, the user can be prompted that there is no more data.

Processing III complex to be further summarized the ListView :()
listView interface display by two methods getView getCount and controlled
getCount: Returns the number of entries
getView: returns each location entry displayed content
to provide ideas:
Optimized processing for items with multiple types: Since ListView has only one Adapter entry, you can define a total Adapter entry and store various types of Adapters. Take the process management function in the security guard as an example. The effect is shown in the figure:
1. Define two (or more) sets.
Each set stores different types of content (here: user program (userAppinfos) and a collection of system programs (systemAppinfos))
2. In the initialization Initialize two sets in the data (fill data). For
example, here is the initialization in the fillData method.
3. In the data adapter, copy the corresponding method
getCount(): Calculate the number of items that need to be displayed, including listView and textView
getView (): Perform if processing on items displayed in different positions.
4. Judgment of data type. It
should be noted that when reusing views, you need to judge the type of convertView because there are various types of views. When the view is scrolled, different types of views cannot be reused, all need to be judged

4. the optimization of pictures in ListView: look at the optimization of pictures in OOM exceptions.
1. How to handle pictures:
If there are pictures involved in the custom item, you must deal with pictures vigorously. The memory occupied by pictures is ListView The most disgusting of the items, there are roughly the following ways to process pictures:
, do not directly take the path and loop decodeFile(); use Option to save the size of the picture, do not load the picture to the memory
, the picture must be passed through Boundary compression
, when taking pictures in ListView, don t take a path to take pictures directly, but use WeakReference (use WeakReference instead of strong references. For
example, you can use WeakReference mContextRef), SoftReference, WeakHashMap, etc. to store the picture information, which is a picture Information is not a picture!
. When doing picture conversion in getView, the intermediate variables generated must be released in time.
2. The basic idea of asynchronously loading pictures:
1). First obtain the picture display from the memory cache (memory buffer)
2). If not, use the SD card Get it in (SD card buffering)
3) If you can t get it, download the picture from the network and save it to the SD card at the same time add it to the memory and display it (depending on the situation, whether you want to display it)

Optimization 1: First load from the memory, if not, start the thread to get it from the SD card or the network. Here, please note that getting pictures from the SD card is executed in the sub-thread, otherwise the screen will not be smooth enough to slide quickly.
Optimization 2: At the same time, there is a busy variable in the adapter, which indicates whether the listview is in the sliding state. If it is in the sliding state, only the picture is obtained from the memory. If not, there is no need to open the thread to external storage or network to obtain the picture.
Optimization 3: The threads in ImageLoader use the thread pool, which avoids the frequent creation and destruction of too many threads. Some children's shoes always execute a new thread every time. This is very undesirable. Better use the AsyncTask class. In fact, the thread pool is also used internally. When getting a picture from the network, first save it to the sd card and then load it into the memory. The advantage of this is that it can be compressed when loaded into the memory to reduce the memory occupied by the picture.
Tips: There may be a problem of picture jumping (dislocation) here:
the nature of the picture misalignment problem stems from the use of cache convertView in our listview. Assuming a scene, a listview displays nine items on one screen, then the tenth item is pulled out When the item is used, the item actually reuses the first item, which means that when the first item downloads the picture from the network and finally displays it, the item is no longer in the current display area. At this time As a result of the display, the image may be output on the tenth item, which leads to the problem of image misalignment. So the solution is to display if it is visible, and not display if it is invisible. There is a map object of imageViews in ImageLoader, which is used to save the url set corresponding to the image of the current display area, which can be judged and processed before display.

3. Memory buffer mechanism:
First limit the heap memory size of the memory image buffer, and determine whether it exceeds the limit size every time a picture is added to the cache, and if it exceeds the limit, take out the least used picture from it and remove it.
Of course, if this method is not adopted here, it is also feasible to change to soft references. Both of the purposes are to maximize the use of the image cache that already exists in the memory to avoid repeated garbage production and increase the GC burden; OOM overflows are often caused by transient memory. Large increase and the garbage collection is not timely caused. The only difference between the two is that the picture cache in LinkedHashMap will not be reclaimed by GC before it is removed, while the picture cache in SoftReference will be reclaimed by GC at any time when there is no other reference to save. Therefore, using LinkedHashMap, the LRU algorithm cache, is more conducive to effective image hits. Of course, the two are used together to achieve better results, that is, the cache removed from LinkedHashMap is placed in SoftReference, which is the second-level cache of the memory.

This example uses the LRU algorithm, first look at the implementation of MemoryCache

public class MemoryCache { private static final String TAG = "MemoryCache"; //It is a synchronous operation when placed in the cache //The last parameter of the LinkedHashMap construction method, true, means that the elements in this map will be arranged from least to more recently used, that is, LRU //The advantage of this is that if you want to replace the elements in the cache, first traverse the least recently used elements to replace to improve efficiency private Map<String, Bitmap> cache = Collections .synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true)); //The bytes occupied by the pictures in the cache, initially 0, the heap memory occupied by the cache will be strictly controlled through this variable private long size = 0;//current allocated size //The maximum heap memory that the cache can only occupy private long limit = 1000000;//max memory in bytes public MemoryCache() { //use 25% of available heap size setLimit(Runtime.getRuntime().maxMemory()/10); } public void setLimit(long new_limit) { limit = new_limit; Log.i(TAG, "MemoryCache will use up to "+ limit/1024./1024. + "MB"); } public Bitmap get(String id) { try { if (!cache.containsKey(id)) return null; return cache.get(id); } catch (NullPointerException ex) { return null; } } public void put(String id, Bitmap bitmap) { try { if (cache.containsKey(id)) size -= getSizeInBytes(cache.get(id)); cache.put(id, bitmap); size += getSizeInBytes(bitmap); checkSize(); } catch (Throwable th) { th.printStackTrace(); } } /** * Strictly control the heap memory, if it exceeds, the least recently used picture cache will be replaced first * */ private void checkSize() { Log.i(TAG, "cache size=" + size + "length=" + cache.size()); if (size> limit) { //first traverse the least recently used element Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator(); while (iter.hasNext()) { Entry<String, Bitmap> entry =; size -= getSizeInBytes(entry.getValue()); iter.remove(); if (size <= limit) break; } Log.i(TAG, "Clean cache. New size "+ cache.size()); } } public void clear() { cache.clear(); } /** * Memory used by pictures * <a href="\"\"" target="\"_blank\"">@Param</a> bitmap * @return */ long getSizeInBytes(Bitmap bitmap) { if (bitmap == null) return 0; return bitmap.getRowBytes() * bitmap.getHeight(); } } Copy code

5. Other optimizations of ListView:
1. Try to avoid using static in BaseAdapter to define global static variables:
static is a keyword in Java. When it is used to modify member variables, then the variable belongs to the class instead of An instance of this class. Therefore, a variable modified with static has a very long life cycle. If you use it to refer to some instances that consume too much resources (such as the case of Context the most), you should try to avoid using it at this time.
2. Use getApplicationContext as much as possible:
If you must use Context in order to meet your needs: Context should use Application Context as much as possible, because Application Context has a relatively long life cycle, and reference to it will not cause memory leaks.
3. Try to avoid using it in the ListView adapter. Thread:
Because the main reason for thread memory leaks is the uncontrollable thread life cycle. The previously used custom ListView used AsyncTask to start the thread by itself when adapting data. This is more dangerous than using Thread, because Thread only has this kind of memory leakage problem when the run function does not end. However, the internal implementation mechanism of AsyncTask is to use In addition to the thread execution pool (ThreadPoolExcutor), the life cycle of the Thread object generated by this class is uncertain and cannot be controlled by the application. Therefore, if AsyncTask is used as an internal class of Activity, it is more prone to memory leaks. The solution is as follows:
. Change the internal class of the thread to a static internal class.
, use weak references to save Context references within the thread