Easy Tutorial
❮ Android Tutorial Webview Download File Mapreduce Coding ❯

8.2.2 Bitmap-Induced OOM Issues

Category Android Basic Tutorial

Introduction:

>

In the previous section, we learned the basic usage of Bitmap, and in this section, we will explore the OOM issues related to Bitmap. You may have encountered, or not encountered, OOM issues due to Bitmap in actual development. In this section, we will focus on this topic to learn what OOM is, why it occurs, and how to improve OOM issues caused by Bitmap.


1. What is OOM? Why does it occur?

Answer: Out Of Memory (OOM), as we know, Android systems allocate a separate workspace or a separate Dalvik virtual machine for each app, so each app can run independently without affecting each other! Android has a maximum memory limit for each Dalvik virtual machine. If the current memory usage plus the memory resources we apply for exceeds this limit, the system will throw an OOM error! Also, don't confuse this with RAM. Even if there is more than 1GB of free memory in the current RAM, OOM can still occur! Don't mix up RAM (physical memory) with OOM! If RAM is insufficient, it's about killing applications, not just OOM! The maximum memory standard in Dalvik varies by model and can be obtained by calling:

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e("HEHE","Maximum Memory: " + activityManager.getMemoryClass());

Or directly enter in the command line:

adb shell getprop | grep dalvik.vm.heapgrowthlimit

You can also open the system source code /system/build.prop file and look at the information in this part of the file:

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

We focus on three aspects: heapstartsize, the initial size of the heap memory; heapgrowthlimit, the maximum heap memory size for standard applications; and heapsize, which sets the maximum heap memory size for applications using android:largeHeap.

I tested the normal maximum memory allocation standard on a few models I have:

You can also try the models you have.

Okay, enough about the generation of OOM issues, let's move on to some tips for avoiding OOM caused by Bitmap!


2. Summary of Techniques to Avoid OOM Caused by Bitmap


1) Use low memory-consuming encoding methods

In the previous section, we mentioned BitmapFactory.Options. We can set the inPreferredConfig property, which defaults to Bitmap.Config.ARGB_8888**, and we can change it to **Bitmap.Config.ARGB_4444.


2) Image compression

Again, using BitmapFactory.Options, we can set the inSampleSize to adjust the scaling factor. For example, setting it to 2 reduces the width and height to 1/2 of the original, making the image 1/4 of the original size. If no scaling is needed, set it to 1. However, don't compress blindly, as a too small value can result in blurry images and distortion. Therefore, we need to dynamically calculate an appropriate value for inSampleSize in the program. The Options class also has a method: inJustDecodeBounds. Setting this parameter to true allows us to calculate the original image's width and height without allocating memory space. We can then use options.outWidth and options.outHeight to get the image's width and height and calculate an appropriate inSampleSize. Thanks to Jie Shen for providing the code—extracted from Hongyang's blog!

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
        int widthRadio = Math.round(width * 1.0f / reqWidth);
        int heightRadio = Math.round(height * 1.0f / reqHeight);
        inSampleSize = Math.max(widthRadio, heightRadio);
    }
    return inSampleSize;
}

Then use the above method:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // Remember to set this property to false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
options.inSampleSize = computeSampleSize(options,128,128);
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
/* The following two fields need to be used together */  
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
    bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
        Log.e(TAG, "OutOfMemoryError");
}

3. Timely Recycling of Images

If you reference a large number of Bitmap objects and the application does not need to display all images simultaneously, you can recycle Bitmap objects that are temporarily not in use. For some scenarios where the usage of images is clear, you can actively recycle them, such as guide page images, which can be recycled after use, or frame animations, where one is loaded, drawn, and released at a time! Load when needed, and set to null or recycle when not displayed. For example: imageView.setImageResource(0); However, in some cases, specific images may be repeatedly loaded, released, and reloaded, which is inefficient.


4. Other Methods

The following methods have not been used by me, and you can refer to relevant materials:

1. Simple management of image resources through SoftReference

Create a hashmap of SoftReference. When using an image, first query whether the hashmap has a softreference, and whether the image in the softreference is empty. If it is empty, load the image into the softreference and add it to the hashmap. There is no need to handle the recycling and release of images explicitly in the code, as the gc will automatically release resources. This method is simple and practical, and can一定程度上 avoid前一种方法反复加载释放的低效率。但还不够优化。

Example code:

private Map&lt;String, SoftReference<Bitmap>> imageMap 
                                           = new HashMap&lt;String, SoftReference<Bitmap>>();

public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
    SoftReference<Bitmap> reference = imageMap.get(imageUrl);
    if(reference != null) {
        if(reference.get() != null) {
            return reference.get();
        }
    }
    final Handler handler = new Handler() {
        public void handleMessage(final android.os.Message msg) {
            //Add to cache
            Bitmap bitmap = (Bitmap)msg.obj;
            imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
            if(imageCallBack != null) {
                imageCallBack.getBitmap(bitmap);
            }
        }
    };
    new Thread(){
        public void run() {
            Message message = handler.obtainMessage();
            message.obj = downloadBitmap(imageUrl);
            handler.sendMessage(message);
        }
    }.start();
    return null ;
}

//Download image from the internet
private Bitmap downloadBitmap (String imageUrl) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
        return bitmap ;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } 
}
public interface ImageCallBack{
    void getBitmap(Bitmap bitmap);
}

2. LruCache + sd cache method

>

Starting from Android 3.1, the official also provides LruCache for cache processing. When the size of stored images exceeds the value set by LruCache, the least recently used images will be recycled, and the system will automatically release memory!

Usage example:

Steps:

1) First, set the memory size for caching images. Here, I set it to 1/8 of the phone's memory. The method to get the phone's memory: int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);

2) The key-value pairs in LruCache are URL and corresponding image.

3) Override the sizeOf method to return the number of images.

private LruCache&lt;String, Bitmap> mMemoryCache;
private LruCacheUtils() {
    if (mMemoryCache == null)
        mMemoryCache = new LruCache&lt;String, Bitmap>(
                MAXMEMONRY / 8) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // Override this method to measure the size of each image, default returns the number of images.
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }

            @Override
            protected void entryRemoved(boolean evicted, String key,
                    Bitmap oldValue, Bitmap newValue) {
                Log.v("tag", "hard cache is full, push to soft cache");

            }
        };
}

4) The following methods are for clearing the cache, adding images to the cache, retrieving images from the cache, and removing images from the cache.

Removing and clearing the cache are essential tasks because improper handling of image caching can lead to out-of-memory errors, so it must be taken seriously.

public void clearCache() {
    if (mMemoryCache != null) {
        if (mMemoryCache.size() > 0) {
            Log.d("CacheUtils",
                    "mMemoryCache.size() " + mMemoryCache.size());
            mMemoryCache.evictAll();
            Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
        }
        mMemoryCache = null;
    }
}

public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (mMemoryCache.get(key) == null) {
        if (key != null && bitmap != null)
            mMemoryCache.put(key, bitmap);
    } else
        Log.w(TAG, "the res is already exists");
}

public synchronized Bitmap getBitmapFromMemCache(String key) {
    Bitmap bm = mMemoryCache.get(key);
    if (key != null) {
        return bm;
    }
    return null;
}

/**
 * Remove cache
 * 
 * @param key
 */
public synchronized void removeImageCache(String key) {
    if (key != null) {
        if (mMemoryCache != null) {
            Bitmap bm = mMemoryCache.remove(key);
            if (bm != null)
                bm.recycle();
        }
    }
}

The above content is excerpted from——Image Caching Techniques: LruCache, Soft References


Summary:

>

This section explains the causes of OOM issues and summarizes some solutions provided online to avoid OOM caused by Bitmap. Since the company's APP is mostly map-based and rarely involves images, the author has not encountered OOM issues and is not very familiar with them. We will delve into the OOM issue in the advanced course on memory management. That's all for this section, thank you.


References: Analysis and Solutions for OOM Issues in Android Applications

-1.0 Android Basic Introduction Tutorial

-1.0.1 2015 Latest Android Basic Introduction Tutorial Table of Contents

-1.1 Background and System Architecture Analysis

-1.2 Development Environment Setup

-1.2.1 Developing Android APP with Eclipse + ADT + SDK

-1.2.2 Developing Android APP with Android Studio

-1.3 Solving SDK Update Issues

-1.4 Genymotion Emulator Installation

-1.5.1 Git Tutorial for Basic Local Repository Operations

-1.5.2 Git: Setting Up a Remote Repository on GitHub

-1.6 How to Use the 9-Patch Image

-1.7 Interface Prototype Design

-1.8 Project Source Analysis (Various Files, Resource Access)

-1.9 Android Application Signing and Packaging

-1.11 Decompiling APK to Obtain Code & Resources

-2.1 Concepts of View and ViewGroup

-2.2.1 LinearLayout (Linear Layout)

-2.2.2 RelativeLayout (Relative Layout)

-2.2.3 TableLayout (Table Layout)

-2.2.4 FrameLayout (Frame Layout)

-2.2.5 GridLayout (Grid Layout)

-2.2.6 AbsoluteLayout (Absolute Layout)

-2.3.1 Detailed Explanation of TextView (Text Box)

-2.3.2 Detailed Explanation of EditText (Input Box)

-2.3.3 Button and ImageButton (Image Button)

-2.3.4 ImageView (Image View)

-2.3.5.RadioButton (Radio Button) & Checkbox (Checkbox)

-2.3.6 ToggleButton and Switch (Switch Button)

-2.3.7 ProgressBar (Progress Bar)

-2.3.8 SeekBar (Drag Bar)

-2.3.9 RatingBar (Star Rating Bar)

-2.4.1 ScrollView (Scroll Bar)

-2.4.2 Date & Time Components (Part 1)

-2.4.3 Date & Time Components (Part 2)

-2.4.4 Adapter Basics

-2.4.5 Simple Usage of ListView

-2.4.6 Optimization of BaseAdapter

-2.4.7 Focus Issues with ListView

-2.4.8 Solving Checkbox Misalignment in ListView

-2.4.9 Data Update Issues in ListView

-2.5.0 Building a Reusable Custom BaseAdapter

-2.5.1 Implementing Multi-Layout ListView Items

-2.5.2 Basic Usage of GridView (Grid View)

-2.5.3 Basic Usage of Spinner (List Option Box)

-2.5.4 Basic Usage of AutoCompleteTextView (Auto-Complete Text Box)

-2.5.5 Basic Usage of ExpandableListView (Collapsible List) ```

WeChat Subscription

❮ Android Tutorial Webview Download File Mapreduce Coding ❯