by AlexQ (email alexq_andr@163.com)
工程托管在此:GitHub
这套系统跟随应用上线后,由于实际应用的结构要远高于demo,线上crash统计会捕捉到不少我们demo中发现不了的问题,收集到很多崩溃、异常问题后,也一直在持续修改、更新,总结一下反思一下也还是有不少值得总结的问题。
1. 生命周期函数不要随意干预
崩溃日志如下:
android.support.v4.app.SuperNotCalledException: Fragment
liveroomLibGDXFragment{64ab91f #0 id=0x7f0d00d6}
did not call through to super.onPause()
提示没有调用super.onPause()方法,来看看源代码以及设计原因
liveroomLibGDXFragment中的代码如下:
@Override
public void onPause() {
super.onPause();
//非销毁状态下,恢复继续展示
if (!m_isDestorying)
super.onResume(); //核心问题出在这里
但我们在demo中做退出操作,反复测试时未发现问题。
结论是不要随便调用生命周期方法。
这么做的原因当时设计时还特意做了解释。如下:
继承自AndroidFragmentApplication的子类中onPause处理,手动调用super.onResume,在我应该并能够看到特效的情况下,让动画别被frozen,连续的播放下去,如果在播放粒子特效时,突然由于用户点击,弹出一个dialog或者dialogstyle样式的activity(无论是半窗还是有透明度),我们此时一定是希望用户看到弹窗的同时,下面的粒子特效继续播放,这样的用户体验是连续的,视觉上也不是唐突的而是连贯的。那么如果我们不处理onPause的话,现象上会出现,粒子特效暂停了,frozen了,看一下libGDX中代码便知晓了,他在onPause时调用了this.graphics.pause();从而引起frozen了,只需要强制在此之后调用onResume即可,视觉效果上连续不间断,达到了我们的要求。
2. 深入源代码解决问题
崩溃日志如下:
java.util.ConcurrentModificationException问题
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
at java.util.HashMap$KeyIterator.next(HashMap.java:814)
at com.badlogic.gdx.graphics.Texture.getManagedStatus(Texture.java:285)
at com.badlogic.gdx.backends.android.AndroidGraphics.logManagedCachesStatus(AndroidGraphics.java:541)
at com.badlogic.gdx.backends.android.AndroidGraphics.clearManagedCaches(AndroidGraphics.java:536)
at com.badlogic.gdx.backends.android.AndroidFragmentApplication.onPause(AndroidFragmentApplication.java:228)
引起这个问题的原因从错误日志上很明显是HashMap操作上由于增删操作冲突引起的偶先的崩溃。(在日活跃20w的app中发生概率为千分之三左右,量还是很大的)
我们分析一下libgdx源代码:
public static String getManagedStatus () {
...
for (Application app : managedTextures.keySet()) {
...
}
...
}
问题就出在for循环上,两种方式解决此问题:
- try catch ConcurrentModificationException异常,但此方法可能只是忽略的了严重问题的出现;
- 使用迭代器解决此问题;(同学们可自行google “java 迭代器 ConcurrentModificationException”之类的);
已将此问题反馈给官方,但是没有得到回复。
神奇的是我自己编译了一遍libgdx源代码,发现代码此段代码会被自动优化成迭代器模式。另外,libgdx编译按照官网wiki上的说明即可,使用ant,真是超级方便。