Android Handler之更新ui使用分析

 

在Android中Handler相信大家都很熟悉了,主要用在:将工作线程中需要操作UI的消息传递到主线程,主线程收到消息后根据需求更新UI。

这里举个例子看下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    mBtn = findViewById(R.id.text);
    new Thread(){
        @Override
        public void run() {
            super.run();
            Log.e(TAG,"My Thread running");
            mBtn.setText("hello");
        }
    }.start();
}

 

这里我们直接在onCreate方法中创建一个子线程,并更新UI, 这时候跑下程序会发现,程序正常运行,并且ui也更新了,这是为什么呢?不是说不能在子线程更新ui吗?

其实这是一个比较特殊的情况,通过源码知道子线程更新ui报的异常是在android.view.ViewRootImpl中抛出的:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }

}

ViewRootImpl的创建是在onResume方法之后的,所以直接在onCreate创建子线程更新ui并不会抛出异常。

接下来我们改下例子:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    mBtn = findViewById(R.id.text);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startThread();
        }
    });
}

private void startThread(){
    new Thread(){
        @Override
        public void run() {
            super.run();
            Log.e(TAG,"My Thread running");
            mBtn.setText("hello");
        }
    }.start();

}

 这里我们把子线程放到点击事件中开启,运行程序,点击button,这是发现抛出异常了:

Android Handler之更新ui使用分析

这就是ViewRootImpl中要求不能再子线程中更新ui抛出的异常信息。

那么我们如果要在子线程中更新Ui要怎么做呢?这就是我们要介绍的Handler,这里我们分两种情况来使用

1.直接在子线程中new Handler代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    mBtn = findViewById(R.id.text);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startThread();
        }
    });
}

private void startThread(){
    new Thread(){
        @Override
        public void run() {
            super.run();
            Log.e(TAG,"My Thread running");
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    mBtn.setText("hello");
                }
            });
        }
    }.start();

}

跑下程序,发现崩了:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

        at android.os.Handler.<init>(Handler.java:200)

        at android.os.Handler.<init>(Handler.java:114)

这是因为Android系统默认情况下非主线程中没有开启Looper,而Handler对象必须绑定Looper对象。

我们修改下代码:

new Thread(){
    @Override
    public void run() {
        super.run();
        Log.e(TAG,"My Thread running");
        new Handler(getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                mBtn.setText("hello");
            }
        });
    }

}.start();

在Handler中加入getMainLooper()方法,把它绑定到主线程的looper这样跑下程序,发现可以了。

2.在主线程中new Handler:

 mHandler new Handler();
    mBtn = findViewById(R.id.text);
    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startThread();
        }
    });
}

private void startThread(){
    new Thread(){
        @Override
        public void run() {
            super.run();
            Log.e(TAG,"My Thread running");
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mBtn.setText("hello");
                }
            });
        }
    }.start();

}

这样跑起来也是没问题的。

Android Handler的简单使用就介绍到这边,接下来一章,会结合源码来详细分析下Handler的机制。