Message,Handler报空指针异常错误
问题描述:
在最近工作中分给我一个问题,是一个点击退出按钮应用闪退的问题,细节点就是一个关于空指针异常的报错,代码如下:
Message message = mHandler.obtainMessage();
message.what = xxx;
mHandler.sendMessage(message);
报错日志如下:
E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Message android.os.Handler.obtainMessage()' on a null object reference
(需要注意此段函数是在一个线程中执行的)
分析过程
1
刚开始我的分析是空指针是因为从 mHandler.obtainMessage()中没有获取到对象,所以更换了获取对象的方式
// Message message = mHandler.obtainMessage();
Message message = Message.obtain(mHandler);
2
修改后,应用依然闪退,空指针依然存在,但是此时报错位置的到了mHandler.sendMessage(message);那么说明,问题不在于message,而在于mHandler.
所以再对其添加异常判断,此时的代码就变成了:
// Message message = mHandler.obtainMessage();
Message message = Message.obtain(mHandler);
if(message == null){
message = new Message();
Log.d(TAG,"message == null");
}
message.what = xxx;
// 异常控制,使得出现异常时不会奔溃出错
try {
mHandler.sendMessage(message);
}
catch (NullPointerException e){
e.printStackTrace();
Log.e(TAG, "mHandler.sendMessage(message) has NullPointer");
}
此时,在点击按钮退出时,就不会出错了,代码提交合入皆大欢喜.
3
如果只是到了上面的两步,解决了问题,那我也就不用写这篇文档了
在其他机器上做了测试,相同的代码逻辑,跑起来没有问题,点击也不出错,说明代码没有问题,那么问题会出在哪里
4
和同事聊了一下,我们认为这个属于系统问题,内存太小导致,那么从头来看空指针是从哪里来的,一般来说空指针出现一般就是下面几种情况:
- 对象未初始化
- 对象置空
先看初始化,mHandler类中的资源,在定义时就已经初始化,而不是在onCreate()等回调函数中
public class XXXActivity{
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {}
}
}
那么就说明,不是初始化的问题,且在退出前的使用都是正常的,那么就只剩下置空了
置空的代码是在onDestroy()中
@Override
protected void onDestroy() {
mHandler = null;
}
那么我的推测就是当mHandler置空时,因为系统内存小(4G),所以为了腾出更多的空间,在刚置为null时,就会触发GC释放资源,而前面的函数是在线程中跑的,代码中也没有等待线程执行结束的代码,所以线程在执行需要资源时,报出了空指针异常
而在大内存的机器上,不会立马触发GC,此时还可以获取到资源所以不会报出错误
修改思路
我的修改方法只能作为一种规避手段,当发生错误时,让其抛出错误,退出执行,而不至于执行出错影响整个应用
想要让这个错误不发生,那么还可以在置空前先等待线程执行结束,再做置空