I am writing an Android app that helps little kids learn maths. Users can select some question options and answer the questions. If he/she answers all of them correctly, he/she can get a prize. And there is a different prize for every question option. And there are 22 different question options. I found 22 images online and put them in my drawable folder. Then I wrote a class full of maps, called QuestionOptionMaps. Here it is, hope you know what I want to do here:
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;
}
}
Most of it is just initializing the maps. I didn't initialize the optionsTimer map because that is a todo. Now I created a PrizeActivity which displays all the prizes that the user got. It basically shows all the prizes and the amount that you have. Because there are a lot of prizes, I decided to dynamically add views to a ScrollView. Here is the layout:
<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>
And here is how I add the views:
@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);
}
And when I run the app, it crashed with an OutOfMemoryError! Here is the call stack:
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.access600ドル(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)
Line 46 is this line:
image.setImageResource (imageId);
I really don't understand why this happens. Other apps have lots more images, why they don't crash? is there any way to fix this?
-
It also matters how large the images are.Karakuri– Karakuri2015年08月28日 03:39:46 +00:00Commented Aug 28, 2015 at 3:39
-
@Karakuri They are 1.9 MB in totalSweeper– Sweeper2015年08月28日 03:42:34 +00:00Commented Aug 28, 2015 at 3:42
-
once again, the size of the image files is of no importance. their dimensions only matters, as it define the size of the bitmap in memorynjzk2– njzk22015年08月28日 03:46:15 +00:00Commented Aug 28, 2015 at 3:46
-
Oh they are about 800px long and wide. I thought images with a too low resolution would look blurry on a phone's screen because the pixels are dense. @njzk2Sweeper– Sweeper2015年08月28日 03:49:31 +00:00Commented Aug 28, 2015 at 3:49
-
@Sweeper just go through this link you will get an idea to solve it.. developer.android.com/training/displaying-bitmaps/…Vishwa– Vishwa2015年08月28日 04:07:08 +00:00Commented Aug 28, 2015 at 4:07
2 Answers 2
Add android:largeHeap="true" in your application tag in manifest file.
And try again.
But this way is not recommended. Take a look at following posts also.
3 Comments
OutOfMemoryError. I think even kids can understand what this word tells. And Opp asked as is there any way to fix this?. So I provided possible solution and also I have explained why it is not recommended. Now I am wired how would you say this is certainly not a good solution.I think your drawables'resolution is higher. Either you have to reduce their resolution or you can use following alternate solution
<application
....
android:largeHeap="true">
Actually android:largeHeap is the instrument for increasing your allocated memory to app.
There is no clear definition of the need to use this flag. If you need more memory - Android provides you with a tool to increase it. But necessity of using, you define yourself.
From Android Docs:
Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results.
Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.
To query the available memory size at runtime, use the methods getMemoryClass() or getLargeMemoryClass().
DrawBack:
The use of largeHeap is not recommended in all cases, please use it very cautiously, it might slow other running application, and also impact your app's reactiveness, since the garbage collector with be solicited more often. For more information check this speech from google i/o Link
I hope it will help you.