android--异步消息处理机制(线程之间通信)+AsyncTask

Android不允许在子线程中进行UI操作。但是有些时候,我们必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI。对于这种情况,Android提供了一套异步消息处理的使用方法。
我们以在子线程中进行myCount++操作,并将结果作为textview中的值来显示为例,来学习android的异步消息处理机制。
代码如下:

package com.example.myandroidthread;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {


    Button btn_exectask;
    TextView tv_changeText;
    int myCount;
    public static final int UPDATE_TEXT = 1;

    public class UpdateCountThread implements Runnable{

        Handler mainHandler;
        @Override
        public void run() {
            for(int i=0;i<1000;i++){
                for(int j=0;j<1000;j++){
                    myCount++;
                    Message message = new Message();
                    message.what = UPDATE_TEXT;
                    message.arg1 = myCount;
                    handler.sendMessage(message);
                }
            }
            Log.d("MainActivity","任务结束");
        }


        UpdateCountThread(Handler handeler){
            this.mainHandler = handler;

        }

    }


    Handler handler = new Handler(){

        public void handleMessage(Message msg){
            //消息id
            switch (msg.what){
                case UPDATE_TEXT:
                    String show = "myCount="+msg.arg1;
                    tv_changeText.setText(show);
                    break;
                    default:
                        break;
            }
        }

    };


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

        myCount = 0;

        btn_exectask = findViewById(R.id.btn_exectask);
        tv_changeText = findViewById(R.id.tv_changeText);

        btn_exectask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("MainActivity","开始任务线程");
                new Thread( new UpdateCountThread(handler)).start();
                Log.d("MainActivity","主线程继续。。。。");

                Toast.makeText(MainActivity.this,"结束任务", Toast.LENGTH_SHORT).show();
            }
        });


    }
}


解释一下:
先定义了一个整型变量UODATE_TEXT,用于表示更新TextView的这个动作。然后新增一个Handle对象,并重写父类的handelMessage()方法,在这里对具体的Message进行处理。若发现Message中的what字段的值等于UPDATE_TEXT,就将TextView显示的内容改成myCount的值。
下面再来看一下change text按钮的点击事件中的代码。这次我们并没有在子线程里直接进行UI操作,而是创建了一个Message(android.os.Message)对象,并将它的what字段的值指定为UPDATE_TEXT,然后调用Handler的sendMessage()方法将这条Meaasge发送出去。很快,Handler就会收到这条Meaage,并在handleMessage()方法中对它进行处理。注意此时handleMessage()方法中的代码就是在主线程当中运行的了,所以我们可以放心的进行UI操作。然后对Message携带的what字段的值进行判断,如果等于UPDATE_TEXT,就将TextView显示的内容改成myCount的值。
解释一下它的原理:
在这里插入图片描述
在这里插入图片描述
每一个线程都可以有一套消息队列、looper。每一个Activity会默认创建一个消息队列和一个looper。主线程中创建一个Handler,Looper一直等待取出消息队列中的消息。子线程对myCount进行++操作,并将myCount的值放进消息队列,主线程中的looper发现消息队列中有消息,将其取出,传递给Handler,在handleMessage方法中对消息进行处理,并将数据显示在UI上。
使用AsyncTask
为了更方便的在子线程中对UI进行操作,android封装了一个asyncTask来实现。
AsyncTask是一个抽象类,我们使用时,要创建一个子类来继承它,继承时我们可以为AsyncTask类指定三个泛型参数。
Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
Progress:后台执行任务时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
Result:当任务执行完毕后,需要对结果进行返回时,这里指定的泛型作为返回值类型。
比如这样写:
在这里插入图片描述
第一个参数:指定为void,表示在执行AsyncTask的时候不需要传入参数给后台任务。
第二个参数:指定为Integer,表示用整型数据来作为进度显示单位。
第三个参数:指定为Boolean,表示用布尔类型来反馈结果。
为了完成对任务的定制,我们还需要重写4个方法。这里以模拟下载任务,显示进度条,下载完成后,关闭进度条并提示下载完成为例。
代码如下:
在这里插入图片描述
在这个DownloadTask中,我们在doInBackground()方法里去执行具体的下载任务,这个方法的代码都是在子线程中运行的,因而不会影响主线程的运行。我们这里虚构了一个download()方法,用于计算当前的下载进度并返回,我们假设这个方法已经存在。在得到当前下载进度后,我们调用publishProgress()方法并将当前的下载进度传进来,很快,onProgress()方法就会被调用,就可以进行UI操作,显示下载进度了。
当下载完成后,doBackground()方法会返回一个布尔型变量,很快,onPostExecute()方法就会被调用,这个方法也是在主线程中运行的。然后我们会根据下载结果弹出相应Toast提示,从而完成DownloadTask任务。
启动方式如下:
在这里插入图片描述
总结一下:
doInBackground()方法在子线程中运行,因此具体的下载任务在这个方法里写。
onPreExecute方法在主线程中运行,在执行后台任务前调用,用于进行一些界面的初始化操作,例如显示一个进度条的对话框。
onProgressUpdate方法在主线程中运行,用于接受后台传来的进度值,利用这个进度值对界面元素进行相应更新。
onPostExecute方法在主线程中运行,用于接收后台任务执行完毕的返回结果,利用返回结果进行一些UI操作,比如提示任务执行结果、关闭进度条对话框等。

注:runOnUiThread()是一个异步消息处理机制的接口封装,表面看起来用法更为简单,但其实背后实现原理是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叮叮当当0543

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值