《第一行代码》第二版 学习总结26 Android中子线程更新UI的三种方式

      最近利用下班时间,找了看什么书比较适合初学android的朋友,很多人推荐了这本书,于是就买了一本,感觉看书,思考,动手,再思考和总结这样过程还是很有必要的,于是就打算把自己学习的东西简单的总结一下;方便自己以后查找,也有利于学习的巩固。在这里首先要感谢一下书籍的作者——郭霖前辈。

      关于Android中子线程更新UI主要就是实现间通信即可,我们下面将会介绍关于在子线程中更新UI的三种方式;在此之前,你可见简单了解一下java中的多线程相关知识,我在前段时间关于多线程的基本知识介绍了一点(多线程一之基本概念多线程二之线程生命周期多线程三之共享数据安全问题多线程四之死锁与等待唤醒机制);有了对多线程的基本认识,我们就来看看如何实现子线程更改UI(线程间通信)。示例代码下载链接


1,子线程更改UI的方式

    上面也说了有三种,分别是:

  • Handler实现
  • runOnUiThread()
  • AsyncTask

其实这几个知识是可以单独拿出来说的,尤其是Handler通过的这种异步消息处理机制,其他的两种方式说白了也是依托于这种机制;就是进行了不同的封装而已,所以这里会简单的介绍一个android一步消息处理机制,其他两个不会展开说明,只会介绍使用步骤。

2,异步消息处理机制

四个部分:

  • Message :线程之间传递的消息
  • Handler : 发送和处理线程间通信的消息(Message)
  • MessageQueue :消息队列,用于存放Handler发送来的消息,等待被处理,一个线程只能有一个消息队列对象
  • Looper :管理(监测--loop()方法)消息队列,队列中有消息就取出处理

为了加深一下印象,我自己也画了异步消息处理的流程图(建议看书上的哈哈);其实Android已经帮我们做了很好的封装,使用起来也是非常的方便;下面就来看看具体的使用



3,示例代码


MainActivity.java代码:

package com.hfut.operationuionsubthread;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {
    public static final int UPDATE_INFO = 1;
    public static final int DOWNLOAD_PROGRESS=2;
    public static final int DOWNLOAD_COMPLETE=3;
    TextView result;
    int progress = 0;
    ProgressBar progressBar;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_INFO:
                    result.setText("Handler:\n我是通过Handler方式修改UI方式");
                    break;
                case DOWNLOAD_PROGRESS:
                    result.setText("正在下载,请稍等...");
                    progressBar.setProgress(progress);
                    break;
                case DOWNLOAD_COMPLETE:
                    result.setText("下载完成:\nAsyncTask:\n我是通过AsyncTask和Message配合在子线程中实现修改UI的");
                    Toast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        result = findViewById(R.id.tv_result);
        progressBar=findViewById(R.id.downloadProgress);
    }


    //handler方式修改UI  public void byHandler(View view) {
        progressBar.setProgress(0);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //可以尝试在子线程中直接修改UI  //result.setText("我要在子线程中修改UI了,也不知道行不行了");  Message message = new Message();
                message.what = UPDATE_INFO;
                handler.sendMessage(message);

            }
        }).start();
    }

    //runOnUiThread方式修改UI  public void byRunOnUiThread(View view) {

        progressBar.setProgress(0);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                result.setText("runOnUiThread:\n我是通过runOnUiThread实现在子线程中修改UI的");
            }
        });

    }

    //AsyncTask方式修改UI  public void byAsyncTask(View view) {
        progress=0;
        progressBar.setProgress(0);
        new ChangeUI().execute();
    }

    class ChangeUI extends AsyncTask<Void, Integer, Boolean> {

        @Override
        protected Boolean doInBackground(Void... voids) {
                boolean endTag = true;
                final Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        progress += 2;
                        if(progress<=10) {
                            message.what = DOWNLOAD_PROGRESS;
                            handler.sendMessage(message);
                        }
                        else{
                            message.what=DOWNLOAD_COMPLETE;
                            handler.sendMessage(message);
                            timer.cancel();
                        }
                    }
                }, 1000, 1000);

// try { // Thread.currentThread().sleep(5000); // } catch (InterruptedException e) { // e.printStackTrace(); // }  return endTag;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            if (aBoolean) {
                result.setText("AsyncTask:\n我是通过AsyncTask在子线程中实现修改UI的");
            }
        }
    }
}
 
 

activity_main.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"  tools:context="com.hfut.operationuionsubthread.MainActivity">

    <Button  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="20dp"  android:onClick="byHandler"  android:text="handler实现"  android:textSize="20dp" />

    <Button  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="10dp"  android:onClick="byRunOnUiThread"  android:text="runOnUIThread实现"  android:textSize="20dp" />

    <Button  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_marginTop="10dp"  android:onClick="byAsyncTask"  android:text="AsyncTask实现"  android:textSize="20dp" />


    <TextView  android:id="@+id/tv_result"  android:textSize="15dp"  android:layout_width="match_parent"  android:layout_height="60dp"  android:hint="显示修改UI结果" />

    <!--<TextView-->  <!--android:layout_marginTop="10dp"-->  <!--android:layout_width="match_parent"-->  <!--android:layout_height="wrap_content"-->  <!--android:text="下载进度"/>-->   <ProgressBar  android:layout_marginTop="5dp"  android:id="@+id/downloadProgress"  android:max="10"  style="?android:attr/progressBarStyleHorizontal"  android:layout_width="match_parent"  android:layout_height="wrap_content" />

</LinearLayout>
 
 

主配置文件AndroidManifest.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  package="com.hfut.operationuionsubthread">

    <application  android:allowBackup="true"  android:icon="@mipmap/ic_launcher"  android:label="@string/app_name"  android:roundIcon="@mipmap/ic_launcher_round"  android:supportsRtl="true"  android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

    代码结构很简单,只有一个活动和一个布局,布局中有三个按钮分别表示三种在子线程中更改UI的方式,其中,第三种我结合了第一种,是一种组合的形式,正常它是通过doInBackground()返回值调用onPostExecute()方法里面的代码来更改UI的,在doInBackground()中是不能修改UI的。


4,运行结果

第一步:运行程序

                              

第二步:点击“HANDLER实现”按钮

                              

第三步:点击“RUNONUITHREDA实现”按钮

                              

第四步:点击“ASYNCTASK实现”按钮

                              

                              

注:

1,在使用AsyncTask的时候,我们需要自定义一个类去继承该抽象类,并传入三个参数,第一个是在执行该任务时需要传入的参数,比如这里执行模拟下载任务,可以传递一个下载URL;第二个表示执行任务的进度单位,第三个是对执行结果的返回,比如这里下载成功返回true,失败返回false。

2,在使用AsyncTask的时候,里面的doInbackground()方法主要处理耗时操作,onProgressUpdate()用于配合第二个参数更新UI;onPostExecute()主要结合doInBackground()返回值处理耗时任务执行完成后的Task收尾工作;

3,为什么要提出异步消息处理机制用于在子线程中修改UI;因为UI数据是异步不安全的;当多个线程操作共享数据(UI)时就会出现数据安全问题;所以才会有这么一个机制;对于多线程数据安全问题可以查看我之前的博客,里面有不错的例子和示例代码。

总结:上面的这些主要目的就是实现不同线程间通信,为的就是解决共享数据安全问题,只不过我们现在使用的都是系统包装好的功能。但是本质还是一样的 。