AsyncTask简介(一) 基本概念和用法
来源:互联网 发布:机械先驱知乎 编辑:程序博客网 时间:2024/05/21 07:29
1 AsyncTask
在Android中,由于不能在子线程更新UI,也不能在主线程(UI线程)做耗时操作。所以经常需要在后台进行操作并在操作完成以后将结果发送给UI线程。Android为了开发者提供了Handler+后台Thread的开发框架,用于上述场景。关于Handler的介绍可以参见:Handler,Message,Looper & MessageQueue 。Handler的使用稍显复杂,需要继承Handler类,重写处理函数。还需要开发者自己开辟线程,在线程中完成操作以后再发送消息将结果更新到UI线程。Android为我们提供了AsyncTask这个工具类。AsyncTask对Handler+Thread开发框架进行了封装,AysncTask可以帮助我们创建后台线程,开发者只需要简单的重写几个回调函数,就可以完成后台子线程处理耗时操作,再将结果更新到UI线程的操作。
在后续的章节先学习AsyncTask的基本用法,以及使用AsyncTask的注意点。
下一篇文章AsyncTask简介(二) 源码剖析 将从AsyncTask的源码入手,分析AsyncTask的原理。
2 AsyncTask的基本用法
2.1 AsyncTask的定义
AsyncTask的类声明:
public abstract class AsyncTask<Params, Progress, Result> {...}
这个声明向我们传达了两个信息
1. AsyncTask是一个泛型类,有三个泛型类型:
a) Params: 要执行的任务的参数的类型
b) Progress:任务在后台执行的进度单元的类型
c) Result:返回值的类型
2. 同时AsyncTask还是一个抽象类,必须继承以后才能使用
使用AsyncTask,可以重写四个回调函数
//在UI线程调用,在doInBackground之前执行protected void onPreExecute() //在后台线程调用,完成后台任务的方法主体,抽象方法,AsyncTask的子类必须重写该方法protected abstract Result doInBackground(Params... params) //在UI线程调用,显示操作进度 protected void onProgressUpdate(Progress... values) //在UI线程调用,在doInBackground之后执行 protected void onPostExecute(Result result)
下面我们就结合一个Demo对AsyncTask的类型参数和回调的使用进行说明。
2.2 使用doInBackground方法的AsyncTask
这个Demo很简单,在主Activity中,有一个Button,一个ProgressBar,一个TextView。点击Button,会执行下载任务,用ProgressBar显示下载的进度,下载完成后,在TextView上更新Task End,告诉用户下载完毕。
——>
public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; private static final int PROGRESS_BAR_MAX_VALUE = 5; private static final int PORGRESS_UPDATE_TIME_GAP = 100; private Button mButton; private ProgressBar mProgressBar; private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button)findViewById(R.id.button); mProgressBar = (ProgressBar)findViewById(R.id.progressBar); mTextView = (TextView)findViewById(R.id.textView); mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { AsyncTask<Integer, Integer, String> asyncTask = new AsyncTask<Integer, Integer, String>() { @Override protected void onPreExecute() { Log.d(TAG, "onPreExecute was executed in : " + Thread.currentThread().getName()); mProgressBar.setProgress(0); mProgressBar.setMax(PROGRESS_BAR_MAX_VALUE); mTextView.setText("Task Start"); } @Override protected String doInBackground(Integer... params) { Log.d(TAG, "doInBackground was executed in : " + Thread.currentThread().getName()); try { for (int i = 0 ; i <= params[0]; i++ ) { Thread.sleep(params[1]); this.publishProgress(i); Log.d(TAG, "publishProgress was executed in : " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } return new String("Task End"); } @Override protected void onProgressUpdate(Integer... values) { Log.d(TAG, "onProgressUpdate was executed in : " + Thread.currentThread().getName()); mProgressBar.setProgress(values[0]); } @Override protected void onPostExecute(String result) { Log.d(TAG, "onPostExecute was executed in : " + Thread.currentThread().getName() + ". the result is :" + result); mTextView.setText(result); } }; asyncTask.execute(PROGRESS_BAR_MAX_VALUE, PORGRESS_UPDATE_TIME_GAP); } }); }}
2.2.1 泛型参数
AsyncTask的三个泛型类型,在Demo代码中分别对应Integer, Integer,String。
AsyncTask<Params, Progress, Result> execute(Params... params) -> AsyncTask<Integer, Integer, String> execute(Integer... params)Result doInBackground(Params... params) -> String doInBackground(Integer... params)void onProgressUpdate(Progress... values) -> void onProgressUpdate(Integer... values)void onPostExecute(Result result) -> void onPostExecute(String result)
所以在Demo中执行任务的参数类型是Integer,显示进度的参数类型是Integer,后台任务的返回值的参数类型是String。
2.2.2 AsyncTask回调函数的执行步骤
图是说明顺序和逻辑最好的工具。下面这张图显示了这几个函数执行的顺序和执行的宿主线程。
如图所示:
1. 开发者主动调用execute
2. onPreExecute将会在主线程中被调用
3. doInBackground将会在后台线程中被调用,doInBackground过程调用publishProgress,会出发onUpdateProgress在主线中被调用
4. onPostExecute在主线程中被调用。
下面我们对每一步都进行详细的说明。
Step 1,开始AsyncTask:
点击Button调用AsyncTask的execute(Integer… params),这是开发者唯一需要主动调用的函数,且需要在UI线程中调用。表示开始执行AsyncTask。
需要说明的是,将来doInBackground函数在执行的参数列表正是在执行execute函数时传递的参数列表。
在Demo中,我们向execute传递了两个整形参数:PROGRESS_BAR_MAX_VALUE,PORGRESS_UPDATE_TIME_GA。AsyncTask会帮助我们将这两个参数传递给doInBackground。
asyncTask.execute(PROGRESS_BAR_MAX_VALUE, PORGRESS_UPDATE_TIME_GAP);
Step 2,AsyncTask的准备工作:
onPreExecute(),在调用execute以后立刻执行,对任务进行相关的初始设置。由于其会在主线程中被调用,所以可以用来做一些UI上的准备工作。在Demo中通过该回调将progressBar的值置为0,将TextView置为“Task Start”,表示下载任务即将开始。
protected void onPreExecute() { Log.d(TAG, "onPreExecute was executed in : " + Thread.currentThread().getName()); mProgressBar.setProgress(0); mProgressBar.setMax(PROGRESS_BAR_MAX_VALUE); mTextView.setText("Task Start");}
Step 3, 后台线程任务运行:
doInBackground(Params… params),当准备工作(onPreExecute)执行完以后,就轮到正式的任务(doInBackground)开始了。
doInBackground的参数列表Params… params 对应 step1 中调用的execute的参数列表。所以params[0]和params[1]正是step1中调用execute传入的PROGRESS_BAR_MAX_VALUE和PORGRESS_UPDATE_TIME_GAP。Demo中的下载任务(用sleep函数模拟)就是在这里被运行的。在下载任务执行过程中,总共需要PROGRESS_BAR_MAX_VALUE步,每隔PORGRESS_UPDATE_TIME_GAP毫秒完成一步,主动调用一次pulishProgress,通知主线程调用onProgressUpdate函数,更新Activity中的下载进度条的进度。
protected String doInBackground(Integer... params) { Log.d(TAG, "doInBackground was executed in : " + Thread.currentThread().getName()); try { for (int i = 0 ; i <= params[0]; i++ ) { Thread.sleep(params[1]); this.publishProgress(i); Log.d(TAG, "publishProgress was executed in : " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } return new String("Task End");}
在step3中提到了另外两个函数:
a) publishProgress(Progress… values)
b) onProgressUpdate(Progress… values)
下面是他们的关系图
onProgressUpdate和publishProgress是成对出现的。在doInBackground中(即子线程中)调用一次publishProgress,主线程就会调用一次onProgressUpdate。因此onProgressUpdate严格意义上来说并不是执行AsyncTask过程的中的某个步骤,而是由后台线程的doInBackground主动通知主线程调用的回调函数,是和doInBackground交织在一起的。
Step4:任务完成,通知主线程结果
onPostExecute(Result result)。在doInBackground执行完成以后,需要将执行结果通知到主线程,onPostExecute将在主线程被调用,而其参数(Result result)正是doInBackground的返回值。
protected void onPostExecute(String result) { Log.d(TAG, "onPostExecute was executed in : " + Thread.currentThread().getName() + ". the result is :" + result); mTextView.setText(result);}
在Demo中,Result的参数类型是String,所以doInBackground的返回值类型和onPostExcute的参数类型都是String。Step3中doInBackground返回的字串是”Task End”,onPostExecute将会获得这个字串,并更新至UI。
2.2.3 从Log来看执行顺序
我们在Demo中的每一个回调中添加了Log,运行Demo,打印出如下log:
D/MainActivity(20029): onPreExecute was executed in : mainD/MainActivity(20029): doInBackground was executed in : AsyncTask #2D/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): publishProgress was executed in : AsyncTask #2D/MainActivity(20029): onProgressUpdate was executed in : mainD/MainActivity(20029): onPostExecute was executed in : main
Log也很好的体现了前面流程图中每个回调函数执行的顺序,以及他们执行的宿主线程。
2.2 AsyncTask的简易用法
AsyncTask还提供一个简单易用的使用方法,无需创建AsyncTask实例,使用AsyncTask的静态函数
public static void execute(Runnable runnable)
调用execute方法直接执行一个Runnable对象。下面是这种方法的Demo:
public class MainActivity extends ActionBarActivity { protected static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); AsyncTask.execute(new Runnable() { public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } Log.d(TAG, "run method was executed in : " + Thread.currentThread().getName()); } }); }}
这个Demo非常简单,
1. 定义一个Runnable对象,在run方法中休眠200ms,然后打印出当前线程名。
2. 调用AsyncTask.execute方法执行这个Runnable对象。
运行Demo以后,打印出Log:
D/MainActivity(27481): run method was executed in : AsyncTask #2
注意到打印出的调用run方法的线程名是AsyncTask #2,这说明Runnable对象在后台线程被调用。
与execute( Params… params )方法相比,该方法非常简短易用。但是无onPreExecute,onPostExecute,onProgressUpdate等回调。当应用程序需要用一个后台线程执行一个简短的程序,并且不关心执行进度,执行结果。可以使用execute(Runnable runnable)。
3 使用AsyncTask需要遵守的几个准则:
- execute函数必须在UI上调用。
- 不要手动调用onPreExecute,onPostExecute,doInBackground,onProgressUpdate等回调函数。
- AsyncTask可以保证回调函数的调用都是同步的,不需要额外的同步处理。
- AsyncTask适合简短的操作,不要在AsyncTask中做过分的耗时操作。
4 总结
至此我们已经将AsyncTask的类型参数,回调的执行步骤,使用AsyncTask的注意点都讲解了一遍。讲到这里,相信大家都有点儿晕,那么多的泛型参数,那么多的回调,有的回调在子线程中调用,有的回调在主线程调用,有的在前,有的在后,有的交叉调用。怎么去记?
下一篇文章AsyncTask简介(二) 源码剖析将会带领大家剖析额AsyncTask的的源码,了解AsyncTask是如何设计的,并且了解AsyncTask为什么是这样设计的。到时候再来记这些回调的执行的顺序,执行的宿主线程就不再困难了。
源码之前,了无秘密。 –侯杰
- AsyncTask简介(一) 基本概念和用法
- MongoDB简介和基本概念(一)
- UITableView之(一):基本概念和用法
- GCD简介一:基本概念和Dispatch Queue
- JSP基础(一)--相关基本概念和常见Web服务器简介
- Spark-基本概念和简介
- Android之AsyncTask异步加载的简介(一)
- 一、malloc()和free()的基本概念以及基本用法
- PreferenceActivity用法简介(一)
- Android进阶(一)-------Android中AsyncTask的简单用法
- angular基本概念和用法(1)
- TensorFlow 基本概念和用法
- AsyncTask异步交互的用法简介
- android handler和AsyncTask用法
- Android线程一、AsyncTask使用简介
- 网络流(一).基本概念和算法
- Hibernate(一)hibernate基本概念和体系结构
- 【JavaScript】DOM(一)基本概念和节点
- 发布webservice
- C++ 使用const 引用传递参数
- closure
- 基本字段类型标识
- Windows下编译CAFFE+CUDA, 运行时提示status == CUDNN_STATUS_SUCCESS错误
- AsyncTask简介(一) 基本概念和用法
- Linux终端彩色文字输出
- java 校验文件头 判断虚假文件
- 自学QT之Qt cteator快捷键
- dojo事件机制详解(二)
- POJ 1426 Find The Multiple(dfs)
- react-native布局
- org.hibernate.AnnotationException: No identifier specified for entity
- Python中Numpy的tile函数用法以及operator.itemgetter函数和sorted函数