ANDROID memory optimization (large Summary-medium)
Preface:
The idea of this article mainly draws on a speech PPT at the AnDevCon Developer Conference in 2014, and summarizes, selects, and simplifies the various memory scattered knowledge points collected on the Internet.
So I will define this article as a tool-type article. If you encounter memory problems in ANDROID development, or want to attend an interview immediately, or simply learn or review memory-related knowledge, all are welcome to read. (At the end of this article, I will try my best to list the articles for reference ).
OOM:
Memory leakage can cause many problems:
1. Program freezing and slow response (when memory usage is high, JVM virtual opportunities frequently trigger GC)
2. Inexplicably disappear (the larger the memory occupied by your program, the more likely it will be killed in the background. Otherwise, the smaller the memory usage, the longer the time it takes to exist in the background)
3. Direct crash (OutOfMemoryError)
Problems with ANDROID memory:
1. Limited heap memory, originally only 16 MB
2. Memory size consumption varies depending on the device, operating system level, and screen size
3. The program cannot be directly controlled
4. Support for background multi-task processing (multitasking)
5. Run on a virtual machine
5R:
This article mainly uses the following 5R method to optimize the ANDROID memory:
1. Reckon (Computing)
First, you need to know the memory usage of your app.
2. Reduce)
Consume less resources
3. Reuse)
After the first use, try to use
5. Recycle (Recycle)
Returns the resource to the production stream.
4. Review (check)
Review and check your program to see what is unreasonable in the design or code.
Reckon:
For memory introduction, and Reckon (memory computing) content, please refer to the previous article: ANDROID memory optimization (big Summary-on)
Reduce:
Reduce means Reduce. directly reducing memory usage is the most effective optimization method.
Let's take a look at the methods to reduce memory usage:
Bitmap
:Bitmap is a large memory consumer. The vast majority of OOM crashes occur during Bitmap operations. Let's take a look at how to process images:
Image Display:
We need to load the image size as needed.
For example, you can load a thumbnail (thumbnails) only when previewing in the list ).
Only when you click a specific entry to view details, a fragment/activity/dialog box is started to display the entire image.
Image Size:
Using ImageView directly shows that bitmap occupies a large amount of resources, especially when the image size is large, it may cause a crash.
UseBitmapFactory. OptionsSet inSampleSize to reduce requirements on system resources.
The property value inSampleSize indicates that the thumbnail size is one of several points of the original image size. If the value is 2, the width and height of the thumbnail are 1/2 of the original image, the image size is 1/4 of the original size.
BitmapFactory. options bitmapFactoryOptions = new BitmapFactory. options (); bitmapFactoryOptions. inJustDecodeBounds = true; bitmapFactoryOptions. inSampleSize = 2; // It must be set back to false here, because we set it to true before // After setting inJustDecodeBounds to true, decodeFile does not allocate space, that is, the Bitmap decoded by BitmapFactory is Null, but the length and width of the original image can be calculated as options. inJustDecodeBounds = false; Bitmap bmp = BitmapFactory. decodeFile (sourceBitmap, options );
Image pixels:
In Android, images have four attributes:
ALPHA_8:Each pixel occupies 1 byte of memory
ARGB_4444:Each pixel occupies 2 bytes of memory
ARGB_8888:Each pixel occupies 4 bytes of memory (default)
RGB_565:Each pixel occupies 2 bytes of memory
The default color mode for Android is ARGB_8888. This color mode features the most delicate colors and the highest display quality. However, the memory is also the largest. Therefore, when the image effect is not particularly high, use RGB_565 (565 has no transparency attribute), as shown below:
PublicstaticBitmapreadBitMap (Contextcontext, intresId) {BitmapFactory. optionsopt = newBitmapFactory. options (); opt. inPreferredConfig = Bitmap. config. RGB_565; opt. inPurgeable = true; opt. ininputtransferable = true; // obtain the resource image InputStreamis = context. getResources (). openRawResource (resId); returnBitmapFactory. decodeStream (is, null, opt );}
Image recycling:
After using Bitmap, you need to callBitmap. recycle ()To release the memory space occupied by Bitmap, instead of waiting for the Android system to release.
The following is a sample code snippet for releasing Bitmap.
// Determine whether the if (bitmap! = Null &&! Bitmap. isRecycled () {// recycle and set it to null bitmap. recycle (); bitmap = null;} System. gc ();
Capture exceptions:
After the above optimization, there will be risks of OOM reporting, so we need a final level-capture OOM exception:
Bitmap bitmap = null; try {// instantiate Bitmap bitmap = BitmapFactory. decodeFile (path);} catch (OutOfMemoryError e) {// capture OutOfMemoryError to avoid direct crash} if (bitmap = null) {// If the instantiation fails, the default Bitmap object return defaultBitmapMap;} is returned ;}
Modify Object Reference Type:
Reference Type:
There are four levels of reference. The four levels are from high to low: strong reference> soft reference> weak reference> Virtual Reference.
Strong reference)
For example: Object object = new Object (), an object is a strongly referenced Object. When the memory space is insufficient, the Java Virtual Machine would rather throw an OutOfMemoryError to terminate the program abnormally, and does not recycle strongly referenced objects at will to solve the problem of insufficient memory.
SoftReference)
It is often used for caching when the memory is not enough. When the memory reaches a threshold value, GC recycles it;
WeakReference)
Objects with weak references have a shorter life cycle. When the Garbage Collector thread scans the memory area under its jurisdiction, its memory will be recycled no matter whether the current memory space is sufficient or not.
PhantomReference)
Virtual references, as the name implies, are similar to virtual ones. Different from other types of references, virtual references do not determine the object lifecycle. If an object only holds a virtual reference, it is the same as no reference and may be recycled at any time.
Application Instances of soft reference and weak reference:
In the development of Android applications, to prevent memory overflow, when processing objects that occupy a large amount of memory and have a long Declaration cycle, you can try to apply soft references and weak references.
The following uses soft references as an example to describe in detail (the usage of weak references is similar to that of soft references ):
Suppose our application will use a large number of default images, and these images will be used in many places. If you read an image each time, hardware operations are required to read the file, resulting in low performance. Therefore, we want to cache the image and read it directly from the memory when necessary. However, because the image occupies a large amount of memory and many images need a lot of memory to cache, The OutOfMemory exception may easily occur. At this time, we can consider using soft reference technology to avoid this problem.
First, define a HashMap to save the soft reference object.
private Map
> imageCache = new HashMap
>();
Define a method to save the soft reference of Bitmap to HashMap.
Public void addBitmapToCache (String path) {// strongly referenced Bitmap object Bitmap bitmap = BitmapFactory. decodeFile (path); // soft referenced Bitmap object SoftReference
SoftBitmap = new SoftReference
(Bitmap); // Add the object to Map to cache imageCache. put (path, softBitmap );}
You can use the get () method of SoftReference to obtain the Bitmap object.
Public Bitmap getBitmapByPath (String path) {// obtain the soft referenced Bitmap object SoftReference from the cache
SoftBitmap = imageCache. get (path); // determines whether soft reference if (softBitmap = null) {return null;} // retrieves a Bitmap object. if Bitmap is recycled due to insufficient memory, an empty Bitmap bitmap = softBitmap will be obtained. get (); return bitmap ;}
After soft reference is used, before the OutOfMemory exception occurs, the memory space of these cached image resources can be released, so as to avoid Memory ceiling and Crash.
Note that, before the Garbage Collector recycles this Java object, the get method provided by the SoftReference class will return a strong reference to the Java object. Once the garbage thread recycles this Java object, the get method returns null. Therefore, in the Code for obtaining soft reference objects, you must determine whether the value is null to avoid application crash due to NullPointerException.
When should I use soft references and weak references?
In my opinion, if you only want to avoid the occurrence of an OutOfMemory exception, you can use soft references. If you are more concerned about application performance and want to recycle objects that occupy a large amount of memory as soon as possible, you can use weak references.
In addition, you can determine whether the object is frequently used. If this object may be frequently used, try to use soft references. If this object is not used more likely, you can use weak references.
In addition, WeakHashMap is similar to the weak reference function. For a given key, WeakHashMap does not prevent the garbage collector from recycling the key. After the key is recycled, its entries are effectively removed from the ing. WeakHashMap implements this mechanism using ReferenceQueue.
Other small tips:
Use static final modifier for Constants
Let's take a look at the Declaration of the two paragraphs before the class:
StaticInt intVal = 42;
StaticString strVal = Hello, world !;
The compiler generates an initialization class method called clinit. This method is executed when the class is used for the first time. The method will assign 42 to intVal, and then assign a reference pointing to a common table in the class to strVal. When these values are used in the future, they will be found in the member variable table. Let's make some improvements and use the keyword "final:
Static finalInt intVal = 42;
Static finalString strVal = Hello, world !;
Currently, the clinit method is no longer required for classes, because constants are directly saved to class files during member variable initialization. The code that uses intVal is directly replaced with 42, and strVal points to a String constant instead of a member variable.
Declaring a method or class as final will not improve the performance, but will help the compiler optimize the code. For example, if the compiler knows that a getter method will not be overloaded, the compiler will use inline calls to it.
You can also declare the local variable as final, which will not improve the performance. Using "final" can only make the local variables look clearer (but sometimes this is required, for example, when using anonymous internal classes ).
Static method instead of virtual Method
If you do not need to access the field of an object and set the method to static, the call will be accelerated by 15% to 20%. This is also a good practice, because you can see from the method declaration that calling this method does not need to update the state of this object.
Reduce Unnecessary global variables
Avoid using static member variables to reference instances that consume too much resources, such as Context
Because the reference of Context exceeds its own lifecycle, Context leakage may occur. Therefore, use the Context type of Application as much as possible. You can easily obtain the Application object by calling Context. getApplicationContext () or Activity. getApplication.
Avoid creating unnecessary objects
The most common example is to use StringBuffer instead of String when you want to operate a String frequently.
For the combination of all basic types: the int array is better than the Integer array, which also summarizes the basic fact that the performance of two parallel int arrays is much better than that of (int, int) object arrays.
In general, it is to avoid creating short-lived temporary objects. Reduce the creation of objects to reduce the garbage collection, and thus reduce the impact on user experience.
Avoid internal Getters/SettersIn Android, virtual method calls are much more expensive than direct field access. Generally, based on the object-oriented language practice, it makes sense to use Getters and Setters in public interfaces, but direct access should be used in classes where a field is frequently accessed.
Avoid floating point number
Generally, in Android devices, floating point numbers are twice slower than integer types.
Good object analogy Interface
Suppose you have a HashMap object. You can declare it as a HashMap or Map:
Map map1 = new HashMap();HashMap map2 = new HashMap();
Which one is better?
In the traditional view, Map is better, because you can change its specific implementation class as long as the class inherits from the Map interface. The traditional view is correct for traditional programs, but it is not suitable for embedded systems. Calling an interface takes twice as long as calling an object class. If HashMap is perfect for your program, using Map has no value. If you are not sure about some items, avoid using Map first, and leave the rest to the refactoring function provided by IDE. (Of course, a public API is an exception: A good API often sacrifices some performance)
Avoid Enumeration
It is very convenient to enumerate variables, but unfortunately it will sacrifice the execution speed and greatly increase the file size.
Using enumeration variables can make your API better and provide compile-time checks. Therefore, in general, you should select an enumeration variable for the public API. However, when performance is limited, you should avoid this practice.
For Loop
Accessing member variables is much slower than accessing local variables, as shown in the following code:
for(int i =0; i < this.mCount; i++) {}
Never call any method in the second condition of for, as shown in the following code:
for(int i =0; i < this.getCount(); i++) {}
For the above two examples, we 'd better change:
int count = this.mCount; / int count = this.getCount();for(int i =0; i < count; i++) {}
The for-each syntax introduced in java1.5. The compiler saves the array reference and length to the local variable, which is very good for accessing array elements. However, the compiler will also generate an additional storage operation for local variables in each loop (for example, variable a in the following example), which will be 4 bytes more than a normal loop, the speed is a little slower:
for (Foo a : mArray) { sum += a.mSplat;}
Understand and use the class library
Select the code in the Library instead of self-rewriting, except for the common reasons, considering that the system will use the Assembly Code call to replace the library method when the system is idle, this may be better than the best equivalent Java code generated in JIT.
When processing strings, do not hesitate to useString. indexOf (),String. lastIndexOf ()And other special implementation methods. These methods are implemented using C/C ++, which is 10 to 100 times faster than Java loops.
System. arraycopyThe self-coding cycle is 9 times faster on the Nexus One with JIT.
Android. text. formatFormatterClass, provides IP address conversion, file size conversion, and other methods; DateFormat class, provides a variety of time conversion, is a very efficient method.
TextUtilsClass. Android provides a simple and practical TextUtils class for string processing. If you do not need to think about regular expressions, try this class in android. text. TextUtils.
High PerformanceMemoryFileClass, many people complain that Android does not have very good performance in processing underlying I/O. If you do not want to use NDK, you can use the MemoryFile class to implement high-performance file read/write operations. Where does MemoryFile apply? Frequent I/O operations are mainly related to external storage. MemoryFile maps files on the NAND or SD card to the memory for modification, in this way, high-speed RAM is used to replace ROM or SD card, which naturally improves performance and reduces Power Consumption for Android phones. This class does not provide many functions. It inherits from the Object directly and runs directly on the C underlying layer through JNI.
Reuse:
Reuse is one of the important methods to reduce memory consumption. The core idea is to re-use existing memory resources to avoid creating new ones. The most typical use is
Cache
)And
Pool).
Bitmap cache:
Bitmap cache is divided into two types:
One is memory cache and the other is hard disk cache.
Memory Cache (LruCache ):
At the cost of valuable application memory, the memory cache provides a fast Bitmap access method. The LruCache class provided by the system is very suitable for caching Bitmap tasks. It stores recently referenced objects in a strongly referenced LinkedHashMap, in addition, when the cache size exceeds the specified size, the objects that are not frequently used recently are released.
Note: In the past, a very popular memory cache implementation was SoftReference (soft reference) or WeakReference (weak reference) Bitmap cache solution. However, it is not recommended now. Since Android2.3 (API Level 9), the garbage collector focuses more on the collection of Soft/weak references, which makes the above solution quite ineffective.
Hard disk cache (DiskLruCache ):
A memory cache is very helpful for accelerating access to the recently browsed Bitmap, but you cannot be limited to available images in the memory. Components with larger datasets such as GridView can easily consume the memory cache. Your application may be interrupted when executing other tasks (such as making a phone call), and tasks in the background may be killed or the cache may be released. Once the user resume to your application, you have to process each image again.
In this case, the hard disk cache can be used to store Bitmap and reduce the image loading time (times) after the image is released by the memory cache ). Of course, loading images from a hard disk is slower than the memory and should be performed in the background thread, because the hard disk reading time is unpredictable.
Note: If you frequently access images, ContentProvider may be more suitable for storing cached images, such as Image Gallery applications.