我只是加载22个drawables,为什么OutOfMemoryError?

时间:2015-08-28 03:36:06

标签: java android image out-of-memory

我正在编写一款可帮助小孩学习数学的Android应用。用户可以选择一些问题选项并回答问题。如果他/她正确回答所有这些,他/她可以获得奖品。每个问题选项都有不同的奖品。有22种不同的问题选择。我在网上发现了22张图片并将它们放入我的drawable文件夹中。然后我写了一个充满地图的类,叫做QuestionOptionMaps。在这里,希望你知道我想在这里做什么:

package com.smartkidslovemaths.util;

import com.smartkidslovemaths.QuestionOptions;
import com.smartkidslovemaths.R;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

public class QuestionOptionMaps {

    private QuestionOptionMaps () {}

    public static ArrayList<QuestionOptions> getOptionsList() {
        return optionsList;
    }

    public static HashMap<QuestionOptions, Integer> getOptionsDrawableMap() {
        return optionsDrawableMap;
    }

    public static HashMap<QuestionOptions, String> getOptionsKeysMap() {
        return optionsKeysMap;
    }

    public static HashMap<QuestionOptions, Integer> getOptionsTimerMap() {
        return optionsTimerMap;
    }

    private static ArrayList<QuestionOptions> optionsList;
    private static HashMap<QuestionOptions, Integer> optionsDrawableMap;
    private static HashMap<QuestionOptions, String> optionsKeysMap;
    private static HashMap<QuestionOptions, Integer> optionsTimerMap;

    static {
        optionsList = new ArrayList<> ();
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 1, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 2, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 3, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 1, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 2, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADDITION, 3, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 1, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 2, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 3, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 1, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 2, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.SUBTRACTION, 3, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 1, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 2, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 3, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 1, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 2, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.ADD_AND_SUB, 3, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 1, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 2, false));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 1, true));
        optionsList.add (new QuestionOptions (QuestionOptions.OperationType.MULTIPLICATION, 2, true));

        ArrayList<String> prefKeyArray = new ArrayList<> ();
        prefKeyArray.add ("p110");
        prefKeyArray.add ("p120");
        prefKeyArray.add ("p130");
        prefKeyArray.add ("p111");
        prefKeyArray.add ("p121");
        prefKeyArray.add ("p131");
        prefKeyArray.add ("p210");
        prefKeyArray.add ("p220");
        prefKeyArray.add ("p230");
        prefKeyArray.add ("p211");
        prefKeyArray.add ("p221");
        prefKeyArray.add ("p231");
        prefKeyArray.add ("p310");
        prefKeyArray.add ("p320");
        prefKeyArray.add ("p330");
        prefKeyArray.add ("p311");
        prefKeyArray.add ("p321");
        prefKeyArray.add ("p331");
        prefKeyArray.add ("p410");
        prefKeyArray.add ("p420");
        prefKeyArray.add ("p411");
        prefKeyArray.add ("p421");
        optionsKeysMap = getHashMapFromCollections (optionsList, prefKeyArray);

        ArrayList<Integer> idArray = new ArrayList<> ();
        idArray.add (R.drawable.p110);
        idArray.add (R.drawable.p120);
        idArray.add (R.drawable.p130);
        idArray.add (R.drawable.p111);
        idArray.add (R.drawable.p121);
        idArray.add (R.drawable.p131);
        idArray.add (R.drawable.p210);
        idArray.add (R.drawable.p220);
        idArray.add (R.drawable.p230);
        idArray.add (R.drawable.p211);
        idArray.add (R.drawable.p221);
        idArray.add (R.drawable.p231);
        idArray.add (R.drawable.p310);
        idArray.add (R.drawable.p320);
        idArray.add (R.drawable.p330);
        idArray.add (R.drawable.p311);
        idArray.add (R.drawable.p321);
        idArray.add (R.drawable.p331);
        idArray.add (R.drawable.p410);
        idArray.add (R.drawable.p420);
        idArray.add (R.drawable.p411);
        idArray.add (R.drawable.p421);
        optionsDrawableMap = getHashMapFromCollections (optionsList, idArray);

        //TODO initialize the collections
    }

    private static <K, V> HashMap<K, V> getHashMapFromCollections (Collection<K> keys, Collection<V> values) {
        if (keys.size () != values.size ())
            throw new AssertionError ();
        HashMap<K, V> map = new HashMap<> ();
        K[] keyArray = (K[])keys.toArray ();
        V[] valueArray = (V[])values.toArray ();
        for (int i = 0 ; i < keys.size () ; i++) {
            map.put (keyArray[i], valueArray[i]);
        }
        return map;
    }
}

大多数只是初始化地图。我没有初始化optionsTimer地图,因为这是一个待办事项。现在我创建了一个PrizeActivity,显示了用户获得的所有奖品。它基本上显示了所有的奖品和金额。因为有一个很多的奖品,所以我决定动态地将视图添加到ScrollView。这是布局:

<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:paddingBottom="@dimen/activity_vertical_margin"
                tools:context="com.smartkidslovemaths.PrizeActivity">
    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/trophy_content"
            android:orientation="horizontal">

        </LinearLayout>
    </HorizontalScrollView>

</RelativeLayout>

以下是我添加观点的方式:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate (savedInstanceState);
    setContentView (R.layout.activity_prize);
    for (QuestionOptions option : QuestionOptionMaps.getOptionsList ()) {
        displayAPrize (option);
    }
}

private void displayAPrize (QuestionOptions options) {
    Resources res = getResources ();
    int parentMargin = (int)res.getDimension (R.dimen.prize_display_margin);
    LinearLayout.LayoutParams parentParams =
            new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
    parentParams.setMargins (parentMargin, parentMargin, parentMargin, parentMargin);

    LinearLayout.LayoutParams imageParams =
            new LinearLayout.LayoutParams (
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
            );

    LinearLayout parent = new LinearLayout (this);
    parent.setLayoutParams (parentParams);
    parent.setOrientation (LinearLayout.VERTICAL);


    ImageView image = new ImageView (this);
    image.setLayoutParams (imageParams);
    int imageId = QuestionOptionMaps.getOptionsDrawableMap ().get (options);
    image.setImageResource (imageId);
    parent.addView (image);

    TextView text = new TextView (this);
    text.setLayoutParams (imageParams);
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences (this);
    String key = QuestionOptionMaps.getOptionsKeysMap ().get (options);
    int prizeCount = prefs.getInt (key, 0);
    text.setText ("x" + prizeCount);
    parent.addView (text);

    ((LinearLayout)findViewById (R.id.trophy_content)).addView (parent);
}

当我运行应用程序时,它崩溃了OutOfMemoryError!这是调用堆栈:

java.lang.OutOfMemoryError
        at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:503)
        at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:356)
        at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:816)
        at android.content.res.Resources.loadDrawable(Resources.java:2117)
        at android.content.res.Resources.getDrawable(Resources.java:702)
        at android.widget.ImageView.resolveUri(ImageView.java:636)
        at android.widget.ImageView.setImageResource(ImageView.java:365)
        at com.smartkidslovemaths.PrizeActivity.displayAPrize(PrizeActivity.java:46)
        at com.smartkidslovemaths.PrizeActivity.onCreate(PrizeActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:5133)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2230)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2316)
        at android.app.ActivityThread.access$600(ActivityThread.java:150)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1298)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:213)
        at android.app.ActivityThread.main(ActivityThread.java:5225)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:741)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
        at dalvik.system.NativeStart.main(Native Method)

第46行是这一行:

image.setImageResource (imageId);

我真的不明白为什么会这样。其他应用程序有更多的图像,为什么他们不崩溃?有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:3)

在清单文件的android:largeHeap="true"标记中添加application

再试一次。

但不推荐这种方式。看看以下帖子。

  1. Displaying Bitmaps Efficiently
  2. Strange out of memory issue while loading an image to a Bitmap object

答案 1 :(得分:1)

我认为你的drawables分辨率更高。您必须降低其分辨率,或者您可以使用以下备用解决方案

<application
    ....
       android:largeHeap="true"> 

实际上android:largeHeap是增加分配内存到app的工具。

没有明确定义使用此标志的必要性。如果您需要更多内存 - Android为您提供了增加内存的工具。但是使用的必要性,你定义自己。

来自Android文档:

  

是否应该使用大型创建应用程序的进程   达尔维克堆。这适用于为此创建的所有进程   应用。它只适用于第一个加载到的应用程序   处理;如果您使用共享用户ID来允许多个   要使用进程的应用程序,它们都必须使用此选项   一直或他们将有不可预测的结果。

     

大多数应用程序不应该需要这个,而应该专注于减少   它们的整体内存使用率以提高性能。启用此功能   也不保证可用内存的固定增加,因为   某些设备受其总可用内存的限制。

     

要在运行时查询可用内存大小,请使用这些方法   getMemoryClass()或getLargeMemoryClass()。

<强>缺点:

在所有情况下都不建议使用largeHeap,请非常小心地使用它,它可能会减慢其他正在运行的应用程序,并且还会影响应用程序的反应性,因为垃圾收集器会被更频繁地请求。有关详细信息,请查看Google i / o Link

中的此演讲

我希望它会对你有所帮助。