Message,Handler报空指针异常错误

685 阅读2分钟

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

和同事聊了一下,我们认为这个属于系统问题,内存太小导致,那么从头来看空指针是从哪里来的,一般来说空指针出现一般就是下面几种情况:

  1. 对象未初始化
  2. 对象置空
    先看初始化,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,此时还可以获取到资源所以不会报出错误

修改思路

我的修改方法只能作为一种规避手段,当发生错误时,让其抛出错误,退出执行,而不至于执行出错影响整个应用
想要让这个错误不发生,那么还可以在置空前先等待线程执行结束,再做置空