用AsyncTask实现多线程
在Android应用开发中,有时我们需要实现任务的同步。
Android里的AsyncTask类可以帮我们更好地管理线程同步(异步方式),就像Thread类能做的,不过用法比Thread更简单。
AsyncTask算是帮我们做了一层封装吧,使我们可以不用操心那么多,如果阅读AsyncTask的源码就可以了解。
具体AsyncTask的使用方法,最好参看Google Android的官方文档:
https://developer.android.com/reference/android/os/AsyncTask.html
在你开发Android应用程序时,如果有一个耗时任务(通常是一个子线程),并且这个任务调用了主线程,应用就会抛出著名的“ANR” (Application Not Responding,"应用无响应")错误。
ANR
AsyncTask类可以帮我们解围,使用AsyncTask能让我们正确及简便地使用主线程,即使此时另有一个异步线程被创建。
AsyncTask是asynchronous(英语“异步的”的意思)和task(英语“任务”的意思)的缩写,表示“异步任务”。
它使得耗时任务可以在后台执行,并在前台(UI线程,或称主线程)把执行结果展现出来,不必用到Thread类或Handler类。线程间通信也随之变得更简单,优雅。
主线程(User Interface Thread,UI线程)是在Android里负责和用户界面进行交互的线程。
AsyncTask是一个抽象类(abstract class),必须被继承才能实例化。有三个泛型参数,分别是:
Params : 传递给执行的任务的参数,也就是doInBackground方法的参数。
Progress : 后台任务执行过程中在主线程展现更新时传入的参数,也就是onProgressUpdate方法的参数。
Result : 后台执行的任务返回的结果,也就是onPostExecute方法的参数。
除此之外,继承AsyncTask类时,一般需要实现四个方法。
当然应用程序不需要调用这些方法,这些方法会在任务执行过程中被自动调用: onPreExecute, doInBackground, onProgressUpdate 和 onPostExecute (其中的doInBackground):
onPreExecute : 此方法在主线程中执行,用于初始化任务。
doInBackground : 此方法在后台执行,是一个抽象方法,必须要被子类重写。此方法在onPreExecute方法执行完后启动。这个方法中执行的操作可以是耗时的,并不会阻塞主线程。通过调用publishProgress方法来在主线程显示后台任务执行的结果更新。
onProgressUpdate : 此方法也在主线程中执行,每当publishProgress方法被调用时,此方法就被执行,此方法只在doInBackground执行过程中才能被调用。
onPostExecute : 在doInBackground方法执行完之后启动的方法,在后台任务结束后才调用此方法,也在主线程执行。
实例
为了更好地理解AsyncTask的使用,我们来实现一个计时器的小应用。
首先我们创建一个Android项目,就命名为AsyncTaskActivity好了(名字无所谓),修改 res->layout 里的定义主用户界面的 xml 文件(比如是main.xml):
在以上的main.xml文件中,我们主要定义了:
一个EditText,用于输入需要计数的时间
一个TextView,用于显示计数的变化
一个Button,用于启动计数任务。
在我们的类AsyncTaskActivity中,我们首先声明三个private变量,对应以上三个元素。
然后创建一个内部类,继承AsyncTask类,命名为“Chronograph”,就是英语“秒表,计时器”的意思。
以上,我们重写了我们需要的四个方法。最后我们再完成我们AsyncTaskActivity类的onCreate方法:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 获取三个UI组件 start = (Button)findViewById(R.id.start); chronoText = (TextView)findViewById(R.id.chronoText); chronoValue = (EditText)findViewById(R.id.chronoValue); start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 获取EditText里的数值 int value = Integer.parseInt(String.valueOf(chronoValue.getText())); // 验证数值是否大于零 if (value > 0) { new Chronograph().execute(value); } else { Toast.makeText(AsyncTaskActivity.this, "请输入一个大于零的整数值 !", Toast.LENGTH_LONG).show(); } } }); }
如果我们在继承AsyncTask类时,对于三个参数中有不需要的,可以定义为Void类型(注意,与小写的 void 不同),例如:
运行我们的项目,可以得到如下面三张图所示的结果:
按下Start按钮前
计数中
计数结束后
怎么样,AsyncTask不难使用吧~
这个例子项目我发在我的Github上了,请参看:
https://github.com/frogoscar/AsyncTaskExample
总结
今后,当有异步任务需要执行时,可以使用AsyncTask类,可以根据自己的需要来定制。
AsyncTask使用了线程池(Thread Pool)的机制,使得同时执行多个AsyncTask成为可能。但是要注意的是,这个线程池的容量是5个线程同时执行,如果超过了这个数量,多余的线程必须等待线程池里的线程执行完才能启动。
使用AsyncTask,最好在明确知道任务会有一个确定和合理的结束的情况下。否则,还是使用传统的Thread类为好。
在doInBackground方法中的耗时操作最好是能保证在几秒钟之内完成的,不要做特别久的耗时操作。
我是谢恩铭,在法国巴黎奋斗的嵌入式软件工程师。
目前工作是Android嵌入式和app开发。
热爱生活, 喜欢游泳,略懂烹饪。