Android 子线程更新主线程UI视图

        消息机制,对于Android开发者来说,应该是非常熟悉。对于处理有着大量交互的场景,采用消息机制,是再好不过了。在Android开发中,子线程不能更新主线程UI,而主线程又不能进行耗时操作(例:网络请求),一种常用的处理方法就是,在子线程中进行耗时操作,完成之后发送消息,通知主线程更新UI。或者使用异步任务,异步任务的实质也是对消息机制的封装。

我们在子线程去更改主线程视图时,都会抛异常:

Only the original thread that created a view hierarchy can touch its views.

AndroidRuntime: FATAL EXCEPTION: Thread-176
                     Process: com.example.joy.messagetest, PID: 11201
                     android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                              at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:635)
                              at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:87)

        Android实现View更新有两组方法,分别是invalidate和postInvalidate。前者在UI线程中使用,后者在非UI线程即子线程中使用。在子线程调用 invalidate 方法会导致线程不安全。熟悉View工作原理的人都知道,invalidate 方法会通知 view 立即重绘,刷新界面。假如,现在我用 invalidate 在子线程中刷新界面,同时UI线程也在用 invalidate 刷新界面,这样会不会导致界面的刷新不能同步?这就是invalidate不能在子线程中使用的原因。

  但是我们可以在子线程执行某段代码,需要更新UI的时候去通知主线程,让主线程来更新。如何做呢?常见的方法,除了前面提到的在UI线程创建Handler,在子线程发送消息到UI线程,通知UI线程更新UI,还有 handler.post(Runnable r)view.post(Runnable r)activity.runOnUIThread(Runnable r)等方法。发现其实它们的实现原理都还是一样,最终都是通过Handler发送消息来实现的。

xml页面:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.joy.messagetest.MainActivity">

    <TextView
        android:id="@+id/tv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Hello World!" />

    <Button
        android:id="@+id/btn_test2"
        android:layout_below="@id/btn_test1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Handler发送消息"/>

    <Button
        android:id="@+id/btn_test3"
        android:layout_below="@id/btn_test2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Handler.Post"/>

    <Button
        android:id="@+id/btn_test4"
        android:layout_below="@id/btn_test3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="View.Post"/>

    <Button
        android:id="@+id/btn_test5"
        android:layout_below="@id/btn_test4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Activity.RunOnUIThread"/>

</RelativeLayout>

java代码如下:

package com.example.joy.messagetest;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView mTvTest;
    private Button mBtnTest1;
    private Button mBtnTest2;
    private Button mBtnTest3;
    private Button mBtnTest4;
    private Button mBtnTest5;


    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 100) {
                mTvTest.setText("由Handler发送消息");
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        new Thread(new Runnable() {
            @Override
            public void run() {
                mTvTest.setText("子线程可以更新UI");
            }
        }).start();
    }

    private void initView() {
        mTvTest = (TextView) findViewById(R.id.tv_test);
        mBtnTest2 = (Button) findViewById(R.id.btn_test2);
        mBtnTest3 = (Button) findViewById(R.id.btn_test3);
        mBtnTest4 = (Button) findViewById(R.id.btn_test4);
        mBtnTest5 = (Button) findViewById(R.id.btn_test5); 
        mBtnTest2.setOnClickListener(this);
        mBtnTest2.setOnClickListener(this);
        mBtnTest3.setOnClickListener(this);
        mBtnTest4.setOnClickListener(this);
        mBtnTest5.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_test2:   //通过发送消息
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mHandler.sendEmptyMessage(100);
                    }
                }).start();
                break;
            case R.id.btn_test3:  //通过Handler.post方法
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTvTest.setText("handler.post");
                            }
                        });
                    }
                }).start();
                break;
            case R.id.btn_test4:  //通过 view.post方法
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mTvTest.post(new Runnable() {
                            @Override
                            public void run() {
                                mTvTest.setText("view.post");
                            }
                        });
                    }
                }).start();
                break;
            case R.id.btn_test5:  //通过 activity 的 runOnUiThread方法
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mTvTest.setText("runOnUIThread");
                            }
                        });
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

只有初始创建视图的线程才能触碰这些视图,也就是说只有主线程才能更新UI。

猜你喜欢

转载自blog.csdn.net/minusn/article/details/126589121
今日推荐