android中,由于图像处理不当而引起的OOM问题及其解决方案(一)

在android中,经常会出现OOM(OutOfMemoryError), 原因有很多,其中,也可能由于图像处理不当而引起的的,本篇将从这个方面介绍几种常见的场景。


重要:而通常情景是,这个OOM不是必现的,有时运行过程中会发生,有时,同样的代码,有的手机上不出现;有的手机上会偶现。

这就加大了解决问题的难度。不过,只要遵循一定的良好的编程方式,还是有办法避免的。


1. 图片太大。
android中,系统给每个应用分配的内存是有限的,如果一个资源图片太大,加载到内存中后,占用的内存空间也会很大,(尤其是这种图片比样多的话),这样,随着程序的运行,内存不堪重负,就有可能引起OOM。
即使将background改为src,也无济于事。
例如:

xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/color_white"
    android:id="@+id/main_activity_layout">
            android:id="@+id/image_app_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/imageview_main"
        android:scaleType="fitXY"/>

以上,是最常用的方法,但是,也有可能出现OOM的问题。

OOM错误:
java.lang.OutOfMemoryError: Failed to allocate a 9437196 byte allocation with 6950442 free bytes and 6MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:741)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:562)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1014)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:3706)
at android.content.res.Resources.loadDrawable(Resources.java:3579)
at android.content.res.Resources.getDrawable(Resources.java:1852)
at android.content.res.Resources.getDrawable(Resources.java:1818)


分析:在以上xml中,直接通过android:background引用了资源文件,如果这个文件比较大,就很容易引起OOM。
我的图片大于1M,在一些低端机上很容易出现OOM,如果调成200K左右,也会偶发。最后调成30K以下,问题基本解决。
但是,仍然有一款Samsang的机子上,会偶发OOM。(解决方法:采用ImageLoader,下一篇章会介绍)。




2. 多张图片加载问题:
有时候,我们会在view中显示动态加载多张图片,可能会引起OOM问题。奇怪的是,如果加载少量图片,就不会出现类似问题。看一段伪代码(真实的有错误的代码不在这儿贴了):

for (int i = 0; i< 100; i++) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            //执行图片请求,回调,然后,在回调函数中,发msg到handler。

        }
    }).start();


}

Hander的处理:
private Handler mNetworkHandler = new Handler() {
    public void handleMessage(Message msg)
    {
        switch (msg.what)
        {
            case Request_Success:
                setMyImage(msg.what);
                break;
            case Request_Fail:
                setMyImage(R.mipmap.default_image);
                break;
            default:
                break;
        }
    }
};


分析:这种情况,也会发生OOM。因为创建了多个线程,而没有对线程进行管理,在请求数据完成后,在setImage的时候(注意,是多个image同时进行设置),这样,必然会消耗大量内存,最终导致OOM的出现。也有可能线程开启太多了,就会发生各种意想不到的错误,例如,阻塞UI线程,出现ANR也是有可能的。
初步解决办法:
不要在for循环内进行大量线程的创建,改用线程池,或者现有的成熟的图片加载框架。

总结:上面的场景,都是对原始图进行加载,没有进行压缩处理,而且,也没有用第三方的加载框架。很容易发生OOM。下一篇介绍采用ImageLoader可以避免这种情况发生。


你可能感兴趣的:(Android开发技术与实践)