Android如何编写多线程程序,【Android】用AsyncTask实现多线程

b7bc53aeba68cf801b534ee32542f927.png

前言

在Android应用开发中,有时我们需要实现任务的同步。Android里的AsyncTask类可以帮我们更好地管理线程同步(异步方式),就像Thread类能做的,不过用法比Thread更简单。

这篇博文包含以下两个部分:

1、AsyncTask介绍

2、实例

一、 AsyncTask介绍

在你开发Android应用程序时,如果在一个Activity里有一个耗时任务(通常是一个子线程),并且这个任务调用/操作了主线程,应用就会抛出著名的“ANR” (Application Not Responding)错误。

anr.png

Figure 1: ANR

AsyncTask类可以帮我们解围,使用AsyncTask能让我们正确及简便地使用主线程,即使此时另有一个异步线程被创建。它使得耗时任务可以在后台执行,并在前台(UI线程或主线程)把执行结果展现出来,不必用到Thread类或Handler类。线程间通信也随之变得更简单,优雅。

* 主线程(User Interface Thread UI线程)是在Android里负责和用户界面进行交互的线程。

AsyncTask是一个抽象类,必须被继承才能实例化。有三个泛型参数,分别是: Params, Progress 和 Result:

- 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

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

android:padding="15dp" >

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center"

android:padding="5dp"

android:text="Time in min"

android:textSize="18dp"

android:textStyle="bold" />

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:singleLine="true"

android:text="1"

android:textSize="15dp" />

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="80dp" />

android:id="@+id/start"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_marginTop="15dp"

android:text="Start" />

在以上的main.xml文件中,我们主要定义了一个EditText,用于输入需要计数的时间;一个TextView,用于显示计数的变化; 和一个Button,用于启动计数任务。

在我们的类AsyncTaskActivity中,我们首先声明三个private变量,对应以上三个元素。

private Button start;

private TextView chronoText;

private EditText chronoValue;

然后创建一个内部类,继承AsyncTask类,命名为“Chronograph”,就是秒表或计时器。

private class Chronograph extends AsyncTask {

@Override

protected void onPreExecute() {

super.onPreExecute();

// Disable the button and edittext before the start of the deduction

chronoValue.setEnabled(false);

start.setEnabled(false);

chronoText.setText("0:0");

}

@Override

protected Void doInBackground(Integer... params) {

// Deduction

for (int i = 0; i <= params[0]; i++) {

for (int j = 0; j 

try {

// Publication of increment

publishProgress(i, j);

if (i == params[0]) {

return null;

}

// Pause for one second

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

if (isCancelled()) {

return null;

}

return null;

}

@Override

protected void onProgressUpdate(Integer... values) {

super.onProgressUpdate(values);

// Update on the User Interface

chronoText.setText(values[0] + ":" + values[1]);

}

@Override

protected void onPostExecute(Void result) {

super.onPostExecute(result);

// Reactivation of the button and edittext

chronoValue.setEnabled(true);

start.setEnabled(true);

}

}

以上,我们重写了我们需要的四个方法。最后我们再完成我们AsyncTaskActivity类的onCreate方法:@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// Recuperation of the usable components

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) {

// Recuperation of the value in the EditText

int value = Integer.parseInt(String.valueOf(chronoValue.getText()));

// Verification of the content

if (value > 0) {

new Chronograph().execute(value);

}

else {

Toast.makeText(AsyncTaskActivity.this, "Please enter a correct value !", Toast.LENGTH_LONG).show();

}

}

});

}

如果我们在继承AsyncTask类时,对于三个参数中有不需要的,可以定义为Void类型(注意,与小写的 void 不同),例如:

private class Chronograph extends AsyncTask {...}

运行我们的项目,可以得到如下面三张图所示的结果:

3c4ef56ebd82abf280f31879e28a2149.png

Figure 2: 按下Start按钮前

055257c16d8747693a8b234eabb27d61.png

Figure 3: 计数中

b444b8e3b9c581179bf53de37ba66abc.png

Figure 4: 计数后

总结

今后,当有异步任务需要执行时,可以使用AsyncTask类,可以根据自己的需要来定制。

AsyncTask使用了线程池(Thread Pool)的机制,使得同时执行多个AsyncTask成为可能。但是要注意的是,这个线程池的容量是5个线程同时执行,如果超过了这个数量,多余的线程必须等待线程池里的线程执行完才能启动。

因此,使用AsyncTask,最好在明确知道任务会有一个确定和合理的结束的情况下。否则,还是使用传统的Thread类为好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值