AsyncTask 使用及封装实践

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的基本使用,不知道你有没有发现,其实代码耦合性是挺高的,

- 我们直接在 onProgressUpdata(),onPostExecute()方法里面更新我们的界面,即我们的AsyncTask访问了我们Activity里面的控件,那如果我们修改了Activity的控件,我们岂不是又要去阅读AsyncTask的代码,去做相应的修改。

- 下一次我们如果要下载别的东西,按照我们前面的代码,我们又要重新复制一份,这样无疑是做了很多重复的工作。

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

  1. 在Activity里面定义静态方法

  2. 把需要访问的View对象通过构造函数传递进来

  3. 采用接口回调机制

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

  • 第一种方法定义静态方法,那View对象也必须定义为static变量,这static变量的级别比较高,不易被垃圾回收机制回收,易发生没存泄露。

  • 第二种方法,把需要访问的View对象通过构造函数传递进来。如果需要访问的对象少的话,勉强可以接受,如果多的话,那岂不是要定义很多成员变量。不过最致命的还算是代码耦合性太高了。还不如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使用的注意事项


  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.

  • The task instance must be created on the UI thread.(AsyncTask必须在UI 线程里面初始化

  • execute(Params…) must be invoked on the UI thread.

  • Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) manually.(不要手动地调用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 这些方法)

  • The task can be executed only once (an exception will be thrown if a second execution is attempted.) (Task任务只能被执行一次,否则会抛出异常)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-Atx06gPk-1715501665811)]

[外链图片转存中…(img-F1sez8Yl-1715501665813)]

[外链图片转存中…(img-9wmuoQcL-1715501665814)]

[外链图片转存中…(img-yW1K94tn-1715501665815)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值