Android Miscellaneous -- Memory leakage (1) -- contentView Cache Usage and ListView Optimization

Source: Internet
Author: User

 
There are many causes for Android Memory leakage. The problems listed below will be solved one by one in the future.
1. The convertView cache is not used when the Adapter is constructed (the ListView optimization problem is derived)
2. the query database cursor is not closed
3. the lifecycle object in the Activity is greater than the Activity lifecycle (Application Context and Activity Context)
4. The Bitmap object is not recycle when it is not in use (there are other solutions here)
 
Today we are talking about the first one: how to use cache to optimize ListView
If you do not use the cached convertView, the View will be re-created every time you call getView, so that the previous View may not be destroyed, and the memory leakage will inevitably occur when you create a new View.
Three solutions are available when getView is used: (1) convertView is not used; (2) convertView is used; (3) convertView + static ViewHolder class
 
I did a test. The code below creates 2000 views, from 0 to the end, calculates the total consumption, and displays the memory size released by GC. The results of the three tests are as follows:
Note: here we will first describe GC_EXTERNAL_ALLOC freed 7 K, 18% free 11153 K/13511 K, external 1632 K/1672 K, paused 89 ms
In Dalvik, the memory allocated for a program varies according to the model, generally 32 M, and the virtual opportunity allocates these memories separately, heap memory used by JAVA and external memory used by Nativie (that is, the memory allocated by malloc in the Nativie class called by JNI in the virtual machine, such as Bitmap and java. nio. byteBuffers ). However, the two are shared differently. That is to say, Native memory is insufficient, while JAVA memory is insufficient to apply to JAVA. You must apply to the virtual machine, an OOM error is reported when the VM cannot be assigned.
Freed 7 k: indicates that GC has released 7 k of memory
18% free 11153 K/13511 K: indicates the heap memory used by JAVA (the object exists here), and 18% free indicates the remaining 18% heap memory ), 11153K indicates the heap memory used currently, and 13511K indicates the total heap memory size (this part is incorrect in some articles on the Internet, and many reposts are the same)
External 1632 K/1672 K: 1632K indicates that external memory has been used, a total of 1672 K external memory (Note: This may only exist before Android 3.0)
Paused 89 ms: There are two parts: one is the pause time before GC is called, and the other is the pause time when GC is basically completed after GC is called.
For details, refer to: http://stackoverflow.com/questions/4550757/android-logs-gc-external-alloc-gc-for-malloc
 
(1) convertView is not used
It is not recommended to write this statement because there is no processing. If the amount of data is small, but if the list item has a large amount of data, the View will be re-created every time, set resources, seriously affecting performance, so this method is not used from the very beginning.
@ Override
Public View getView (int position, View convertView, ViewGroup parent ){
// Get a View that displays the data at the specified position in the data set.
// Start timing. The performance test with nanoTime will be more accurate, because it is a nanosecond-level
Long startTime = System. nanoTime ();
View item = mInflater. inflate (R. layout. list_item, null );
ImageView img = (ImageView) item. findViewById (R. id. img );
TextView title = (TextView) item. findViewById (R. id. title );
TextView info = (TextView) item. findViewById (R.id.info );
Img. setImageResource (R. drawable. ic_launcher );
Title. setText ("loulijun ");
Info. setText ("");

// Stop timing
Long endTime = System. nanoTime ();
// Time consumed
Long spendTime = (endTime-startTime );

SumTime + = spendTime;
Log. d ("GoogleIO", "position at:" + position + "-- sumTime:" + String. valueOf (sumTime ));
Return item;
}
Test results:
Currently, the VM only allocates 5767 K + K of memory for them, while the peak memory is 32 M.
At the beginning, heap memory only applied for 5767 kb and used 3353 KB of memory. Note the data size change: Time consumed: 167633055ns = 0.167633055 seconds.

 

 
At the time of pulling to 1000, the total heap memory has been applied for 9607 kb, And the used memory is 7245 kb, which is much larger than the first time. The time consumed is 3435241667ns = 3.435241667 seconds.

When it reaches 2000, the total heap memory is 13511 kb, And the used memory is 11153 kb. Time consumed: 6660213835ns = 6.660369835 seconds.

--------------------------- I created another 10000 ListView and tested it until the memory leak. It turns out that the peak value is 32 MB, instead of convertView, causing memory leakage, when the memory leaks, the mobile phone will prompt force close and write the error to/data/anr/traces.txt. You can view the specific information by using adb pull.

 


 

(2) test data after convertView (after optimization)
By caching convertView, convertView can cache convertView within the visible range. When sliding down again, it starts to update the View. This way, by caching convertView, you can determine that a View is created only when no View exists in the cache, if a View already exists, you can use the View in the cache, which reduces the number of View creation and improves the performance.
 
@ Override
Public View getView (int position, View convertView, ViewGroup parent ){
// Get a View that displays the data at the specified position in the data set.
If (convertView = null)
{
ConvertView = mInflater. inflate (R. layout. list_item, null );
}
// Start timing. The performance test with nanoTime will be more accurate, because it is a nanosecond-level
Long startTime = System. nanoTime ();

ImageView img = (ImageView) convertView. findViewById (R. id. img );
TextView title = (TextView) convertView. findViewById (R. id. title );
TextView info = (TextView) convertView. findViewById (R.id.info );
Img. setImageResource (R. drawable. ic_launcher );
Title. setText ("loulijun ");
Info. setText ("");

// Stop timing
Long endTime = System. nanoTime ();
// Time consumed
Long spendTime = (endTime-startTime );

SumTime + = spendTime;
Log. d ("GoogleIO", "position at:" + position + "-- sumTime:" + String. valueOf (sumTime ));
Return convertView;
}
 
I still use 2000 for testing data. 10000 is too big (10 thousand years long, just for the evening)
Test results:
This pull is much smoother than it was just now, and the number of times GC releases the memory is much less. The last time used and the memory used is much smaller, which is indeed much better after optimization.

When the position is 1000, GC is not called in the vicinity. The time interval is 0.213653551 seconds. The difference is a little large. When the position reaches 1000, it takes as many as 3.43 seconds.

When the position is 2000, the memory used is only 3068 kb, the total heap memory is 6215 kb, and the external memory is 0 kb. the time used is 378326213ns = 0.378326396 seconds. The performance gap is so high, A little unbelievable. I don't know if this method is correct. If there is anything wrong with it, I hope Daniel can give a correct answer.

 

 

(3) Use contentView + static class ViewHolder class
Implemented through convertView + ViewHolder, ViewHolder is a static class. The key advantage of ViewHolder is that it caches the View of the displayed data and accelerates the UI response speed.
When convertView = null is determined, if it is null, it will assign a value to convertView according to the Item layout (XML) of the designed List, and generate a viewHolder to bind various View Controls in converView (those controls in XML layout ). Use the setTag of convertView to set viewHolder to the Tag, so that the system can retrieve it from the Tag when drawing the ListView for the second time. (See the following code)
If convertView is not empty, the getTag () of convertView is used directly to obtain a ViewHolder.
Static class ViewHolder
// Define the static class ViewHolder
Static class ViewHolder
{
Public ImageView img;
Public TextView title;
Public TextView info;
}
@ Override
Public View getView (int position, View convertView, ViewGroup parent ){
// Get a View that displays the data at the specified position in the data set.

// Start timing. The performance test with nanoTime will be more accurate, because it is a nanosecond-level
Long startTime = System. nanoTime ();
ViewHolder holder;

If (convertView = null)
{
Holder = new ViewHolder ();
ConvertView = mInflater. inflate (R. layout. list_item, null );
Holder. img = (ImageView) convertView. findViewById (R. id. img );
Holder. title = (TextView) convertView. findViewById (R. id. title );
Holder.info = (TextView) convertView. findViewById (R.id.info );
ConvertView. setTag (holder );
} Else
{
Holder = (ViewHolder) convertView. getTag ();
Holder. img. setImageResource (R. drawable. ic_launcher );
Holder. title. setText ("loulijun ");
Holder.info. setText ("");
}

// Stop timing
Long endTime = System. nanoTime ();
// Time consumed
Long spendTime = (endTime-startTime );

SumTime + = spendTime;
Log. d ("GoogleIO", "position at:" + position + "-- sumTime:" + String. valueOf (sumTime ));
Return convertView;
}
At this point, someone may ask if there is any difference between ViewHolder static classes combined with the cache convertView and directly using convertView. Are there any duplicates?
The official explanation is provided here.
Two methods to improve the Adapter
To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView () to avoid inflating View when it is not necessary
Reuse the cached convertView and pass it to the getView () method to avoid filling in unnecessary views)
-It uses the ViewHolder pattern to avoid calling findViewById () when it is not necessary
Use the ViewHolder mode to avoid unnecessary calls to findViewById (): Too Many findviewbyids will also affect the performance)
Role of ViewHolder class
-The ViewHolder pattern consists in storing a data structure in the tag of the view
Returned by getView (). This data structures contains references to the views we want to bind data,
Thus avoiding calling to findViewById () every time getView () is invoked
The ViewHolder mode stores a Data Structure in the Tag returned by the getView () method. This data structure includes
To bind a reference to the view of data, so as to avoid calling findViewById () Every time getView () is called ())
 
Test data: (similar to directly using convertView data)

When the position is 1000, it takes 0.199188216 seconds: 199188216ns = seconds. There is no better heap memory than convertView.

When the position is 2000, the time is 3 seconds 69887ns = 0.336669887 seconds, which is a little better than convertView. However, the performance is similar.

 



By Lou Lijun

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.