前几日做项目的时候使用了静态的成员变量Handler,如下所示:
public class MyUtility
{
private static final int MSG_ADD = 0;
private static final int MSG_DECREASE = 1;
private static int cnt = 0;
public static Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
switch(msg.what)
{
case MSG_ADD:
cnt++;
break;
case MSG_DECREASE:
cnt--;
break;
default:
break;
}
}
};
public static void bench()
{
Message msg = handler.obtainMessage(MSG_ADD);
handler.sendMessage(msg);
new Thread(){
@Override
public void run()
{
Message msg = handler.obtainMessage(MSG_DECREASE);
handler.sendMessage(msg);
}
}.start();
}
}
并在主线程中一个按钮的onClick事件中调用这个类中的静态函数:
public void onClick(View vw)
{
new Thread(){
@Override
public void run()
{
MyUtility.bench();
}
}.start();
}
注意是在子线程中调用的MyUtility中的函数,结果运行中出错,调试发现如下错误:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
意思是说handler是在子线程中创建的,而子线程没有调用Looper.perpare()。
原先以为static成员变量是在程序加载的时候初始化,怎么会在子线程中创建呢?网上搜索也没发现有价值的线索,最后只有自己总结了。网上说静态变量是在类加载的时候初始化,难道是MyUtility是在第一次使用的时候才加载?如果程序运行过程中没有使用这个类,那么这个类是不会被程序加载的?带着这样的疑问,进行了下面的调试:
如果类第一次使用是在子线程中,那么类加载是在子线程中进行的,static变量的初始化也是在子线程中进行。把Handler改成如下声明方式(这样可以打断点进行调试):
public static Handler handler = null;
static{
handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
switch(msg.what)
{
case MSG_ADD:
cnt++;
break;
case MSG_DECREASE:
cnt--;
break;
default:
break;
}
}
};
}
经调试发现,果然如我总结的一样。看来java中static成员变量初始化时机和c++中static成员变量不一样啊。
发现问题的原因就好解决问题了,第一次使用MyUtility类要在主线程中(比如先在主线程中随便调用一个MyUtility函数或者变量),那么就不会出现java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()异常了。
后续:在一篇文章中看到,Java和C#语言都支持类的动态加载,而C++不支持!