使用sensor的时候,有一个问题困扰了我很多天。
每次打开app读取sensor数值,只要过了60秒,logcat就会出现:
更奇怪的是,只要按过按键之后,出错时间就会重新被更新为60秒之后。
在反复检查了driver和hal的代码并确认无误之后,终于不得不把目光转向了更上层。
由于简短的jni代码只是负责调用,并没有再添加多少处理代码;而app代码非常简单,所以把问题锁定在了framework层。
根据报错的调试信息,可以快速地通过ctrl + F定位到SensorManager.java
所以问题出错的原因是sListeners.isEmpty()返回true,而sListeners的定义是:
static final ArrayList<ListenerDelegate> sListeners = new ArrayList<ListenerDelegate>();
通过在网上不多关于Android Sensor的搜索,得知isEmpty的原因是Listener的列表为空。
可是为什么过了60s列表就会被清空呢?在线程中查看了很多次,都没有发现移除的相关代码。不管是和control_activate、control_wake还是enableSensor相关的代码都没有影响到sensor列表。在偶然的查看代码中,突然想到了查找“remove”看是否有移除sensor的方法。最后,终于找到了:
根据注释很明显就是这里导致了sListeners.isEmpty()。再一看,居然是在unregisterListener方法中调用。看来是因为unregisterListener被调用导致了出错,再回到app代码查看,才发现原来是Activity的onPause方法中调用了unregistereListener()。
在onPause中加了句调试信息,果然,没动触摸屏或键盘60秒后,onPause就会被调用。到了这个时候,只能怪自己不去理解透彻Activity的工作原理,照着实例代码写直接在onPause中加了不该加的代码。
等等!我之前自己专门加上的unregisterListener调试信息不是在一开始,就跟随报错一起输出了吗?为什么忽略了这条信息而错误地在其他地方查错?回想一下,其实一开始也注意到了,只是这条信息写得和系统的报错信息实在是太像了,让我误以为是framework内某段代码的判断输出。
所以,这次调试得到了几个经验:
- 个人的调试信息写得特别一点,要一眼就能看出来是自己写的,而不会被埋没在大量调试输出中;
- 内核或Android系统提供的代码出错概率不大,优先考虑自己的代码和接口有什么不符合;
- 即使是很短的小代码,也有可能导致出错。