android中的类加载和静态成员变量的初始化

来源:互联网 发布:linux创建用户组和用户 编辑:程序博客网 时间:2024/04/27 14:53

前几日做项目的时候使用了静态的成员变量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++不支持!


原创粉丝点击