2015-08-28 48 views
0

我正在寫一個Android應用程序,幫助小孩學數學。用戶可以選擇一些問題選項並回答問題。如果他/她正確回答所有問題,他/她可以獲得獎勵。每個問題選項都有不同的獎勵。有22個不同的問題選項。我在網上找到了22張圖片,並將它們放在我的可繪製文件夾中。然後,我寫了一個充滿地圖的課程,名爲QuestionOptionMaps。在這裏,希望你知道我想在這裏做什麼:我只是加載22個drawable,爲什麼OutOfMemoryError?

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); 

我真的不明白爲什麼會這樣。其他應用程序有更多的圖像,爲什麼他們不會崩潰?有沒有什麼辦法解決這一問題?

+0

這也很重要的圖像是多大。 – Karakuri

+0

@Karakuri他們總共有1.9 MB – Sweeper

+0

再一次,圖像*文件*的大小沒有任何重要性。它們的尺寸只是很重要,因爲它定義了內存中位圖的大小 – njzk2

回答

3

在您的清單文件中的application標記中添加android:largeHeap="true"

然後再試一次。

但是這種方式不建議。看看下面的帖子也。

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

我希望這會幫助你。

+0

是的,它的工作原理!謝謝@Gunaseelan! – Sweeper

+0

歡迎你。但不推薦。 – Gunaseelan

+2

這當然不是一個好的解決方案,因爲它沒有解決你爲什麼會遇到問題,所以即使堆中有大堆,你也可能會遇到同樣的問題。 – Karakuri

1

我認爲你的drawables的分辨率更高。要麼你不得不減少其分辨率也可以使用下面的替代解決方案

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

其實android:largeHeap是增加你的內存分配給應用程序的工具。

有沒有明確的定義需要使用此標誌。如果你需要更多的內存 - Android爲你提供了一個工具來增加它。但是使用的必要性,你定義你自己。

從Android的文檔:

無論您的應用程序的進程應以大 Dalvik的堆創建。這適用於爲 應用程序創建的所有進程。它僅適用於加載到 進程中的第一個應用程序;如果您使用的是共享的用戶ID,允許多個 應用程序使用過程中,都必須使用該選項 一致,否則會產生無法預料的結果。

大多數應用程序不應該需要此功能,而應該專注於減少其總體內存使用量以提高性能。啓用此 也不保證可用內存的固定增加,因爲某些設備的 受其總可用內存的限制。

要在運行時查詢可用內存大小,請使用方法 getMemoryClass()或getLargeMemoryClass()。

缺點:

在所有的情況下,不建議使用largeHeap,請使用它非常謹慎,這可能會降低其他正在運行的應用程序,同時還影響您的應用程序的reactiveness,因爲垃圾收集器更經常地被請求。欲瞭解更多信息,請檢查谷歌I/O的演講Link

我希望它能幫助你。

相關問題