Android非常规BUG之createView AndroidRuntime(29797)

Android非常规BUG之createView AndroidRuntime(29797)

如今的手游越来越注重画面、特效,美术方面希望游戏UI美观大方,动画优美细致,策划方面希望游戏上手易学、交互友好,而程序自身需要实现两者,并保证程序的稳定性。当然这些需求直接决定这游戏的可玩性,但是有时候个别天马行空的构思对经验不足的程序来说,简直是天方夜谭,Android应用开发搬来抄去的做法对程序员并不是耻辱,而是提高效率很好的方法,虽然前无古人后无来者的事情未必不能实现,但我更倾向于做一些力所能及的事情,于是乎某些参考IOS甚至Cocos的构思只能在我手中湮灭了。

本文将要讨论“由于内存紧张导致的布局加载失败”问题,至于类似问题的根源尚未可知,但与内存利用、垃圾回收干系甚大,重点在于“优化”,而不敢说“解决”类似问题。

导致内存溢出的原因有很多,最直接的莫过于图片加载过多过大、布局过于复杂、前期资源未释放等等,这里我没兴趣说什么“多用软引用、虚引用”、“利用Options缩减图片质量”,如果在加载一个布局的时候因为一个ImageView崩溃了,有谁会想的通在布局中初始化图片时在createView处为何崩溃?!当然可以用软引用进行优化,但某些情境下,软引用替代不了强引用,而且降低图片Options是针对Bitmap,只是降低内存占用,也并未从根源上解决,再者,能从createView崩出去的异常说明内存已经紧张到连渲染布局中的一个默认背景图都不可以了,即便是换软引用,也难保证加载的时候那个图片没有被回收。因此遇到这种情况,我个人觉得,应该去前面优化内存,而非在内存紧张的情况下改用什么软引用之类的加载方式。

在我的上上个项目中,准备把一个老程序,进行改版,更换UI,另外新增一些新鲜玩法,原游戏一些ScrollView、GridView被取消,换用全场景长幅图片滚动显示,虽然使界面更加自然随和,但控件数、图片量翻倍增长,还要添加很多动画、特效,直接丧失了老程序的简洁、流畅,虽然继承了老程序的大体框架,但新需求已经撑的老框架千疮百孔,而且项目后期开始多机型安装调试,在个别定制系统中更加暴露出一些闻所未闻的异常,这便是我第一次碰到这种加载一个布局也会崩溃的BUG。

问题日志大致如下:

E/dalvikvm-heap(29797): Out of memory on a 2791984-byte allocation.

E/AndroidRuntime(29797): FATAL EXCEPTION: main

全部是 “E/AndroidRuntime(29797):”这种标识的错误,后续省略之

android.view.InflateException: Binary XML file line #148: Error inflating class <unknown>

at android.view.LayoutInflater.createView(LayoutInflater.java:684)

at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)

at com.android.internal.policy.impl.MiuiPhoneLayoutInflater.onCreateView(MiuiPhoneLayoutInflater.java:44)

at android.view.LayoutInflater.onCreateView(LayoutInflater.java:731)

at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:756)

at android.view.LayoutInflater.rInflate(LayoutInflater.java:817)

··············     ···············       ··············

Caused by: java.lang.reflect.InvocationTargetException

at java.lang.reflect.Constructor.constructNative(Native Method)

at java.lang.reflect.Constructor.newInstance(Constructor.java:417)

at android.view.LayoutInflater.createView(LayoutInflater.java:658)

··············     ···············       ··············

Caused by: java.lang.OutOfMemoryError

at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)

at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:502)

at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:355)

at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:827)

at android.content.res.Resources$Injector.createFromResourceStream(Resources.java:83)

at android.content.res.Resources.loadDrawable(Resources.java:2003)

at android.content.res.MiuiResources.loadDrawable(MiuiResources.java:324)

at android.content.res.TypedArray.getDrawable(TypedArray.java:601)

at android.view.View.<init>(View.java:3414)

at android.view.View.<init>(View.java:3343)

at android.view.ViewGroup.<init>(ViewGroup.java:538)

at android.widget.RelativeLayout.<init>(RelativeLayout.java:210)

… 24 more

由于平时调试程序仅限于一两个测试机,自己手机上很少见崩溃异常,但安装到一些高档机器上反而出现各种崩溃,比如在米3、三星上运行起来,还不如几百块的华为、HTC上稳定,正如Boss常言:我们总不能让玩家换手机吧?!起初我坚信高档手机上不会比普通手机内存还低,首先排除了内存紧张的问题,沿用老程序改良后的框架。无奈之下,向组长求助,经过他对程序框架的分析,认为是场景之间跳转的栈式结构没有优化导致内存浪费严重,建议我跳转一次就结束回收一个。

由于框架调整动作较大,我先按照日志信息提示对问题的位置进行排查,最终确认,“Binary XML file line #148”,将布局文件第148行位置的控件的属性“android:background=’@drawable/img_bg’”注释掉,将大大的降低布局渲染在该位置崩溃的概率,但是偷梁换柱,错误日志很可能跑到了另外一个控件所在位置,这样的行为属于标、本都没治好,即没了背景图片,又将问题转移到了别处。

鉴于崩溃位置在于setContentView(layout)或createView,那么在布局中直接设置背景图不行,就转换方式,用代码根据控件ID对其进行setBackgroundResource(drawable);这样做虽然使程序在个别手机上崩溃的概率有所降低,但是还是没有抓住重点,依然时不时的崩溃在这种异常上。

前面两种尝试都没有解决崩溃的发生,只能按组长的指导进行大改造了,改造环节有三:

一、采用逐一销毁的方式,保证程序运行中只有一个Activity处于活跃状态,其他界面一旦跳出就立即销毁回收,缺点是需要已销毁Activity还原销毁前状态并有后继动作的情况下,恢复早前的状态比直接初始化当前的状态还要多绕一个弯。

二、Activity结束后立即对其资源进行整理、回收操作。回收步骤有3:

1、在Handler里面监听一个消息MSG_RECYCLE,收到消息后,在里面清空、回收占用内存较多的对象。

private Handler mHandler = new Handler() {

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_RECYCLE:

bmpBG.recycle();

bmpBG = null ;

System.gc();

break;

}

};

};

然后在finish();语句后面发送这个回收资源的指令:mHandler.sendEmptyMessage(MSG_RECYCLE);

2、在onDestroy内部进行全部对象置空的操作。

 

 

 

 

 

 

3、在资源对象置空后,最好再次对非空资源进行回收操作,也就是步骤(1)。

有人说,这个Activity将要销毁了,在finish()后面必然会进入onDestroy()方法,何必多此一举呢,直接在onDestroy中发出MSG_RECYCLE消息不就好了吗!

但是我个人分析,内存最紧张的时候就是前一Activity没有Destroy,而后一Activity Create加载布局、渲染图片,而startActivity开始跳转后,要等数秒钟,前一Activity才会执行onDestroy方法,下面是我对跳转过程中时间的打印日志,注意执行间隔:

跳转过程中得日志

 

 

 

 

在这个日志中finish()过了大约10秒左右,前一Activity才执行onDestroy方法,似乎迟的有些严重,因此在后一Activity最需要内存的时候应该先回收一部分内存,而不是傻傻的等待十秒后在onDestroy处回收。

三、对于个别较大的图片,在布局中默认它的背景图片为空,而在layout铺设完毕后,再用代码进行背景图设置,这样将减小createView过程的压力,如果有必要,甚至可以给某些占用内存较多的图片渲染设置一定的延迟

总结:

这种异常是实实在在存在的,但绝大多数开发中,少有一个Activity就占用很多内存的应用,抑或即便不进行可以的回收操作也未必会出现内存溢出的问题,而且这个BUG出现的规律是:垃圾手机上正常,牛×手机上崩溃。之所以说是非常规错误,就是因为没有什么规律或理论支持。上文所书,纯属个人经验,如过哪位有更深刻见解,求指导、完善。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值