Android知识Android技术知识Android开发

AsyncTask 使用及封装实践

2017-02-28  本文已影响728人  程序员徐公

前言

IntentService使用及源码分析

HandlerThread源码分析

AsyncTask使用及封装实践

AsyncTask源码分析

这篇博客主要是讲解AsyncTask的使用及封装实践,对于新手们还是有很大的参考意义的,尤其是AsyncTask的封装实践这部分。对于老鸟们,你们可以跳过了。同时需要声明的一点是,下面下载的例子只是进行简单的下载而已,并没有支持断点续传下载。需要的话请自行到github上面找相应的库,因为这并不是本篇博客的重点。

这篇博客主要讲解以下问题:


AsyncTask的使用例子

简介

AsyncTask ,异步任务。没错,就想字面上理解的那样。它允许我们在子线程执行耗时任务,在UI 线程更新操作(如更新进度条等)。简单来说,就是帮我们做好了子线程与UI 线程的通讯,我们只需要调用响应的方法实现即可。底层是用Handler消息机制实现的。

在Android开发中,我们经常需要下载各种东西,为了给用户较好的体验,我们经常需要显示下载进度。今天我们用以这个为例子,来教大家怎样使用AsyncTak。当然,github上面有很多开源库,实现断点下载,文件重命名等。不过这些不是本篇博客的重点。

效果图

AsyncTask的主要几个方法

在task 任务开始执行的时候调用,在doInBackground(Params... params)方法之前调用,在主线程中执行

主要用来执行耗时操作,在子线程中执行,Params为我们参数的类型。而Result这个泛型,是我们返回的类型(可以是Integer,Long,String等等类型,只要不是八种基本类型就OK),同时 Result 的类型将作为 onPostExecute(Result result)的参数。

Runs on the UI thread after publishProgress(Progress...) is invoked. 当我们调用 publishProgress()方法的时候,会调用 onProgressUpdate()这个方法

在Task 任务取消的时候会调用

Executes the task with the specified parameters.当我们调用这个方法的时候,会执行任务

在指定的线程池里面执行Task

需要注意的是,Params,Progress,Result 并不是一种特定的类型,它其实是泛型,它支持除了八种基本类型之外的类型,跟普通的泛型一样。

AsyncTask使用的几个步骤

这里我们以下载一个apk为例讲解

  1. 写一个类继承AsyncTask,并传入Params,Progress,Result 。三个参数的类型。

比如我们传入的 Params,Progress,Result 的参数的类型分别为 Void, FileInfo, FileInfo,那我们可以这样写。

private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo>{
     
 }

那Void, FileInfo, FileInfo,这几个参数的类型在哪里体现出来呢?

请看下面注释

private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo> {

   ---
  
   // 方法参数的类型为Void,跟我们传入的Void一致,返回类型为 FileInfo ,跟我们传入Result的类型FileInfo一致
    @Override
    protected FileInfo doInBackground(Void... params) {
        
    }

   // 方法参数类型为FileInfo,跟我们传入Progress的类型FileInfo一致
    @Override
    protected void onProgressUpdate(FileInfo... values) {
       
    }


   // 方法参数FileInfo,跟我们传入Result的类型FileInfo一致
    @Override
    protected void onPostExecute(FileInfo fileInfo) {
       

    }

 
}
  1. 如果我们更新进度的话,需要重写 onProgressUpdate()方法,并在doInBackground()方法里面调用publishProgress()方法
protected FileInfo doInBackground(Void... params) {
   
   publishProgress(fileInfo);

  
}

@Override
protected void onProgressUpdate(FileInfo... values) {
    super.onProgressUpdate(values);
    refreshProgress(values[0]);
}


  1. 当我们调用execute(Params... params) 或者 executeOnExecutor(Executor exec, Params... params) 方法的时候,Task将被防盗相应的 Executor 执行。
 MyDownloadTask myDownloadTask = new MyDownloadTask(mDownloadUrl, mDstPath);
 myDownloadTask.execute();

完整的Task代码如下

private class MyDownloadTask extends AsyncTask<Void, FileInfo, FileInfo> {

    String mDownLoadUrl;
    String mDstPath;

    public MyDownloadTask(String downloadUrl, String dstPath) {
        this.mDownLoadUrl = downloadUrl;
        this.mDstPath = dstPath;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        start();
    }

    @Override
    protected FileInfo doInBackground(Void... params) {
        //url字符串,检查网址是否已http:// 开头
        mDownLoadUrl = (mDownLoadUrl.startsWith("http://")) ? mDownLoadUrl : "http://" +
                mDownLoadUrl;
        Log.d(TAG, "doInBackground: mDownLoadUrl=" + mDownLoadUrl);
        Log.d(TAG, "doInBackground: mDstPath=" + mDstPath);
        URL url = null;
        FileInfo fileInfo = null;
        int contentLength = -1;
        int downloadLength = 0;
        OutputStream output = null;
        InputStream istream = null;
        try {
            url = new URL(mDownLoadUrl);
            //打开到url的连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            contentLength = connection.getContentLength();
            Log.i(TAG, "doInBackground: contentLength=" + contentLength);
            //O部分,大体来说就是先检查文件夹是否存在,不存在则创建
            istream = connection.getInputStream();
            String filename = mDownLoadUrl.substring(mDownLoadUrl.lastIndexOf("/") + 1);

            File dir = new File(mDstPath);
            if (!dir.exists()) {
                dir.mkdir();
            }
            File file = new File(mDstPath + filename);
            // 如果存在同名文件,重命名
            if (file.exists()) {
                file = FileUtils.rename(file.getPath());
            }


            output = new FileOutputStream(file);
            byte[] buffer = new byte[1024 * 4];
            int count = 0;
            int len = -1;
            while ((len = istream.read(buffer)) != -1) {
                output.write(buffer, 0, len);
                downloadLength += len;

                if (count == 10) {
                    fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath
                            (), file.getName());
                    publishProgress(fileInfo);
                    count = 0;
                }
                count++;

            }
            //                有可能count还没有走到10
            fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath(), file
                    .getName());
            publishProgress(fileInfo);
            output.flush();
            output.close();
            istream.close();

        } catch (Exception e) {
            e.printStackTrace();
            try {
                IOUtils.close(output);
                IOUtils.close(istream);
            } catch (IOException e1) {
                e1.printStackTrace();
            }

        } finally {
            try {
                IOUtils.close(output);
                IOUtils.close(istream);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        return fileInfo;
    }

    @Override
    protected void onProgressUpdate(FileInfo... values) {
        super.onProgressUpdate(values);
        refreshProgress(values[0]);
    }

    @Override
    protected void onPostExecute(FileInfo fileInfo) {
        super.onPostExecute(fileInfo);
        downloadfinish(fileInfo);

    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

private void start() {
    mTvDownloadText.setText("开始下载");
    mProgressBar.setMax(100);
    mProgressBar.setProgress(0);
}

private void downloadfinish(FileInfo fileInfo) {
    Log.i(TAG, "onPostExecute: 下载完成=" + fileInfo.mPath);
    Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
}

private void refreshProgress(FileInfo value) {
    FileInfo fileInfo = value;
    if (fileInfo != null) {
        mProgressBar.setMax((int) fileInfo.mLength);
        mProgressBar.setProgress((int) fileInfo.mDownloadLength);
        mDownText = fileInfo.mFile.getName() + "下载了" + fileInfo.mDownloadLength + "总长度是" +
                fileInfo.mLength;
        mTvDownloadText.setText(mDownText);
    }
}



AsyncTask的封装使用

前面我们讲完了AsyncTask的基本使用,不知道你有没有发现,其实代码耦合性是挺高的,

说到这样,我相信大多数人的第一感觉就是把AsyncTask提取为外部类,封装起来。是的,确实,我们就是要把AsyncTask提取为外部类。那提取为歪不累之后呢?我们要访问Activity里面的空间,要怎样访问呢?

  1. 在Activity里面定义静态方法
  2. 把需要访问的View对象通过构造函数传递进来
  3. 采用接口回调机制

前面说到的三种方法,是可以做到AsyncTask与外界进行通讯的。但第一第二中方法明显不行。原因如下:

好了,说了这么多,下面我们一起来看怎样使用接口回调机制来进行解耦。

AsyncTask 使用接口回调机制来进行解耦

  1. 使用接口回调机制,首先我们必须有一个接口
public interface DownloadListener {

    void onStart();
    void onProgress(FileInfo fileInfo);
    void onFinish(FileInfo FileInfo);
    void onPaused(FileInfo fileInfo);
    void onCancled();
}
  1. 将DownLoadTask提取为一个外部类,并将需要传递的参数传递进来
public class DownloadTask extends AsyncTask<Void,FileInfo,FileInfo> {

    private  String mDownloadUrl;
    private final String mDstPath;
    private final String mFileName;
    private final DownloadListener mDownloadListener;

    public DownloadTask(String downloadUrl, String dstPath, String fileName, DownloadListener downloadListener){
        mDownloadUrl = downloadUrl;
        mDstPath = dstPath;
        mFileName = fileName;
        mDownloadListener = downloadListener;
    }
}
  1. 在相应的地方调用我们接口的方法
public class DownloadTask extends AsyncTask<Void,FileInfo,FileInfo> {

    ----

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mDownloadListener.onStart();
    }

    @Override
    protected FileInfo doInBackground(Void... params) {
    
       ----
            int len = -1;
            while ((len = istream.read(buffer)) != -1) {
                output.write(buffer, 0, len);
                downloadLength += len;

                if (count == 10) {
                    fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath
                            (), file.getName());
                    publishProgress(fileInfo);
                    count = 0;
                }
                count++;

            }
            //                有可能count还没有走到10
            fileInfo = new FileInfo(contentLength, downloadLength, file, file.getPath(), file
                    .getName());
            publishProgress(fileInfo);
            output.flush();
            output.close();
            istream.close();

    
       
        return fileInfo;
    }

    @Override
    protected void onProgressUpdate(FileInfo... values) {
        super.onProgressUpdate(values);
        mDownloadListener.onProgress(values[0]);
    }

    @Override
    protected void onPostExecute(FileInfo fileInfo) {
        super.onPostExecute(fileInfo);
        mDownloadListener.onFinish(fileInfo);
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
        mDownloadListener.onCancled();
    }
}

使用

以后我们要下载东西,只需要调用下面的方法即可。同时,如果产品再更改需求,比如,从显示一个进度条ProgressDialog对话框,改成显示一个ProgressBar,我们只需要在
onProgress()里面做相应的修改就好了,在也不用去阅读DownloadTask里面的代码呢?减少了代码的耦合性,是不是瞬间感觉世界很美好呢?

mDownloadTask = new DownloadTask(mDownloadUrl, mDstPath, null, new
        DownloadListener() {
    @Override
    public void onStart() {
          start();
    }

    @Override
    public void onProgress(FileInfo fileInfo) {
        refreshProgress(fileInfo);
    }

    @Override
    public void onFinish(FileInfo fileInfo) {
        downloadfinish(fileInfo);
    }

    @Override
    public void onPaused(FileInfo fileInfo) {

    }

    @Override
    public void onCancled() {

    }
});
mDownloadTask.execute();

AsyncTask使用的注意事项


相关知识点推荐:

IntentService使用及源码分析

HandlerThread源码分析

AsyncTask使用及封装实践

AsyncTask源码分析

Demo下载地址

上一篇 下一篇

猜你喜欢

热点阅读