华为5.1和5.1.1手机出现java.lang.IllegalArgumentException: regist too many Broadcast Receivers
表面上看是注册太多广播导致出现异常,通过崩溃日志观察发现,崩溃量不少,而且全部都是华为5.1和5.1.1上出现的,可知是机型特定问题。
于是使用华为5.1.1测试,动态注册广播并计数,发现每次刚好到注册第501个广播时出现IllegalArgumentException,在其他手机上测试注册到4w+个广播也没出现问题,验证是机型特定问题,并且该机型上最多只能注册500个广播导致。
同时也怀疑是不是rom内部计数有问题只增不减导致超过500,于是加入反注册进行验证,结果是注册减去反注册广播数达到500就达到了上限,可知计数没有问题。
于是产生了两种思路解决该问题:
1.做一个广播总控制器,控制广播数量
2.每次注册前进行反注册
做一个广播总监控器,控制广播数量,是希望在广播超过限额时将一些优先级低的反注册掉,
这样考虑的原则是,即使放弃一些业务的稳定性,也好过让整个进程崩溃,但是,这就面临几个
问题了,超过500个广播都一个个挂接到这个监控器上?怎么确定所有广播的优先级?而且我发现
还忽略了一个很重要的问题,虽然是一个庞大的项目,但是怎么可能会有500个广播呢?
目前来看,也许第二种思路会更简单和稳妥些。通过以前的埋点数据,发现确实可能是更深层的原因,
已知有几个广播被重复注册上百次,而且这几个广播都在一个后台Service的onCreate里注册,onDestroy里反注册,而且onCreate也确实被调用过上百次,这时产生了一个猜想,是不是这个后台Service在不调用onDestroy的情况被干掉,再加载起来了,此时就跳过了这几个广播的反注册,而完成了重复注册的事件,为了验证这个猜想,在网上翻阅只知道有人有相同的困惑相同的猜想,然而都不得证实。
翻阅google文档,关键信息摘录如下:
A service can be both started and have connections bound to it. In such a case,
the system will keep the service running as long as either it is started or
there are one or more connections to it with the Context.BIND_AUTO_CREATE flag.
Once neither of these situations hold, the service's onDestroy() method is
called and the service is effectively terminated. All cleanup (stopping threads,
unregistering receivers) should be complete upon returning from onDestroy().
Service的释放资源,反注册广播应该放到onDestroy,Service的生命周期结束也只有这么一个回调。
When your activity receives a call to the onStop() method, it's no longer visible and
should release almost all resources that aren't needed while the user is not using it.
Once your activity is stopped, the system might destroy the instance if it needs to
recover system memory. In extreme cases, the system might simply kill your app process
without calling the activity's final onDestroy() callback, so it's important you use
onStop() to release resources that might leak memory.
Although the onPause() method is called before onStop(), you should use onStop() to
perform larger, more CPU intensive shut-down operations, such as writing information to
a database.
Activity的释放资源,反注册广播应该放到onPause,onStop而不能放到onDestroy因为可能被干掉时不被调用,不可靠。
对比官方对Activity和Service的说法,Service的onDestroy应该是可靠的,Activity的onDestroy是不靠谱的,这么说也不应该出现Service的onCreate被调用多次而onDestroy不被调用的情况。
因此决定,在Service的onCreate和onDestroy都加一个静态变量统计被调用次数,于此同时,将被大量重复注册的广播在注册前反注册,不过这里需要注意一个问题,如果直接在注册前进行反注册,并不能直接完成反注册,因为可能在当前Service里声明的成员变量,在Service被干掉时下一次调用时不是同一个引用,所以需要把这个广播改成静态的,这样就能确保广播独立在Service之外,每次注册和反注册进行判断时都是对应同一个广播。
尝试性解决问题:在Service的onCreate和onDestroy加入静态变量统计注册数看是否两者调用次数相差很大,并将被大量重复注册的广播改成静态变量,在注册之前进行反注册操作。
先尝试性解决,后面继续跟进这个问题的修复程度及验证结果,未完待续。