Android线程(2)——AsyncTask

       在上一篇中,我们简单的介绍了HandlerThread,我们了解到其是一种特殊的与Handler结合使用的一种Thread。今天我们来学习一下Android线程中的另一个知识点AsyncTask,这个词直译过来是异步任务的意思,首先先看一下这个类的官方介绍


AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called begin, doInBackground, processProgress and end.


大概意思是:异步任务能够让我们简单准确的使用UI线程,它可以运行在后台并把最终的运行结果回传给UI线程,整个过程不需要借助(管理)其他的线程或者Handler。

异步任务运行在后台,运行结果回传给UI线程。一个异步任务可以通过三个名为Params,Progress和Result的泛型以及begin,doInBackground,processProgress以及end四步来定义。


到这里,我们对其有一个基本的了解了,总结一句话:其创建的异步任务在后台线程执行,最终的执行结果会回传给UI线程。

 

下面只需要看其如何实现这个目标的即可。

AsyncTask must be subclassed to be used. The subclass will override at least one method (doInBackground(Params...)), and most often will override a second one (onPostExecute(Result).)

Here is an example of subclassing:

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }
 Once created, a task is executed very simply:

 new DownloadFilesTask().execute(url1, url2, url3);
 AsyncTask's generic types
The three types used by an asynchronous task are the following:

Params, the type of the parameters sent to the task upon execution. 
Progress, the type of the progress units published during the background computation. 
Result, the type of the result of the background computation. 
Not all types are always used by am asynchronous task. To mark a type as unused, simply use the type Void:

 private class MyTask extends AsyncTask

AsyncTask是一个抽象类,所以其必须被子类化了之后才能实例化使用,通常其子类必须重写doInBackground(),通常还需要重写onPostExecute()。关于三个泛型参数的说明也很详细:

  • Params:发送给即将要执行的异步任务的参数
  • Progress:执行的进度,后台运行
  • Result:后台任务的运行结果

如果其中有的参数没有用到,直接用Void代替即可

上面说到执行一个异步任务通常有四步,下面就来看看这四步的具体实现:

The 4 steps
 When an asynchronous task is executed, the task goes through 4 steps:
 
     onPreExecute(), invoked on the UI thread immediately after the task
     is executed. This step is normally used to setup the task, for instance by
     showing a progress bar in the user interface.

     doInBackground(Params...), invoked on the background thread
     immediately after onPreExecute() finishes executing. This step is used
     to perform background computation that can take a long time. The parameters
     of the asynchronous task are passed to this step. The result of the computation must
     be returned by this step and will be passed back to the last step. This step
     can also use publishProgress(Progress...) to publish one or more units
     of progress. These values are published on the UI thread, in the
     onProgressUpdate(Progress...) step.

     onProgressUpdate(Progress...), invoked on the UI thread after a
     call to publishProgress(Progress...). The timing of the execution is
     undefined. This method is used to display any form of progress in the user
     interface while the background computation is still executing. For instance,
     it can be used to animate a progress bar or show logs in a text field.

     onPostExecute(Result), invoked on the UI thread after the background
     computation finishes. The result of the background computation is passed to
     this step as a parameter.
  • onPreExecute():运行在UI线程,通常用于在异步任务执行前触发界面进度view显示
  • doInBackground():运行在后台线程,主要用于计算任务执行进度,可以通过publishProgress()(本质还是调用onProgressUpdate())实现进度在UI界面的实时显示
  • onProgressUpdate():当publishProgress()被调用的时候其会执行(在UI线程)
  • onPostExecute():当异步任务执行完成后,计算结果会通过result参数回传过来,因为其运行在UI线程,所以运行结果就被传递到了UI线程。

 

        下面就来结合具体的实例来看一下使用,下面就以API给的例子扩展一下,来实现通过异步任务实现下载并在界面实时显示进度的这样一个效果。主要代码如下:

自定义异步任务代码:

package aoto.com.operationasynctask;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

/**
 * author:why
 * created on: 2019/4/15 19:47
 * description:
 */
public class DownloadFilesTask extends AsyncTask<Integer, Integer, Long> {

    private static final String TAG = "DownloadFilesTaskWhy";
    private ProgressBar progressBar;
    private TextView textView;
    private Context context;
    int progress=0;

    public DownloadFilesTask(Context context,ProgressBar progressBar,TextView textView) {
        this.progressBar = progressBar;
        this.context=context;
        this.textView=textView;
    }

    @Override
    protected Long doInBackground(Integer... voids) {

        while(progress<50){
            publishProgress((progress+1));
            try {
                Thread.sleep(100);
                progress++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return Long.valueOf(100);
    }


    @Override
    protected void onProgressUpdate(Integer... values) {
        Log.e(TAG, "onProgressUpdate: " );
        progressBar.setProgress(values[0]);
        textView.setText("已下载:"+values[0]*2+"%");
    }

    @Override
    protected void onPostExecute(Long aLong) {
        Log.e(TAG, "onPostExecute: " );
        Toast.makeText(context,"下载完成",Toast.LENGTH_SHORT).show();
    }

}

测试代码:

package aoto.com.operationasynctask;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * @author why
 * @date 2019-4-15 19:43:24
 */
public class MainActivity extends AppCompatActivity {

    DownloadFilesTask task;
    ProgressBar progressBar;
    TextView progressText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        progressBar=findViewById(R.id.download_progress_graphic);
        progressText=findViewById(R.id.download_progress_digital);

        task=new DownloadFilesTask(this,progressBar,progressText);
    }


    /**
     * 模拟执行下载文件
     * @param view
     */
    public void download(View view){
        task.execute();
    }

}

Android线程(2)——AsyncTask

AsyncTask本身维护着一个线程池,大小在2-4之间。有一个长度为128的任务队列...里面的很多内容可以之间在该类中查看到

 // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work


 private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

........

具体的使用还是结合文档较好。