当前位置: 移动技术网 > IT编程>移动开发>Android > Android中实现异步任务机制的AsyncTask方式的使用讲解

Android中实现异步任务机制的AsyncTask方式的使用讲解

2018年09月30日  | 移动技术网IT编程  | 我要评论

手机伦理片,时时彩计划:5877578,嵌入式linux论坛

android中实现异步任务机制有两种方式,handler和asynctask。

handler模式

需要为每一个任务创建一个新的线程,任务完成后通过handler实例向ui线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。关于handler的相关知识,前面也有所介绍,不清楚的朋友们可以参照一下。

为了简化操作,android1.5提供了工具类android.os.asynctask,它使创建异步任务变得更加简单,不再需要编写任务线程和handler实例即可完成相同的任务。

先来看看asynctask的定义:

public abstract class asynctask {

三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.void类型代替。

一个异步任务的执行一般包括以下几个步骤:

1.execute(params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

2.onpreexecute(),在execute(params... params)被调用后立即执行,一般用来在执行后台任务前对ui做一些标记。

3.doinbackground(params... params),在onpreexecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishprogress(progress... values)来更新进度信息。

4.onprogressupdate(progress... values),在调用publishprogress(progress... values)时,此方法被执行,直接将进度信息更新到ui上。

5.onpostexecute(result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到ui组件上。

在使用的时候,有几点需要格外注意:

1.异步任务的实例必须在ui线程中创建。

2.execute(params... params)方法必须在ui线程中调用。

3.不要手动调用onpreexecute(),doinbackground(params... params),onprogressupdate(progress... values),onpostexecute(result result)这几个方法。

4.不能在doinbackground(params... params)中更改ui组件的信息。

5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。

接下来,我们来看看如何使用asynctask执行异步任务操作,我们先建立一个项目,结构如下:

\

结构相对简单一些,让我们先看看mainactivity.java的代码:

package com.scott.async;
 
import java.io.bytearrayoutputstream;
import java.io.inputstream;
 
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.httpclient;
import org.apache.http.client.methods.httpget;
import org.apache.http.impl.client.defaulthttpclient;
 
import android.app.activity;
import android.os.asynctask;
import android.os.bundle;
import android.util.log;
import android.view.view;
import android.widget.button;
import android.widget.progressbar;
import android.widget.textview;
 
public class mainactivity extends activity {
 
 private static final string tag = "async_task";
 
 private button execute;
 private button cancel;
 private progressbar progressbar;
 private textview textview;
 
 private mytask mtask;
 
 @override
 public void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.main);
 
  execute = (button) findviewbyid(r.id.execute);
  execute.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
 //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
 mtask = new mytask();
 mtask.execute("https://www.baidu.com");
 
 execute.setenabled(false);
 cancel.setenabled(true);
}
  });
  cancel = (button) findviewbyid(r.id.cancel);
  cancel.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
 //取消一个正在执行的任务,oncancelled方法将会被调用
 mtask.cancel(true);
}
  });
  progressbar = (progressbar) findviewbyid(r.id.progress_bar);
  textview = (textview) findviewbyid(r.id.text_view);
 
 }
 
 private class mytask extends asynctask {
  //onpreexecute方法用于在执行后台任务前做一些ui操作
  @override
  protected void onpreexecute() {
log.i(tag, "onpreexecute() called");
textview.settext("loading...");
  }
 
  //doinbackground方法内部执行后台任务,不可在此方法内修改ui
  @override
  protected string doinbackground(string... params) {
log.i(tag, "doinbackground(params... params) called");
try {
 httpclient client = new defaulthttpclient();
 httpget get = new httpget(params[0]);
 httpresponse response = client.execute(get);
 if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
  httpentity entity = response.getentity();
  inputstream is = entity.getcontent();
  long total = entity.getcontentlength();
  bytearrayoutputstream baos = new bytearrayoutputstream();
  byte[] buf = new byte[1024];
  int count = 0;
  int length = -1;
  while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
//调用publishprogress公布进度,最后onprogressupdate方法将被执行
publishprogress((int) ((count / (float) total) * 100));
//为了演示进度,休眠500毫秒
thread.sleep(500);
  }
  return new string(baos.tobytearray(), "gb2312");
 }
} catch (exception e) {
 log.e(tag, e.getmessage());
}
return null;
  }
 
  //onprogressupdate方法用于更新进度信息
  @override
  protected void onprogressupdate(integer... progresses) {
log.i(tag, "onprogressupdate(progress... progresses) called");
progressbar.setprogress(progresses[0]);
textview.settext("loading..." + progresses[0] + "%");
  }
 
  //onpostexecute方法用于在执行完后台任务后更新ui,显示结果
  @override
  protected void onpostexecute(string result) {
log.i(tag, "onpostexecute(result result) called");
textview.settext(result);
 
execute.setenabled(true);
cancel.setenabled(false);
  }
 
  //oncancelled方法用于在取消执行中的任务时更改ui
  @override
  protected void oncancelled() {
log.i(tag, "oncancelled() called");
textview.settext("cancelled");
progressbar.setprogress(0);
 
execute.setenabled(true);
cancel.setenabled(false);
  }
 }
}

布局文件main.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <button
    	android:id="@+id/execute"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:text="execute"/>
    <button
    	android:id="@+id/cancel"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:enabled="false"
    	android:text="cancel"/>
	<progressbar 
	    android:id="@+id/progress_bar" 
	    android:layout_width="fill_parent" 
	    android:layout_height="wrap_content" 
	    android:progress="0"
	    android:max="100"
	    style="?android:attr/progressbarstylehorizontal"/>
	<scrollview
	    android:layout_width="fill_parent" 
	    android:layout_height="wrap_content">
		<textview
		    android:id="@+id/text_view"
		    android:layout_width="fill_parent" 
		    android:layout_height="wrap_content"/>
	</scrollview>
</linearlayout>

因为需要访问网络,所以我们还需要在androidmanifest.xml中加入访问网络的权限:


我们来看一下运行时的界面:

\\

\\

以上几个截图分别是初始界面、执行异步任务时界面、执行成功后界面、取消任务后界面。执行成功后,整个过程日志打印如下:

\

如果我们在执行任务时按下了“cancel”按钮,日志打印如下:

\

可以看到oncancelled()方法将会被调用,onpostexecute(result result)方法将不再被调用。

上面介绍了asynctask的基本应用,有些朋友也许会有疑惑,asynctask内部是怎么执行的呢,它执行的过程跟我们使用handler又有什么区别呢?答案是:asynctask是对thread+handler良好的封装,在android.os.asynctask代码里仍然可以看到thread和handler的踪迹。下面就向大家详细介绍一下asynctask的执行原理。

我们先看一下asynctask的大纲视图:

\

我们可以看到关键几个步骤的方法都在其中,doinbackground(params... params)是一个抽象方法,我们继承asynctask时必须覆写此方法;onpreexecute()、onprogressupdate(progress... values)、onpostexecute(result result)、oncancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;publishprogress(progress... values)是final修饰的,不能覆写,只能去调用,我们一般会在doinbackground(params... params)中调用此方法;另外,我们可以看到有一个status的枚举类和getstatus()方法,status枚举类代码段如下:


//初始状态
private volatile status mstatus = status.pending;
 
public enum status {
 /**
  * indicates that the task has not been executed yet.
  */
 pending,
 /**
  * indicates that the task is running.
  */
 running,
 /**
  * indicates that {@link asynctask#onpostexecute} has finished.
  */
 finished,
}
 
/**
 * returns the current status of this task.
 *
 * @return the current status.
 */
public final status getstatus() {
 return mstatus;
}

可以看到,asynctask的初始状态为pending,代表待定状态,running代表执行状态,finished代表结束状态,这几种状态在asynctask一次生命周期内的很多地方被使用,非常重要。

介绍完大纲视图相关内容之后,接下来,我们会从execute(params... params)作为入口,重点分析一下asynctask的执行流程,我们来看一下execute(params... params)方法的代码段:


public final asynctask execute(params... params) {
 if (mstatus != status.pending) {
  switch (mstatus) {
case running:
 //如果该任务正在被执行则抛出异常
 //值得一提的是,在调用cancel取消任务后,状态仍未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)");
  }
 }
 
 //改变状态为running
 mstatus = status.running;
 
 //调用onpreexecute方法
 onpreexecute();
 
 mworker.mparams = params;
 sexecutor.execute(mfuture);
 
 return this;
}

代码中涉及到三个陌生的变量:mworker、sexecutor、mfuture,我们也会看一下他们的庐山真面目:

关于sexecutor,它是java.util.concurrent.threadpoolexecutor的实例,用于管理线程的执行。代码如下:


private static final int core_pool_size = 5;
private static final int maximum_pool_size = 128;
private static final int keep_alive = 10;
 
//新建一个队列用来存放线程
private static final blockingqueue sworkqueue =
  new linkedblockingqueue(10);
//新建一个线程工厂
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 threadpoolexecutor sexecutor = new threadpoolexecutor(core_pool_size,
  maximum_pool_size, keep_alive, timeunit.seconds, sworkqueue, sthreadfactory);

mworker实际上是asynctask的一个的抽象内部类的实现对象实例,它实现了callable接口中的call()方法,代码如下:


private static abstract class workerrunnable implements callable {
 params[] mparams;
}

而mfuture实际上是java.util.concurrent.futuretask的实例,下面是它的futuretask类的相关信息:


/**
 * a cancellable asynchronous computation.
 * ...
 */
public class futuretask implements runnablefuture {

public interface runnablefuture extends runnable, future {
 /**
  * sets this future to the result of its computation
  * unless it has been cancelled.
  */
 void run();
}

可以看到futuretask是一个可以中途取消的用于异步计算的类。

下面是mworker和mfuture实例在asynctask中的体现:


private final workerrunnable mworker;
private final futuretask mfuture;
 
public asynctask() {
 mworker = new workerrunnable() {
  //call方法被调用后,将设置优先级为后台级别,然后调用asynctask的doinbackground方法
  public result call() throws exception {
process.setthreadpriority(process.thread_priority_background);
return doinbackground(mparams);
  }
 };
 
 //在mfuture实例中,将会调用mworker做后台任务,完成后会调用done方法
 mfuture = new futuretask(mworker) {
  @override
  protected void done() {
message message;
result result = null;
 
try {
 result = get();
} catch (interruptedexception e) {
 android.util.log.w(log_tag, e);
} catch (executionexception e) {
 throw new runtimeexception("an error occured while executing doinbackground()",
e.getcause());
} catch (cancellationexception e) {
 //发送取消任务的消息
 message = shandler.obtainmessage(message_post_cancel,
new asynctaskresult(asynctask.this, (result[]) null));
 message.sendtotarget();
 return;
} catch (throwable t) {
 throw new runtimeexception("an error occured while executing "
+ "doinbackground()", t);
}
 
//发送显示结果的消息
message = shandler.obtainmessage(message_post_result,
  new asynctaskresult(asynctask.this, result));
message.sendtotarget();
  }
 };
}

我们看到上面的代码中,mfuture实例对象的done()方法中,如果捕捉到了cancellationexception类型的异常,则发送一条“message_post_cancel”的消息;如果顺利执行,则发送一条“message_post_result”的消息,而消息都与一个shandler对象关联。这个shandler实例实际上是asynctask内部类internalhandler的实例,而internalhandler正是继承了handler,下面我们来分析一下它的代码:


private static final int message_post_result = 0x1;	//显示结果
private static final int message_post_progress = 0x2;	//更新进度
private static final int message_post_cancel = 0x3;	//取消任务
 
private static final internalhandler shandler = new internalhandler();
 
private static class internalhandler extends handler {
 @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
 //调用asynctask.finish方法
 result.mtask.finish(result.mdata[0]);
 break;
case message_post_progress:
 //调用asynctask.onprogressupdate方法
 result.mtask.onprogressupdate(result.mdata);
 break;
case message_post_cancel:
 //调用asynctask.oncancelled方法
 result.mtask.oncancelled();
 break;
  }
 }
}

我们看到,在处理消息时,遇到“message_post_result”时,它会调用asynctask中的finish()方法,我们来看一下finish()方法的定义:


private void finish(result result) {
 if (iscancelled()) result = null;
 onpostexecute(result);	//调用onpostexecute显示结果
 mstatus = status.finished;	//改变状态为finished
}

原来finish()方法是负责调用onpostexecute(result result)方法显示结果并改变任务状态的啊。

另外,在mfuture对象的done()方法里,构建一个消息时,这个消息包含了一个asynctaskresult类型的对象,然后在shandler实例对象的handlemessage(message msg)方法里,使用下面这种方式取得消息中附带的对象:


asynctaskresult result = (asynctaskresult) msg.obj;

这个asynctaskresult究竟是什么呢,它又包含什么内容呢?其实它也是asynctask的一个内部类,是用来包装执行结果的一个类,让我们来看一下它的代码结构:


@suppresswarnings({"rawuseofparameterizedtype"})
private static class asynctaskresult {
 final asynctask mtask;
 final data[] mdata;
 
 asynctaskresult(asynctask task, data... data) {
  mtask = task;
  mdata = data;
 }
}

看以看到这个asynctaskresult封装了一个asynctask的实例和某种类型的数据集,我们再来看一下构建消息时的代码:


//发送取消任务的消息
message = shandler.obtainmessage(message_post_cancel,
  new asynctaskresult(asynctask.this, (result[]) null));
message.sendtotarget();

在处理消息时是如何使用这个对象呢,我们再来看一下:


result.mtask.finish(result.mdata[0]);

result.mtask.onprogressupdate(result.mdata);

概括来说,当我们调用execute(params... params)方法后,execute方法会调用onpreexecute()方法,然后由threadpoolexecutor实例sexecutor执行一个futuretask任务,这个过程中doinbackground(params... params)将被调用,如果被开发者覆写的doinbackground(params... params)方法中调用了publishprogress(progress... values)方法,则通过internalhandler实例shandler发送一条message_post_progress消息,更新进度,shandler处理消息时onprogressupdate(progress... values)方法将被调用;如果遇到异常,则发送一条message_post_cancel的消息,取消任务,shandler处理消息时oncancelled()方法将被调用;如果执行成功,则发送一条message_post_result的消息,显示结果,shandler处理消息时onpostexecute(result result)方法被调用。

经过上面的介绍,相信朋友们都已经认识到asynctask的本质了,它对thread+handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率,希望朋友们能多多体会一下。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网