问题描述
我有一个如此简单的活动:
public class SurvivalActivity extends Activity { private static final String KEY_LAYOUT_ID = "SurvivalLayoutId"; int mLayoutId; private static final int[] mSurvivalLayouts = { R.layout.survival1, R.layout.survival2, R.layout.survival3, }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout setContentView(mSurvivalLayouts[mLayoutId]); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putInt(KEY_LAYOUT_ID, mLayoutId); } }
两个util. * 静态方法只会更改TextView的字体.
所有3个布局相当简单,让我告诉你第一个:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView style="@style/Title" android:text="@string/surv1_title" /> <TextView style="@style/Content" android:text="@string/surv1_1" /> <ImageView style="@style/Picture" android:src="@drawable/surv_crafting" /> <TextView style="@style/PicLegend" android:src="@string/surv_crafting_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_hache" /> <TextView style="@style/PicLegend" android:src="@string/surv_hache_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_pioche" /> <TextView style="@style/PicLegend" android:src="@string/surv_pioche_leg" /> <TextView style="@style/Content" android:text="@string/surv1_2" /> <ImageView style="@style/Picture" android:src="@drawable/surv_charbon" /> <TextView style="@style/PicLegend" android:src="@string/surv_charbon_leg" /> <TextView style="@style/Content" android:text="@string/surv1_3" /> <ImageView style="@style/Picture" android:src="@drawable/surv_torch" /> <TextView style="@style/PicLegend" android:src="@string/surv_torch_leg" /> <TextView style="@style/Content" android:text="@string/surv1_4" /> <ImageView style="@style/Picture" android:src="@drawable/surv_abri" /> <TextView style="@style/PicLegend" android:src="@string/surv_abri_leg" /> <TextView style="@style/Content" android:text="@string/surv1_5" /> <Button style="@style/SurvivalBoxNext" android:text="@string/surv1_next" /> </LinearLayout> </ScrollView>
您可以看到它只是具有ImageView s的几个TextView s,以及触发下一个活动的负载的Button.
我启动应用程序,它显示第一个屏幕.我向下滚动到按钮,单击,显示第二个屏幕(使用HICCUP).我向下滚动到按钮,单击,然后悬挂.
03-02 16:36:46.488 E/AndroidRuntime(10611): java.lang.RuntimeException: Unable to start activity ComponentInfo{net.bicou.myapp/net.bicou.myapp.SurvivalActivity}: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.access$600(ActivityThread.java:122) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Handler.dispatchMessage(Handler.java:99) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Looper.loop(Looper.java:137) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.main(ActivityThread.java:4340) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invokeNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invoke(Method.java:511) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at dalvik.system.NativeStart.main(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:742) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.setContentView(Activity.java:1835) 03-02 16:36:46.488 E/AndroidRuntime(10611): at net.bicou.myapp.SurvivalActivity.onCreate(SurvivalActivity.java:34) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.performCreate(Activity.java:4465) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 11 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.reflect.InvocationTargetException 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.constructNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 25 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.OutOfMemoryError 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.nativeCreate(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.Resources.loadDrawable(Resources.java:1937) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:119) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:109) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 28 more 03-02 16:36:46.520 W/ActivityManager( 211): Force finishing activity net.bicou.myapp/.SurvivalActivity
我不明白:
- 为什么以前的活动没有释放释放一些空间
- 为什么我的应用程序占用这么多堆(Grow heap (frag case) to 60.869MB for 3600016-byte allocation)
- 如何执行连续3个活动,其中包含每个约10 TextView s和10 ImageView s
- 如果我做错了什么,因为我在代码中看不到任何错误,但显然有问题.
谢谢.
编辑:第一个活动的图片大小:
-rw-r--r-- 1 bicou staff 167197 29 fév 16:42 surv_abri.jpg 600x377 px -rw-r--r-- 1 bicou staff 24658 29 fév 16:42 surv_charbon.png 559x277 px -rw-r--r-- 1 bicou staff 5285 29 fév 16:42 surv_crafting.png 214x121 px -rw-r--r-- 1 bicou staff 4190 29 fév 16:42 surv_hache.png 214x121 px -rw-r--r-- 1 bicou staff 3809 29 fév 16:42 surv_pioche.png 214x121 px -rw-r--r-- 1 bicou staff 2252 29 fév 16:42 surv_torch.png 215x121 px
第二个活动:
-rw-r--r-- 1 bicou staff 3371 29 fév 16:42 surv_four.png 204x112 px -rw-r--r-- 1 bicou staff 5059 29 fév 16:42 surv_glass.png 215x122 px -rw-r--r-- 1 bicou staff 3176 29 fév 16:42 surv_malle.png 204x112 px -rw-r--r-- 1 bicou staff 2676 29 fév 16:42 surv_pelle.png 204x112 px -rw-r--r-- 1 bicou staff 167166 29 fév 16:42 surv_repere.png 528x331 px -rw-r--r-- 1 bicou staff 134706 29 fév 16:42 surv_sand.jpg 854x480 px
第三活动(崩溃):
-rw-r--r-- 1 bicou staff 58919 29 fév 16:42 surv_1.jpg 600x377 px -rw-r--r-- 1 bicou staff 51144 29 fév 16:42 surv_2.jpg 600x377 px -rw-r--r-- 1 bicou staff 46917 29 fév 16:42 surv_3.jpg 600x377 px -rw-r--r-- 1 bicou staff 53226 29 fév 16:42 surv_4.jpg 600x377 px -rw-r--r-- 1 bicou staff 37787 29 fév 16:42 surv_5.jpg 600x377 px -rw-r--r-- 1 bicou staff 31050 29 fév 16:42 surv_6.jpg 600x377 px -rw-r--r-- 1 bicou staff 38389 29 fév 16:42 surv_7.jpg 600x377 px -rw-r--r-- 1 bicou staff 44471 29 fév 16:42 surv_8.jpg 600x377 px
和窗口背景:
-rw-r--r-- 1 bicou staff 30857 29 fév 16:42 background_img.png
编辑:
好吧 - 我尝试直接从代码膨胀视图,以查看我是否有超过错误图像.这是我的活动代码(只有变量+ onCreate,因为其余相同)private static final int[] mTitles = { R.string.surv1_title, R.string.surv2_title, R.string.surv3_title }; private static final int[][] mTextViews = { { R.string.surv1_1, R.string.surv1_2, R.string.surv1_3, R.string.surv1_4, R.string.surv1_5 }, { R.string.surv2_1, R.string.surv2_2, R.string.surv2_3, R.string.surv2_4, R.string.surv2_5, R.string.surv2_6 }, { R.string.surv3_1, R.string.surv3_2, R.string.surv3_3 } }; private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, R.drawable.surv_8 }, }; private static final int[] mNextButtons = { R.string.surv1_next, R.string.surv2_next, R.string.surv3_next }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout //setContentView(mSurvivalLayouts[mLayoutId]); LinearLayout ll; ScrollView sv; TextView tv; ImageView iv; Button b; sv = new ScrollView(this); sv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); ll = new LinearLayout(this); ll.setOrientation(LinearLayout.VERTICAL); ll.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); tv = new TextView(this); tv.setId(R.id.title); tv.setText(getString(mTitles[mLayoutId])); ll.addView(tv); for (final int textId: mTextViews[mLayoutId]) { tv = new TextView(this); tv.setText(getString(textId)); ll.addView(tv); } for (final int imgId: mImageViews[mLayoutId]) { L.i("Creating image: "+imgId); iv = new ImageView(this); iv.setImageResource(imgId); ll.addView(iv); } b = new Button(this); b.setText(getString(mNextButtons[mLayoutId])); b.setId(R.id.survival_next); ll.addView(b); sv.addView(ll); setContentView(sv); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); }
可以看到我只是直接膨胀视图,而L.i(这是Log.i(TAG, ...)的别名)表示在任何崩溃之前膨胀的最后一个图像视图是什么.
我运行了应用程序,看到了第一个活动,然后是第二个活动,并在加载第三个时崩溃.我在logcat中看到的最后一个ID是最后一个图片R.drawable.surv_8.这是一个630x377 jpeg文件,45个kib.
我试图更改这样的视图的顺序:
private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_8, R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, }, };
所以SURV_8首先.该应用程序仍然在最后一张图片中崩溃,这是R.drawable.surv_7.所以这不是一个图像问题.
然后我从mImageViews[2]中删除了一个图片,以便我只有7张图片而不是8.该应用程序正常工作:活动负载,当我点击下一个按钮时,我会转到下一个最后我去了第一个.没有崩溃.
我可以告诉活动1和2留在记忆中.实际上,看看时间:
03-04 15:47:13.685 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +2s570ms 03-04 15:47:36.834 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +803ms 03-04 15:47:39.756 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +1s476ms 03-04 15:47:44.201 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +462ms 03-04 15:47:46.717 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms 03-04 15:47:48.599 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms
400ms而不是1.5-2.5秒加载活动.
为什么这会留在内存中?
=> **我是否需要在onDestroy()上手动释放所有图片? **
=>您如何考虑到这一点: http://androidactivity.wordpress.com/2011/09/24/sosolution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
使用此解决方案,我可以全局加载10位图,我会在加载另一个活动时释放一些?
推荐答案
我很抱歉这个问题的答案实际上并没有与我在问题中给出的元素有可能.
事实上,如这里的另一个问题所提到的:为什么我的位图令人采样200%?,我把我的照片放在drawable文件夹中,在像我这样的XHDPI屏幕上,它将导致Android upsamplumplumple两次(匹配XHDPI对MDPI),并将导致400%内存用法(宽度的两倍,高度的两倍为0个像素的4倍).还有一件事,因为我不希望用户能够回去(在startActivity()之后看到finish()),这就是我放入onPause()的内容:
private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } }
这是我在stackoverflow上到达的一些代码(例如这里:通过内存更好地添加与单个可重复使用的位图更好?).
以下是我关于此代码的备注,为什么每行都很重要:
- setCallback(null)删除对活动的引用,以便收集垃圾.如果您不这样做,所有视图都将保持内存.
- 您还需要从onPause调用它,因为onDestroy不保证调用.
- 您可能需要在此之后导致垃圾收集,以帮助GC了解对已制作孤儿并且可以是GC'D的对象的一些引用.只需调用System.gc().
这是由此产生的onPause代码,具有id top_layout的活动的主要布局:
@Override protected void onPause() { super.onPause(); unbindDrawables(findViewById(R.id.top_layout)); System.gc(); }
其他推荐答案
它可能只是您放入imageview的位图非常大.这是您可以尝试的一些事情:
泄漏XML布局,只是膨胀并显示有问题的图像视图或ImageViews,并查看此方法您是否收到错误.
还检查每个图像以查看其实际尺寸.他们将采用内存所需的大小为4 *宽度*高度(以宽度和高度为图像尺寸,其中宽度和高度为像素的大小).
由于您的位图实例正在堆积在堆上,我想象您正在使用Android 3或更高版本.在这些版本中,位于位图实例的位置(就像任何其他Java实例一样).在Android 2.x及以下,位图实例在一些脱机内存中分配.知道这一点,确保您获得的高堆用实际上来自图像,而不是来自创建的其他某些实例.尝试调试构造函数,并查看App启动/运行时呼叫谁.
根据BICOU的观察,您可能希望通过BitMapFactory手动加载图像,并且在这样做时可能会缩小它们(通过选项对象).此外,尝试通过递增4 *宽*递增,以便将计数器保留多少图像字节通过递增4 *宽*您实际加载到位图对象中.
其他推荐答案
优化内存用法的最佳方法,以便在您的情况下使用位图来保存您的ImageViews的列表,使其在呼叫FinishView()之前使其进行调用:
((BitmapDrawable) myImageView.getDrawable()).getBitmap().recycle(); myImageView = null;
问题描述
I have a quite simple activity like this :
public class SurvivalActivity extends Activity { private static final String KEY_LAYOUT_ID = "SurvivalLayoutId"; int mLayoutId; private static final int[] mSurvivalLayouts = { R.layout.survival1, R.layout.survival2, R.layout.survival3, }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout setContentView(mSurvivalLayouts[mLayoutId]); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putInt(KEY_LAYOUT_ID, mLayoutId); } }
The two Util.* static methods just change the font of the TextView.
All 3 layouts are rather simple, let me show you the first :
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView style="@style/Title" android:text="@string/surv1_title" /> <TextView style="@style/Content" android:text="@string/surv1_1" /> <ImageView style="@style/Picture" android:src="@drawable/surv_crafting" /> <TextView style="@style/PicLegend" android:src="@string/surv_crafting_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_hache" /> <TextView style="@style/PicLegend" android:src="@string/surv_hache_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_pioche" /> <TextView style="@style/PicLegend" android:src="@string/surv_pioche_leg" /> <TextView style="@style/Content" android:text="@string/surv1_2" /> <ImageView style="@style/Picture" android:src="@drawable/surv_charbon" /> <TextView style="@style/PicLegend" android:src="@string/surv_charbon_leg" /> <TextView style="@style/Content" android:text="@string/surv1_3" /> <ImageView style="@style/Picture" android:src="@drawable/surv_torch" /> <TextView style="@style/PicLegend" android:src="@string/surv_torch_leg" /> <TextView style="@style/Content" android:text="@string/surv1_4" /> <ImageView style="@style/Picture" android:src="@drawable/surv_abri" /> <TextView style="@style/PicLegend" android:src="@string/surv_abri_leg" /> <TextView style="@style/Content" android:text="@string/surv1_5" /> <Button style="@style/SurvivalBoxNext" android:text="@string/surv1_next" /> </LinearLayout> </ScrollView>
As you can see it's just several TextViews with ImageViews, and a Button that triggers the load of the next activity.
I launch the app, it displays the first screen. I scroll down to the button, click, the second screen is displayed (with a hiccup). I scroll down to the button, click, and boom.
03-02 16:36:46.488 E/AndroidRuntime(10611): java.lang.RuntimeException: Unable to start activity ComponentInfo{net.bicou.myapp/net.bicou.myapp.SurvivalActivity}: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.access$600(ActivityThread.java:122) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Handler.dispatchMessage(Handler.java:99) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Looper.loop(Looper.java:137) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.main(ActivityThread.java:4340) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invokeNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invoke(Method.java:511) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at dalvik.system.NativeStart.main(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:742) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.setContentView(Activity.java:1835) 03-02 16:36:46.488 E/AndroidRuntime(10611): at net.bicou.myapp.SurvivalActivity.onCreate(SurvivalActivity.java:34) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.performCreate(Activity.java:4465) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 11 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.reflect.InvocationTargetException 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.constructNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 25 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.OutOfMemoryError 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.nativeCreate(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.Resources.loadDrawable(Resources.java:1937) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:119) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:109) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 28 more 03-02 16:36:46.520 W/ActivityManager( 211): Force finishing activity net.bicou.myapp/.SurvivalActivity
I don't understand :
- Why the previous activities are not freed to free up some space
- Why is my app taking up so much heap (Grow heap (frag case) to 60.869MB for 3600016-byte allocation)
- How can I do to display successively 3 activities containing each about 10 TextViews and 10 ImageViews
- If I did something wrong, because I can't see anything wrong in my code, but there is obviously something wrong.
Thank you.
Edit: picture sizes of the first activity:
-rw-r--r-- 1 bicou staff 167197 29 fév 16:42 surv_abri.jpg 600x377 px -rw-r--r-- 1 bicou staff 24658 29 fév 16:42 surv_charbon.png 559x277 px -rw-r--r-- 1 bicou staff 5285 29 fév 16:42 surv_crafting.png 214x121 px -rw-r--r-- 1 bicou staff 4190 29 fév 16:42 surv_hache.png 214x121 px -rw-r--r-- 1 bicou staff 3809 29 fév 16:42 surv_pioche.png 214x121 px -rw-r--r-- 1 bicou staff 2252 29 fév 16:42 surv_torch.png 215x121 px
Second activity:
-rw-r--r-- 1 bicou staff 3371 29 fév 16:42 surv_four.png 204x112 px -rw-r--r-- 1 bicou staff 5059 29 fév 16:42 surv_glass.png 215x122 px -rw-r--r-- 1 bicou staff 3176 29 fév 16:42 surv_malle.png 204x112 px -rw-r--r-- 1 bicou staff 2676 29 fév 16:42 surv_pelle.png 204x112 px -rw-r--r-- 1 bicou staff 167166 29 fév 16:42 surv_repere.png 528x331 px -rw-r--r-- 1 bicou staff 134706 29 fév 16:42 surv_sand.jpg 854x480 px
Third activity (crashing):
-rw-r--r-- 1 bicou staff 58919 29 fév 16:42 surv_1.jpg 600x377 px -rw-r--r-- 1 bicou staff 51144 29 fév 16:42 surv_2.jpg 600x377 px -rw-r--r-- 1 bicou staff 46917 29 fév 16:42 surv_3.jpg 600x377 px -rw-r--r-- 1 bicou staff 53226 29 fév 16:42 surv_4.jpg 600x377 px -rw-r--r-- 1 bicou staff 37787 29 fév 16:42 surv_5.jpg 600x377 px -rw-r--r-- 1 bicou staff 31050 29 fév 16:42 surv_6.jpg 600x377 px -rw-r--r-- 1 bicou staff 38389 29 fév 16:42 surv_7.jpg 600x377 px -rw-r--r-- 1 bicou staff 44471 29 fév 16:42 surv_8.jpg 600x377 px
And the Window background:
-rw-r--r-- 1 bicou staff 30857 29 fév 16:42 background_img.png
EDIT:
OK — I tried inflating the views directly from code in order to see if I had a buggy image. Here's my activity code (only variables + onCreate as the rest is identical)
private static final int[] mTitles = { R.string.surv1_title, R.string.surv2_title, R.string.surv3_title }; private static final int[][] mTextViews = { { R.string.surv1_1, R.string.surv1_2, R.string.surv1_3, R.string.surv1_4, R.string.surv1_5 }, { R.string.surv2_1, R.string.surv2_2, R.string.surv2_3, R.string.surv2_4, R.string.surv2_5, R.string.surv2_6 }, { R.string.surv3_1, R.string.surv3_2, R.string.surv3_3 } }; private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, R.drawable.surv_8 }, }; private static final int[] mNextButtons = { R.string.surv1_next, R.string.surv2_next, R.string.surv3_next }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout //setContentView(mSurvivalLayouts[mLayoutId]); LinearLayout ll; ScrollView sv; TextView tv; ImageView iv; Button b; sv = new ScrollView(this); sv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); ll = new LinearLayout(this); ll.setOrientation(LinearLayout.VERTICAL); ll.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); tv = new TextView(this); tv.setId(R.id.title); tv.setText(getString(mTitles[mLayoutId])); ll.addView(tv); for (final int textId: mTextViews[mLayoutId]) { tv = new TextView(this); tv.setText(getString(textId)); ll.addView(tv); } for (final int imgId: mImageViews[mLayoutId]) { L.i("Creating image: "+imgId); iv = new ImageView(this); iv.setImageResource(imgId); ll.addView(iv); } b = new Button(this); b.setText(getString(mNextButtons[mLayoutId])); b.setId(R.id.survival_next); ll.addView(b); sv.addView(ll); setContentView(sv); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); }
As you can see I am just inflating the views directly, and L.i (which is an alias to Log.i(TAG, ...)) indicates what was the last ImageView inflated before any crash.
I ran the app, saw the 1st activity, then the second, and as previously it crashes while loading the third one. The last ID I saw in the logcat was the last picture R.drawable.surv_8. This is a 630x377 jpeg file, 45 kiB.
I tried to change the order of the views like this:
private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_8, R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, }, };
So that surv_8 is first. The app is still crashing at the last picture, which is R.drawable.surv_7. So that is not an image issue.
Then I removed one of the pictures out of mImageViews[2] so that I have only 7 pictures instead of 8. The app is working as expected: the activity loads, when I click the button I go to the next one, and on the last I go to the first. No crash.
I can however tell that the activity 1 and 2 stay into memory. Indeed, have a look at the timings:
03-04 15:47:13.685 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +2s570ms 03-04 15:47:36.834 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +803ms 03-04 15:47:39.756 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +1s476ms 03-04 15:47:44.201 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +462ms 03-04 15:47:46.717 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms 03-04 15:47:48.599 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms
400ms instead of 1.5-2.5 sec to load the activity.
Why is this staying into memory?
=> ** Do I need to manually free all pictures upon onDestroy() ? **
=> What do you think about this: http://androidactivity.wordpress.com/2011/09/24/solution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
With this solution I could load like 10 bitmaps globally, and I would free some while loading another activity?
推荐答案
I'm sorry that the answer of this question wasn't actually possible to have with the elements I gave in my question.
Indeed, as mentioned in another question here: Why are all my bitmaps upsampled 200%?, I put my pictures in the drawable folder, which, on a xhdpi screen like mine, will cause Android to upsample everything twice (to match xhdpi against mdpi), and will result in a 400% memory usage (twice the width and twice the height so 4 times as many pixels).
One more thing, since I don't want the user to be able to go back (see the finish() right after the startActivity()), here's what I put in the onPause():
private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } }
This is some code I got here on StackOverflow (for example here: Drawable vs Single reusable Bitmap better with memory?).
Here are my remarks about this code and why every line is important:
- The setCallback(null) removes the reference to the activity, so that it can be garbage collected. If you don't do this, all the views will stay in memory.
- You also need to call it from onPause, because onDestroy is not guaranteed to be called.
- You may need to cause a garbage collection after this, in order to help the GC understand that some references to objects that were made orphans and that can be GC'd. Just call System.gc().
This is the resulting onPause code, with the main layout of your activity having the id top_layout:
@Override protected void onPause() { super.onPause(); unbindDrawables(findViewById(R.id.top_layout)); System.gc(); }
其他推荐答案
It might just be that the bitmap you're putting in the ImageView is very large. Here's some things you can try:
Ditch the xml layout and just inflate and display the problematic ImageView or ImageViews and see if this way you're getting the error.
Also check each of your images to see what size they actually have. The size they'll take in memory is roughly 4*width*height (in bytes, where width and height are the image's size in pixels).
Since your Bitmap instances are taking up memory on the heap, I'm imagining you're using Android 3 or above. In these versions that's where Bitmap instances go (just like any other Java instance). In Android 2.x and below, Bitmap instances were allocated in some offheap memory. Knowing this, make sure that that high heap usage you're getting is indeed from the images, and not from some other instances that are created a lot. Try debugging your constructors and see who calls what when the app starts/runs.
As per Bicou's observations, you might want to manually load your images via BitmapFactory, and in doing so maybe downsample them (via the Options object). Also, try keeping a counter of how much image bytes in memory you total by incrementing with 4*width*height of each image you're actually loading into a Bitmap Object.
其他推荐答案
The best way to optimize memory usage with bitmaps in your case is to save a list of your ImageViews make this before call finish() for every ImageView:
((BitmapDrawable) myImageView.getDrawable()).getBitmap().recycle(); myImageView = null;