异步消息处理机制之AsyncTask

一、什么是AsyncTask

在之前的文章异步消息处理机制学习笔记中有简单介绍Android的异步消息处理机制,但实际运用上,Android提供了更加好用的工具——AsyncTask。AsyncTask背后的实现原理也是基于异步消息处理机制的,只是Android帮我们做了很好的封装而已,其主要功能还是完成了子线程和主线程之间的消息传递。

二、AsyncTask的基本用法

1.AsyncTask是一个抽象类,因此使用AsyncTask的时候需要继承它,同时要为AsyncTask指定三个泛型参数:

Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用

Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用该泛型作为进度单位

Result:当任务执行完毕后,如果需要对结果进行返回,则使用该泛型作为返回值类型

2.除了指定上述的三个泛型之外,我们通常还要重写下面的四个方法:

onPreExecute():

该方法会在后台任务开始执行之前调用,通常用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

doInBackground(Params...):

该方法接收的参数即为我们指定的第一个泛型,其返回值即为我们指定的第三个泛型。这个方法中所有的代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。如果AsyncTask的第三个泛型指定的是Void,那么就可以不返回任务执行结果。如果在该方法体内,需要反馈任务进度,则可以调用publishProgress(Progress...)方法来完成。

3.onProgressUpdate(Progress...):

当我们在后台任务中调用了publishProgress(Progress...)方法后,该方法便很快被调用。它所接收的参数就是publishProgress(Progress...)方法的传参,也是AsyncTask的第二个泛型。通常在这个方法中对UI进行操作更新。

4.onPostExecute(Result):

该方法接收的参数即为我们指定的第三个泛型。当后台任务执行完毕并通过return语句进行返回时,该方法便很快被调用。通常在这个方法中提醒任务执行结果以及关闭进度条对话框等。

三、AsyncTask的简单运用

示例场景:这里我创建了一个下载任务,通过AsyncTask在后台执行下载任务,然后切换的主线程更新UI上的下载进度。

1.创建一个下载任务类DownloadTask去继承AsyncTask并指定它的三个泛型

/**
 * 类描述:下载功能类
 * 第一个泛型 String 表示在执行AsyncTask的时候需要传入一个字符串参数给后台任务
 * 第二个泛型 Integer 表示用整型数据来作为进度显示单位
 * 第三个泛型 Integer 表示用整型数据来反馈执行结果
 */
public class DownloadTask extends AsyncTask<String,Integer,Integer> 

2.在DownloadTask的构造器中传入一个下载监听器

public DownloadTask(DownloadListener downloadListener){
    this.downloadListener = downloadListener;
}

下载监听器DownloadListener是一个自定义的接口,通过它去监听下载过程中的各个状态,从而回调其中相应的方法

public interface DownloadListener {
    void onProgress(int progress);
    void onSuccess();
    void onFailed();
    void onPaused();
    void onCanceled();
}

3.重写doInBackground()方法实现后台下载功能

@Override
protected Integer doInBackground(String... strings) {
    RandomAccessFile savedFile = null;
    InputStream is = null;
    File file = null;
    //记录已下载的文件长度
    long downloadedFileLength = 0;
    //需要下载的文件总长度
    long contentLength = 0;
    try {
        //获取下载的URL地址
        String downloadUrl = strings[0];
        //获取下载文件名(例如:/example)
        String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
        //获取SD卡Download路径
        String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
        //下载文件路径
        file = new File(directory+fileName);
        //判断需要下载的文件是否已经存在
        if(file.exists()){
            downloadedFileLength = file.length();
        }
        //获取需要下载的文件总长度
        contentLength = getContentLength(downloadUrl);
        if(contentLength == 0){
            return TYPE_FAILED;
        } else if (contentLength == downloadedFileLength){
            return TYPE_SUCCESS;
        }
        //请求网络下载文件
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                //断点下载,指定从哪个字节开始下载
                .addHeader("RANGE","bytes="+downloadedFileLength+"-")
                .url(downloadUrl)
                .build();
        Response response = client.newCall(request).execute();
        if(response!=null){
            is = response.body().byteStream();
            savedFile = new RandomAccessFile(file,"rw");
            //跳过已经下载的字节
            savedFile.seek(downloadedFileLength);
            //io流读写
            byte[] b = new byte[1024];
            int total = 0;
            int len;
            while((len = is.read(b)) != -1){
                if(isCanceled){
                    return TYPE_CANCLED;
                }else if(isPaused){
                    return TYPE_PAUSED;
                }else{
                    total += len;
                    savedFile.write(b,0,len);
                    //计算已经下载的百分比
                    int progress = (int) ((total+downloadedFileLength)*100/contentLength);
                    publishProgress(progress);
                }
            }
            //下载完成
            response.body().close();
            return TYPE_SUCCESS;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        try {
            if(is!=null){
                is.close();
            }
            if(savedFile!=null){
                savedFile.close();
            }
            if(isCanceled && file != null){
                file.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return TYPE_FAILED;
}

PS:这个方法中用到的TYPE值是静态常量,用来反馈下载任务的执行结果

//四个整型常量表示下载状态:成功,失败,暂停,取消
public static final int TYPE_SUCCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PAUSED = 2;
public static final int TYPE_CANCLED = 3;

这个方法中用到的isPaused和isCanceled是两个flag,用于标识当前下载状态

private boolean isCanceled = false;
private boolean isPaused = false;

这个方法还调用了一个自定义方法,用于获取待下载文件的大小

private long getContentLength(String downloadUrl) throws IOException {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
            .url(downloadUrl)
            .build();
    Response response = client.newCall(request).execute();
    if(request != null && response.isSuccessful()){
        long contentLength = response.body().contentLength();
        response.close();
        return contentLength;
    }
    return 0;
}

4.重写onProgressUpdate()方法更新下载进度

@Override
protected void onProgressUpdate(Integer... values) {
    int progress = values[0];
    //如果进度更新
    if(progress>lastProgress){
        //回调监听器onProgress方法更新进度
        downloadListener.onProgress(progress);
        lastProgress = progress;
    }
}

5.重写onPostExecute()方法通知下载结果

@Override
protected void onPostExecute(Integer integer) {
    switch (integer){
        case TYPE_SUCCESS:
            downloadListener.onSuccess();
            break;
        case TYPE_FAILED:
            downloadListener.onFailed();
            break;
        case TYPE_PAUSED:
            downloadListener.onPaused();
            break;
        case TYPE_CANCLED:
            downloadListener.onCanceled();
            break;
        default:
            break;
    }
}

6.在服务中启动DownloadTask

public void startDownload(String url){
    //当前没有下载任务时才能开始下载
    if(downloadTask == null){
        downloadUrl = url;
        downloadTask = new DownloadTask(downloadListener);
        downloadTask.execute(downloadUrl);
    }
}

猜你喜欢

转载自blog.csdn.net/Ein3614/article/details/81913104