美文网首页Android面试二
Handler(四) - 子线程中更新UI的几种方式

Handler(四) - 子线程中更新UI的几种方式

作者: 世道无情 | 来源:发表于2019-01-27 16:29 被阅读118次

    1. 概述


    在子线程中更新UI的方式有以下几种:

    1>:handler.handleMessage(msg);(上篇文章写了)
    2>:handler.post(Runnable r);
    3>:view.post(Runnable r);
    4>:Activity的 runOnUiThread(Runnable r);

    2. 子线程中更新UI的3种方式


    1>:handler.post(Runnable r):
    源码分析如下:
        public final boolean post(Runnable r){
           // 发送消息,把传入的Runnable对象作为 msg消息
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
        // 这个和 handler.sendMessage()方法调用方法一样,最终都会调用 sendMessageAtTime() 方法,
        // sendMessageAtTime() 作用:发送一条消息
        public final boolean sendMessageDelayed(Message msg, long delayMillis){
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
        // getPostMessage()作用:把 Runnable对象 转为一条消息
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            // 把 消息的 callback的值 作为 传进来的 Runnable 对象 ,这个 callback 就是 handler的
            // dispatchMessage(msg) 方法中的 callback,源码如下:
            m.callback = r;
            return m;
        }
    
        public void dispatchMessage(Message msg) {
            // 如果 msg消息里的callback的值 不为null,就执行 handleCallback(msg)方法,源码如下:
            // 否则执行 handler的 handleMessage(msg)方法
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
        private static void handleCallback(Message message) {
            // 这个 run()方法 就是: handler.post(new Runnable).run() 的 run()方法
            // 直接执行 该 run() 方法就 ok
            message.callback.run();
        }
    

    在 handler.post(new Runnable).run() 方法中更新UI,和在 handler.handleMessage() 方法中更新UI一样;

    2>:View的post(Runnable r) 方法:View指的就是任意一个View,比如TextView、ImageView、Button等等所有的View;
    源码分析如下:
        public boolean post(Runnable action) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                // 这里还是调用handler.post()方式,和 handler.post()方式调用源码一样
                return attachInfo.mHandler.post(action);
            }
    
            getRunQueue().post(action);
            return true;
        }
    
        public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
    3>:Activity 的 runOnUiThread
    源码分析如下:
        public final void runOnUiThread(Runnable action) {
            // 如果当前线程不是主线程,即就是如果是子线程,就调用 handler的post方式,
            // 否则 调用 Runnable 的 run()方法 
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
    
        // handler 的 post 方法
        public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
    // Runnable 的 run 方法
    public interface Runnable {
        public abstract void run();
    }
    

    3. 代码实现 - 子线程4种更新UI方式


    效果图如下:
    图片.png
    代码如下:

    1>:activity_test.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="20dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="20dp">
    
        <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_test1"
            android:layout_below="@id/tv_test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="子线程更新UI测试"/>
    
        <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>
    

    2>:TestActivity代码如下:

    /**
     * ================================================
     * Email: 2185134304@qq.com
     * Created by Novate 2018/12/23 9:51
     * Version 1.0
     * Params:
     * Description:    子线程更新UI的4种方式:
     *                      handler发送消息(handler.handleMessage)
     *                      handler.post(Runnable r)
     *                      view.post(Runnable r)
     *                      activity.runOnUiThread(Runnable r)
     *                      
     *      备注:子线程不能更新UI,即就是 new Thread(new Runnable())中不能更新UI,会崩溃                
     * ================================================
    */
    
    public class TestActivity 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
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            initView();
    
        }
    
        private void initView() {
            mTvTest = (TextView) findViewById(R.id.tv_test);
            mBtnTest1 = (Button) findViewById(R.id.btn_test1);
            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);
            mBtnTest1.setOnClickListener(this);
            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_test1:  // 点击直接崩溃
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            mTvTest.setText("子线程不能直接更新UI,会崩溃!");
                        }
                    }).start();
                    break;
                case R.id.btn_test2:   // 通过 handler.sendMessage() 发送消息
                    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;
            }
        }
    }
    

    4. 总结


    由上边可知:子线程中更新UI的方式有4种:

    1>:handler.handleMessage(msg);
    2>:handler.post(Runnable r);
    3>:View.post(Runnable r):View指的是任意View,比如TextView、ImageView、Button等等;
    4>:Activity中的 runOnUiThread(Runnable r);直接在Activity中写这个方法即可;

    注意:子线程不能直接更新UI,会崩溃;

    以上任意一种更新UI的方式,原理都是一样的,都是通过 handler发送消息(异步消息处理机制)完成的,就是上篇文章分析的 源码;

    相关文章

      网友评论

        本文标题:Handler(四) - 子线程中更新UI的几种方式

        本文链接:https://www.haomeiwen.com/subject/iajwkqtx.html