今天的异常很有意思,叫做android.view.InflateException: Binary XML file line #95: Error inflating class(out of memory) 。
其实是因为out of memory,导致 xml是不可能被充气成功,因此activity的onCreate方法中,
setContentView(R.layout.***)也就不可能成功调用.
他出现在我有多个教学动画,并且播放的动画,是基于imageView,imageView的背景是我们项目的大型图片。
出错情境为:打开一个activity,这个activity只用来播放一个动画。然后手动back,关掉activity(finish)。开第二的activity,第二个activity绑定的layout和第一个不一样,播放另一个动画。手动back,关掉activity。
以此类推,多次运行后会出现做android.view.InflateException: Binary XML file line #95: Error inflating class.
caused by out of memory.
一开始我怎么也没有想明白,每次我的activity都finish了,怎么可能会内存不足。
于是上网找答案,在亲爱的stackoverflow上发现了大神的评论,(大神请移步:http://stackoverflow.com/questions/7536988/android-app-out-of-memory-issues-tried-everything-and-still-at-a-loss/7576275),原来提出问题的哥也试了everything,哈哈。
其实是因为我们并没有手工回收资源,换句话说,java的垃圾回收机制并没有那么的聪明,我们finish掉了,但里面相关的资源他未必回收。有可能他自以为很聪明的留下来等着我们下次使用。所以我们需要在onStop的方法中手动释放imageView这样的大型资源。
大神的写法如下:
1
2
3
4
|
Drawable d = imageView.getDrawable();
if
(d !=
null
) d.setCallback(
null
);
imageView.setImageDrawable(
null
);
imageView.setBackgroundDrawable(
null
);
|
我之后在我们项目中的五个播放动画的教学activity中同样配置了如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Override
protected
void
onStop() {
releaseImageViews();
super
.onStop();
}
private
void
releaseImageViews() {
releaseImageView(mTerminal);
releaseImageView(mFingerPressInnerRing);
releaseImageView(mFingerPressMiddleRing);
releaseImageView(mFingerPressOuterRing);
releaseImageView(mInnerRing);
releaseImageView(mMiddleRing);
releaseImageView(mOuterRing);
releaseImageView(mPhone);
releaseImageView(mScreen);
}
private
void
releaseImageView(ImageView imageView) {
Drawable d = imageView.getDrawable();
if
(d !=
null
)
d.setCallback(
null
);
imageView.setImageDrawable(
null
);
imageView.setBackgroundDrawable(
null
);
}
|
此问题便解决了。
异常很经典,特记录之。
大神的tips:
Some tips:
-
Make sure you are not leak activity context.
-
Make sure you are don't keep references on bitmaps. Clean all of your ImageView's in Activity#onStop, something like this:
1234<code>Drawable d = imageView.getDrawable();
if
(d !=
null
) d.setCallback(
null
);
imageView.setImageDrawable(
null
);
imageView.setBackgroundDrawable(
null
);</code>
-
Recycle bitmaps if you don't need them anymore.
-
If you use memory cache, like memory-lru, make sure it is not using to much memory.
-
Not only images take alot of memory, make sure you don't keep too much other data in memory. This easily can happens if you have infinite lists in your app. Try to cache data in DataBase.
-
On android 4.2, there is a bug(stackoverflow#13754876) with hardware acceleration, so if you use
hardwareAccelerated=true
in your manifest it will leak memory.GLES20DisplayList
- keep holding references, even if you did step (2) and no one else is referencing to this bitmap. Here you need:a) disable hardware acceleration for api 16/17;
or
b) detach view that holding bitmap -
For Android 3+ you can try to use
android:largeHeap="true"
in yourAndroidManifest
. But it will not solve your memory problems, just postpone them. -
If you need, like, infinite navigation, then Fragments - should be your choice. So you will have 1 activity, which will just switch between fragments. This way you will also solve some memory issues, like number 4.
-
Use Memory Analyzer to find out the cause of your memory leak.
Here is very good video from Google I/O 2011: Memory management for Android Apps
If you dealing with bitmaps this should be a must read: Displaying Bitmaps Efficiently