Android memory optimization solution materials and summary Experience Sharing

Source: Internet
Author: User

The customer's mobile phone crashes while running the application during project delivery during an image processing application in the previous company. This exception is an OutOfMemory error (OOM for short, we also had a terrible crash. Finally, we optimized the solution by collecting information and Code on the Internet. Here, I will share with you the information we have collected and summarized experiences.

The Android virtual machine is a register-based Dalvik. Its maximum heap size is generally 16 M, and some machines are 24 M. We usually see an OutOfMemory error, which is usually caused by heap memory overflow. The biggest difference between mobile development and web development is limited device resources. For general mobile applications, this resource is quite limited, and the upper limit of heap memory is only 16 Mb. Android has a saved value of 16 M (24 M for some models), which cannot be changed for common applications. When an application processes large resources, when there are a large number of media resources, such as videos and videos, for a long time, 16 m is very easy to use up, and OOM is very easy to appear.
* Android Memory leakage *
Although JAVA has a garbage collection mechanism, there are also memory leaks. If an object is no longer used in a program, but it is still referenced to it, the garbage collector cannot recycle it. Of course, the memory occupied by this object cannot be used, this causes memory leakage. If we have been running java for a long time, and this memory leakage keeps occurring, the memory will not be available at the end. Of course, the memory leakage in java is different from that in C/C ++. If the java program ends completely, all its objects will be inaccessible, and the system will be able to recycle them. Its memory leakage is limited to its own, it does not affect the entire system. The memory leakage of C/C ++ is worse. Its memory leakage is a system level, even if the C/C ++ program exits, its leaked memory cannot be recycled by the system and will never be used unless the machine is restarted.
Memory leakage of an Android app has little impact on other apps. To ensure secure and fast running of Android applications, each Android application uses a proprietary Dalvik Virtual Machine instance to run. It is incubated by the Zygote service process, that is to say, each application runs in its own process. Android allocates different memory usage ceilings for different types of processes. If the program suffers a memory leak during running, the memory usage of the application process exceeds this limit, the system will regard it as a memory leak and kill it, which will kill only its own processes without affecting other processes (if there is a problem with system processes such as system_process, this will cause the system to restart.) This is where our application crashes and we will see OOM.
Generally, there are several common causes for android:
1. The database cursor is not closed.
2. the cache contentview is not used to construct the adapter.
3. unregisterReceiver () is not called after registerReceiver () is called ().
4. InputStream/OutputStream is not disabled.
5. recycle () is not called after Bitmap is used ().
6. Context leakage.
7. static keywords.
Here we will illustrate these one by one:
* 1. First, Let's explain static. This is the source of all evil *
Static is a keyword in Java. When it is used to modify a member variable, the variable belongs to the class, rather than the instance of the class. Many programmers like to modify variables with the keyword "static", because it greatly prolongs the life cycle of the variables and is extremely convenient during access. They can directly access the variables with class names, it is extremely convenient to transmit values between resources, so it is often used by us. However, if you use it to reference instances that consume too much resources (the Context is the most common), you must be cautious.

 

Public class ClassName {
Private static Context mContext;
// Omitted
}



The above code is very dangerous, if the Activity is assigned to mContext. Even if the Activity has been onDestroy, but there are still objects to save its reference, the Activity will not be released, and if the activity contains some resources, that's terrible.
The above is a direct reference leak. Let's look at an example in the google document.

Private static Drawable sBackground;

@ Override
Protected void onCreate (Bundle state ){
Super. onCreate (state );

TextView label = new TextView (this );
Label. setText ("Leaks are bad ");

If (sBackground = null ){
SBackground = getDrawable (R. drawable. large_bitmap );
}
Label. setBackgroundDrawable (sBackground );

SetContentView (label );
}


SBackground is a static variable, but we found that we did not explicitly Save the reference of Contex. However, when Drawable is connected to View, drawable sets the View as a callback. Because the View contains the reference of Context, we still save the reference of Context. The reference chain is as follows:
Drawable-> TextView-> Context
Therefore, the Context is not released and Memory leakage occurs.
So how can we avoid such leaks?
First, we should try our best to avoid using static member variables to reference instances that consume too much resources, such as Context.
Second, use the Application Context whenever possible. Because the Application Context has a long life cycle, it will not cause memory leakage if referenced.
Third, use WeakReference instead of strong reference. For example, you can use WeakReference <Context> mContextRef;
For details about this part, refer to the Article in the Android document.
* 2. Context leakage *
In the static leak mentioned in the first article, we have summarized most of the context leaks. In addition to this static leak context method, there is also a memory leak caused by internal classes holding external objects, it is usually caused by internal threads.

Public class BasicActivity extends Activity {
@ Override
Public void onCreate (Bundle savedInstanceState ){
Super. onCreate (savedInstanceState );
SetContentView (R. layout. main );
New MyThread (). start ();
}

Private class OneThread extends Thread {
@ Override
Public void run (){
Super. run ();
// Do somthing
}
}
}

 

This code is usually very simple and is often used. Let's think about a problem: Assuming the OneThread run function is a very time-consuming operation, when we enable this thread, we will change the secondary landscape to a vertical screen, generally, when the screen is switched, the Activity will be re-created. According to our ideas, the old Activity should be destroyed. However, this is not the case.

Because our thread is an internal class of the Activity, OneThread stores a reference of the Activity. When the OneThread run function does not end, OneThread will not be destroyed, therefore, the old Activity referenced by it will not be destroyed, so Memory leakage occurs.
Some people like to use AsyncTask provided by Android, but in fact AsyncTask is more serious. This kind of memory leakage problem occurs only when the run function does not end, however, the internal implementation mechanism of AsyncTask is to use 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 the internal class of the Activity, therefore, it is generally not recommended to use AsyncTask as an internal class.
How can we solve the above Memory leakage problem?
First, change the internal class of the thread to the static internal class. And pay attention to the second one.
Second, use weak references within the thread to save Context references.
* 3. bitmap Memory leakage *
It can be said that the vast majority of people who encounter the OutOfMemory problem are due to the Bitmap problem. Because Bitmap occupies too much memory, it is a "super fat man", especially a high-resolution image. If you want to display multiple images, the problem is even more significant.
How can we solve the memory problems brought by Bitmap?
1. Timely destruction.
Although the system can confirm that the memory allocated by Bitmap will eventually be destroyed, it may exceed the java heap limit because it occupies too much memory. Therefore, when using Bitmap, You need to delete the recycle in time. Recycle is not sure that Bitmap will be released immediately, but it will imply to the Virtual Machine: "This image can be released." The other is that although recycle () is from the source code, calling it should immediately release the main memory of Bitmap, but the test shows that it does not immediately release the memory. Therefore, we also need to manually set it to NULL to greatly accelerate the release of the main memory of Bitmap ..
As follows:

If (! Bitmap. isRecycled ()){
Bitmap. recycle ()
}



Second, set a certain sampling rate.
Sometimes, the area we want to display is very small and it is not necessary to load the entire image. Instead, we only need to record a reduced image. At this time, we can set a certain sampling rate, therefore, the occupied memory can be greatly reduced. The following code:

Private ImageView preview;
BitmapFactory. Options options = new BitmapFactory. Options ();
Options. inSampleSize = 2; // The image width and height are 1/2 of the original image, that is, the image is 1/4 of the original image.
Bitmap bitmap = BitmapFactory. decodeStream (cr. openInputStream (uri), null, options );
Preview. setImageBitmap (bitmap );
Third, use SoftRefrence)
Sometimes, we do not retain references to Bitmap, so we cannot call the Recycle function. By using soft references, Bitmap can be effectively released when the memory is insufficient. For example:
Private class MyAdapter extends BaseAdapter {

Private ArrayList> mBitmapRefs = new ArrayList> ();
Public View getView (int I, View view, ViewGroup viewGroup ){
View newView = null;
If (view! = Null ){
NewView = view;
} Else {
NewView = (View) mInflater. inflate (R. layout. image_view, false );
}

Bitmap bitmap = BitmapFactory. decodeFile (mValues. get (I). fileName );
MBitmapRefs. add (new SoftReference (bitmap); // add ArrayList here
(ImageView) newView). setImageBitmap (bitmap );

Return newView;
}
}

 


There is a SoftHashMap tool class in the open-source community. This idea is well adopted. We can use this container to store these large memory resources.
* 4. InputStream/OutputStream not disabled *
This is not much to say. We need to close the input and output streams after the operation.
* 5. unregisterReceiver () is not called after registerReceiver () is called ().*
BroadcastReceiver is often used in applications. You can send broadcast notifications to UI updates after a multi-threaded task is completed, or you can receive system broadcasts to implement some functions.
You can register by code:

IntentFilter postFilter = new IntentFilter ();
PostFilter. addAction (getPackageName () + ". background. job ");
This. registerReceiver (receiver, postFilter );



When the registerReceiver () method is used in the Activity to register BroadcastReceiver, you must call the unregisterReceiver () method to cancel registration within the Activity lifecycle.
That is to say, the registerReceiver () and unregisterReceiver () methods must be paired. Generally, We can override the onDestory () of the Activity ().
* 6. No cache contentview is used to construct the adapter *
When there are thousands of sub-items in a listview, if we do not adopt certain policies to reuse these resources, the memory of the application is far from enough.
When inheriting the BaseAdapter, The getView (int position, View convertView, ViewGroup parent) method will be rewritten,
The second parameter convertView is the reusable object we will use.
Here we will only talk about how to use it. For more information about performance testing, see:
Principle of getView in ListView + how to place multiple items in ListView
Http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html
ListView Adapter Optimization for Android Development
Http://shinfocom.iteye.com/blog/1231511
* 7. The database cursor is not closed *
Cursor Is a class used to manage data sets after data is queried by Android. Normally, if the queried data volume is small, there will be no memory problems, moreover, the virtual machine can ensure that the Cusor will be released eventually.
However, if the data size of the Cursor table is large, especially if Blob information exists, the memory occupied by the Cursor should be promptly released, rather than waiting for GC to process. In addition, Android obviously prefers programmers to manually close the Cursor, because in the source code, we find that if the garbage collector is used for collection, an error will be prompted to the user.
Therefore, the method of using Cursor is generally as follows:

Cursor cursor = null;
Try {
Cursor = mContext. getContentResolver (). query (uri, null, null );
If (cursor! = Null ){
Cursor. moveToFirst ();
// Do something
}
} Catch (Exception e ){
E. printStackTrace ();
} Finally {
If (cursor! = Null ){
Cursor. close ();
}
}



In one case, we cannot directly close Cursor, which is applied in CursorAdapter. But note that CursorAdapter does not automatically close Cursor at the end of Acivity, therefore, you need to manually disable it in the onDestroy function.

@ Override
Protected void onDestroy (){
If (mAdapter! = Null & mAdapter. getCurosr ()! = Null ){
MAdapter. getCursor (). close ();
}
Super. onDestroy ();
}


The changeCursor function in the CursorAdapter will release the original Cursor and replace it with the new Cursor, so you don't have to worry that the original Cursor is not closed.
You may think of using Activity managedQuery to generate Cursor, so that the life cycle of Cursor will be consistent with that of Acitivity. What a perfect solution! However, managedQuery also has many limitations.
The Cursor generated by managedQuery must not be replaced, because many programs may not have certain query conditions. Therefore, we often use the new query Cursor to replace the original Cursor. Therefore, this method has a small scope of application.
* Conclusion *
There are many methods and requirements to reduce memory usage. For example, do not use the entire image. Try to use a 9path image. The Adapter must use convertView and so on, which can save memory for many details. These all need to be mined. Who is called Android memory. In fact, the most important thing to end with is correct use and standardized programming.

Related Article

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.