-- 做者 谢恩铭 转载请注明出处javascript
在Android应用开发中,有时咱们须要实现任务的同步。html
Android里的AsyncTask类能够帮咱们更好地管理线程同步(异步方式),就像Thread类能作的,不过用法比Thread更简单。java
AsyncTask算是帮咱们作了一层封装吧,使咱们能够不用操心那么多,若是阅读AsyncTask的源码就能够了解。android
具体AsyncTask的使用方法,最好参看Google Android的官方文档:git
developer.android.com/reference/a…程序员
在你开发Android应用程序时,若是有一个耗时任务(一般是一个子线程),而且这个任务调用了主线程,应用就会抛出著名的“ANR” (Application Not Responding,"应用无响应")错误。github
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:
onPreExecute : 此方法在主线程中执行,用于初始化任务。
doInBackground : 此方法在后台执行,是一个抽象方法,必需要被子类重写。此方法在onPreExecute方法执行完后启动。这个方法中执行的操做能够是耗时的,并不会阻塞主线程。经过调用publishProgress方法来在主线程显示后台任务执行的结果更新。
onProgressUpdate : 此方法也在主线程中执行,每当publishProgress方法被调用时,此方法就被执行,此方法只在doInBackground执行过程当中才能被调用。
onPostExecute : 在doInBackground方法执行完以后启动的方法,在后台任务结束后才调用此方法,也在主线程执行。
为了更好地理解AsyncTask的使用,咱们来实现一个计时器的小应用。
首先咱们建立一个Android项目,就命名为AsyncTaskActivity好了(名字无所谓),修改 res->layout 里的定义主用户界面的 xml 文件(好比是main.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="15dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="5dp"
android:text="Time in min"
android:textSize="22sp"
android:textStyle="bold" />
<EditText
android:id="@+id/chronoValue"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginBottom="15dp"
android:layout_gravity="center"
android:hint="minutes"
android:inputType="number"
android:maxLines="1"
android:text="1"
android:textSize="20sp" />
<TextView
android:id="@+id/chronoText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="0:0"
android:textSize="80sp" />
<Button
android:id="@+id/start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="Start" />
</LinearLayout>复制代码
在以上的main.xml文件中,咱们主要定义了:
一个EditText,用于输入须要计数的时间
一个TextView,用于显示计数的变化
一个Button,用于启动计数任务。
在咱们的类AsyncTaskActivity中,咱们首先声明三个private变量,对应以上三个元素。
private EditText chronoValue;
private TextView chronoText;
private Button start;复制代码
而后建立一个内部类,继承AsyncTask类,命名为“Chronograph”,就是英语“秒表,计时器”的意思。
private class Chronograph extends AsyncTask<Integer, Integer, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 在计时开始前,先使按钮和EditText不能用
chronoValue.setEnabled(false);
start.setEnabled(false);
chronoText.setText("0:0");
}
@Override
protected Void doInBackground(Integer... params) {
// 计时
for (int i = 0; i <= params[0]; i++) {
for (int j = 0; j < 60; j++) {
try {
// 发布增量
publishProgress(i, j);
if (i == params[0]) {
return null;
}
// 暂停一秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (isCancelled()) {
return null;
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 更新UI界面
chronoText.setText(values[0] + ":" + values[1]);
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// 从新使按钮和EditText可使用
chronoValue.setEnabled(true);
start.setEnabled(true);
}
}复制代码
以上,咱们重写了咱们须要的四个方法。最后咱们再完成咱们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 不一样),例如:
private class Chronograph extends AsyncTask<Integer, Integer, Void> {...}复制代码
运行咱们的项目,能够获得以下面三张图所示的结果:
怎么样,AsyncTask不难使用吧~
这个例子项目我发在个人Github上了,请参看:
从此,当有异步任务须要执行时,可使用AsyncTask类,能够根据本身的须要来定制。
AsyncTask使用了线程池(Thread Pool)的机制,使得同时执行多个AsyncTask成为可能。可是要注意的是,这个线程池的容量是5个线程同时执行,若是超过了这个数量,多余的线程必须等待线程池里的线程执行完才能启动。
使用AsyncTask,最好在明确知道任务会有一个肯定和合理的结束的状况下。不然,仍是使用传统的Thread类为好。
在doInBackground方法中的耗时操做最好是能保证在几秒钟以内完成的,不要作特别久的耗时操做。
人世间,
万千情感皆有温度,
千万代码似有性格。
这里有原创教程,IT丛林......
和你一块儿探索程序人生。
微信公众号「程序员联盟」ProgrammerLeague
我是谢恩铭,在巴黎奋斗的嵌入式软件工程师。
我的简介热爱生活,喜欢游泳,略懂烹饪。人生格言:“向着标杆直跑”