综述

在Android中,我们需要进行一些耗时的操作,会将这个操作放在子线程中进行。在子线程操作完成以后我们可以通过Handler进行发送消息,通知UI进行一些更新操作(具体使用及其原理可以查看Android的消息机制——Handler的工作过程这篇文章)。当然为了简化我们的操作,在Android1.5以后为我们提供了AsyncTask类,它能够将子线程处理完成后的结果返回到UI线程中,之后我们便可以根据这些结果进行一列的UI操作了。
AsyncTask的使用方法
实际上AsyncTask内部也就是对Handler和线程池进行了一次封装。它是一个轻量级的异步任务类,它的后台任务在线程池中进行。之后我们可以将任务执行的结果传递给主线程,这时候我们就可以在主线程中操作UI了。
AsyncTask他是一个抽象的泛型类,所以我们创建一个子类,来实现AsyncTask中的抽象方法。AsyncTask中提供了三个泛型参数,下面我们就来看一下这三个泛型参数.
1. Params:在执行AsyncTask时所传递的参数,该参数在后台线程中使用。
2. Progress:后台任务执行进度的类型
3. Result:后台任务执行完成后返回的结果类型。
对于以上三个泛型参数我们不需要使用的时候,可以使用Void来代替。与Activity生命周期类似,在AsyncTask中也为我们提供了一些方法,我们通过重写这几个方法来完成整个异步任务。我们主要使用的方法有一下四个:
1. onPreExecute():该方法在异步任务工作之前执行,主要用于一些参数或者UI的初始化操作。
2. doInBackground(Params… params):该方法在线程池中执行,params参数表示异步任务时输入的参数。在这个方法中我们通过publishProgress来通知任务进度。
3. onProgressUpdate(Progress… values):当后台任务的进度发生改变的时候会调用该方法,我们可以再这个方法中进行UI的进度展示。values参数表示任务进度。
4. postResult(Result result):在异步任务完成之后执行,result参数为异步任务执行完以后所返回的结果。
在上面四个方法中只有doInBackground在子线程中运行,其余都三个方法都是在主线程中运行的。其中的…表示参数的数量不定,是一种数组类型的参数。
下面我们就来写一个例子来看一下AsyncTask的用法,在这里我们就一个下载的功能,从网络上下载两个文件。我们先来看一下效果演示。
效果演示
代码分析
由于我们做的下载任务,首先我们就得添加访问网络权限以及一些sd卡相关的权限。
<!-- 在SD卡中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 向SD卡写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 授权访问网络 --> <uses-permission android:name="android.permission.INTERNET"/>
下面我们来看一下Activity代码。
package com.example.ljd.asynctask;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private DownloadAsyncTask mDownloadAsyncTask;
private Button mButton;
private String[] path = {
"http://msoftdl.360.cn/mobilesafe/shouji360/360safesis/360MobileSafe_6.2.3.1060.apk",
"http://dlsw.baidu.com/sw-search-sp/soft/7b/33461/freeime.1406862029.exe",
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.button);
mButton.setOnClickListener(this);
}
@Override
protected void onDestroy() {
if (mDownloadAsyncTask != null){
mDownloadAsyncTask.cancel(true);
}
super.onDestroy();
}
@Override
public void onClick(View v) {
mDownloadAsyncTask = new DownloadAsyncTask();
mDownloadAsyncTask.execute(path);
}
class DownloadAsyncTask extends AsyncTask<String,Integer,Boolean>{
private ProgressDialog mPBar;
private int fileSize; //下载的文件大小
@Override
protected void onPreExecute() {
super.onPreExecute();
mPBar = new ProgressDialog(MainActivity.this);
mPBar.setProgressNumberFormat("%1d KB/%2d KB");
mPBar.setTitle("下载");
mPBar.setMessage("正在下载,请稍后...");
mPBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mPBar.setCancelable(false);
mPBar.show();
}
@Override
protected Boolean doInBackground(String... params) {
//下载图片
for (int i=0;i<params.length;i++){
try{
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
URL url = new URL(params[i]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时时间
conn.setConnectTimeout(5000);
//获取下载文件的大小
fileSize = conn.getContentLength();
InputStream is = conn.getInputStream();
//获取文件名称
String fileName = path[i].substring(path[i].lastIndexOf("/") + 1);
File file = new File(Environment.getExternalStorageDirectory(), fileName);
FileOutputStream fos = new FileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len ;
int total = 0;
while((len =bis.read(buffer))!=-1){
fos.write(buffer, 0, len);
total += len;
publishProgress(total);
fos.flush();
}
fos.close();
bis.close();
is.close();
}
else{
return false;
}
}catch (IOException e){
e.printStackTrace();
return false;
}
}
return true;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
mPBar.dismiss();
if (aBoolean){
Toast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,"下载失败",Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mPBar.setMax(fileSize / 1024);
mPBar.setProgress(values[0]/1024);
}
}
}
在以上代码中有几点我们需要注意一下。
1. AsyncTask中的execute方法必须在主线程中执行。
2. 每个AsyncTask对象只能执行一次execute方法。
3. 当我们的Activity销毁的时候需要进行取消操作,其中boolean型的参数mayInterruptIfRunning表示是否中断后台任务。
最后的布局代码就很简单了,只有一个Button而已。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.ljd.asynctask.MainActivity">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="download"/>
</LinearLayout>
这里还有一点需要说明一下,由于在不同的Android版本中对AsyncTask进行了多次修改,所以当我们通过多个AsyncTask对象执行多次execute方法的时候,它们执行顺序是串行还是并行根据系统不同的版本而出现差异,这里就不再具体分析。
AsyncTask源码分析
在这里我们采用Android6.0中的AsyncTask源码进行分析,对于不同系统的AsyncTask代码会有一定的差异。当创建一个AsyncTask对象以后我们便可以通过execute方法来执行整个任务。那就在这里首先看一下execute方法中的代码。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看到这个execute方法中的代码是如此的简单,只是执行了executeOnExecutor方法并返回AsyncTask对象。下面我们就来看一下executeOnExecutor这个方法。
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在这个方法内我们首先对AsyncTask所执行的状态进行判断。如果AsyncTask正在执行任务或者任务已经知己完成就会给我们抛出异常,也就解释上面所说的每个AsyncTask对象只能执行一次execute方法。紧接着将当前任务状态修改为正在运行以后便开始执行onPreExecute方法。这也说明了在上面我们重写的四个方法中onPreExecute方法最先指向的。下面我们在看一下mWorker和mFuture这两个全局变量。
mFuture是FutureTask对象,而mWorker是WorkerRunnable对象,WorkerRunnable是AsyncTask中的一个实现Callable接口的抽象内部类,在WorkerRunnable中只定义了一个Params[]。
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
......
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mFuture和mWorker是在AsyncTask的构造方法中初始化的。我们看一下AsyncTask的构造方法。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
在这里首先创建一个WorkerRunnable对象mWorker,并且实现了Callable接口的call方法,对于这个call方面里面的内容在后面会进行详细说明。然后我们再通过mWorker,创建一个FutureTask对象mFuture,并且重写里面的done方法,当我们调用AsyncTask里面的cancel方法时,在FutureTask中会调用这个done方法。在这里介绍完mWorker和mFuture后我们再回过头来看executeOnExecutor方法。在这里通过我们传入的params参数初始化了mWorker中的mParams。而下面的exec则是在execute里面传入的sDefaultExecutor,并且执行sDefaultExecutor的execute方法。下面我们再来看一下这个sDefaultExecutor对象。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
......
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
在这里首先整体介绍一下这段代码。这段代码的主要功能就是创建了两个线程池SerialExecutor和THREAD_POOL_EXECUTOR。SerialExecutor线程池用于对任务的排队,而THREAD_POOL_EXECUTOR则是用来执行任务。而sDefaultExecutor就是SerialExecutor线程池。
我们进入SerialExecutor代码中看一下里面的内容。SerialExecutor中的execute方法内的参数Runnable就是我们传入的mFuture。在execute方法中创建了一个Runnable对象,并将该队列插入对象尾部。这个时候如果是第一次执行任务。mActive必然为null,这时候便调用scheduleNext方法从队列中取出Runnable对象,并且通过THREAD_POOL_EXECUTOR线程池执行任务。我们可以看到在队列中的Runnable的run方法中首先执行mFuture中的run方法,执行完之后又会调用scheduleNext方法,从队列中取出Runnable执行,直到所有的Runnable执行完为止。下面就来看一下在线城池中是如何执行这个Runnable的。从上面代码可以看出,在线城池中执行Runnable其实最核心的部分还是执行mFuture的run方法。那就来看一下这个mFuture中的run方法。
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在这个方法中的callable对象就是我们AsyncTask中的mWorker对象。在这里面也正是执行mWorker中的call方法来完成一些耗时任务,于是我们就能想到我重写的doInBackground应该就在这个call方法中执行了。现在再回到AsyncTask的构造方法中看一下这个mWorker中的call方法。
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
这里我们很清楚的看到它执行我们重写的doInBackground方法,并且将返回的结果传到postResult方法中。下面就在看一下这个postResult方法。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
对与这个postResult方法里面也就是在我们子线程的任务处理完成之后通过一个Handler对象将Message发送到主线程中,并且交由主线程处理。AsyncTaskResult对象中存放的是子线程返回的结果以及当前AsyncTask对象。下面再看一下这和Handler中所处理了哪些事情。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
可以看到在这个Handler的handleMessage中会接受到两种Message。在MESSAGE_POST_PROGRESS这个消息中主要是通过publishProgress方法将子线程执行的进度发送到主线程中并且通过onProgressUpdate方法来更新进度条的显示。在MESSAGE_POST_RESULT这个消息中通过当前的AsyncTask对象调用了finish方法,那么就来看一下这个finish方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这时候就可以看出若是我们取消了AsyncTask的话就不在执行onPostExecute方法,而是执行onCancelled方法,所以我们可以通过重写onCancelled方法来执行取消时我们需要处理的一些操作。当然若是AsyncTask没有被取消,这时候就回执行onPostExecute方法。到这里整个AsyncTask任务也就完成了。
总结
在上面的SerialExecutor线程池中可以看出,当有多个异步任务同时执行的时候,它们执行的顺序是串行的,会按照任务创建的先后顺序进行一次执行。如果我们希望多个任务并发执行则可以通过AsyncTask中的setDefaultExecutor方法将线程池设为THREAD_POOL_EXECUTOR即可。
对于AsyncTask的在不同版本之间的差异不得不提一下。在Android1.6,AsyncTask采用的是串行执行任务,在Android1.6的时候采用线程池处理并行任务,而在3.0以后才通过SerialExecutor线程池串行处理任务。在Android4.1之前AsyncTask类必须在主线程中,但是在之后的版本中就被系统自动完成。而在Android5.0的版本中会在ActivityThread的main方法中执行AsyncTask的init方法,而在Android6.0中又将init方法删除。所以在使用这个AsyncTask的时候若是适配更多的系统的版本的话,使用的时候就要注意了。
源码下载:https://github.com/lijiangdong/asynctask-example
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android
# AsyncTask
# Android AsyncTask实现异步处理任务的方法详解
# Android使用AsyncTask下载图片并显示进度条功能
# Android AsyncTask的优缺点详解
# android使用AsyncTask实现多线程下载实例
# Android中使用AsyncTask做下载进度条实例代码
# Android AsyncTask用法巧用实例代码
# Android中使用AsyncTask实现文件下载以及进度更新提示
# Android AsyncTask详解及使用方法
# 看一下
# 在这里
# 重写
# 在这个
# 是在
# 就来
# 也就
# 多个
# 创建一个
# 而在
# 我们可以
# 可以看到
# 的是
# 当我们
# 在上面
# 这时候
# 方法来
# 就在
# 那就
# 则是
相关文章:
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
网站微信制作软件,如何制作微信链接?
怀化网站制作公司,怀化新生儿上户网上办理流程?
如何配置WinSCP新建站点的密钥验证步骤?
太平洋网站制作公司,网络用语太平洋是什么意思?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
如何通过主机屋免费建站教程十分钟搭建网站?
如何选择域名并搭建高效网站?
如何生成腾讯云建站专用兑换码?
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
官网自助建站平台指南:在线制作、快速建站与模板选择全解析
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
TestNG的testng.xml配置文件怎么写
购物网站制作公司有哪些,哪个购物网站比较好?
如何快速搭建安全的FTP站点?
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
北京网站制作的公司有哪些,北京白云观官方网站?
如何零基础在云服务器搭建WordPress站点?
怎么用手机制作网站链接,dw怎么把手机适应页面变成网页?
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
如何获取上海专业网站定制建站电话?
如何用PHP工具快速搭建高效网站?
建站之星ASP如何实现CMS高效搭建与安全管理?
,购物网站怎么盈利呢?
如何规划企业建站流程的关键步骤?
制作网站建设的公司有哪些,网站建设比较好的公司都有哪些?
如何在IIS7上新建站点并设置安全权限?
清除minerd进程的简单方法
潍坊网站制作公司有哪些,潍坊哪家招聘网站好?
,网页ppt怎么弄成自己的ppt?
如何快速打造个性化非模板自助建站?
如何在IIS7中新建站点?详细步骤解析
Android自定义控件实现温度旋转按钮效果
如何高效利用200m空间完成建站?
PHP 500报错的快速解决方法
如何在万网开始建站?分步指南解析
建站之星如何助力企业快速打造五合一网站?
如何通过.red域名打造高辨识度品牌网站?
保定网站制作方案定制,保定招聘的渠道有哪些?找工作的人一般都去哪里看招聘信息?
如何在阿里云完成域名注册与建站?
html制作网站的步骤有哪些,iapp如何添加网页?
微信小程序 五星评分(包括半颗星评分)实例代码
c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】
建站之星安装后界面空白如何解决?
北京的网站制作公司有哪些,哪个视频网站最好?
黑客如何通过漏洞一步步攻陷网站服务器?
如何快速生成专业多端适配建站电话?
建站之星会员如何解锁更多建站功能?
如何在服务器上三步完成建站并提升流量?
*请认真填写需求信息,我们会在24小时内与您取得联系。