Android中AsyncTask异步

时间:2023-03-08 17:24:13
Android中AsyncTask异步

今天我们学习了 AsyncTack, 这是一个异步任务。

那么这个异步任务可以干什么呢?

  因为只有UI线程,即主线程可以对控件进行更新操作。好处是保证UI稳定性,避免多线程对UI同时操作。

  同时要把耗时任务放在非主线程中执行,否则会造成阻塞,抛出无响应异常。

那么在Android中实现异步任务机制有两种方式,Handler和AsyncTask。今天主要讲的是 asyncTack.

  我们通过API 来学习下 整个 AsyncTack

1.为什么要异步任务

  • Android单线程模式
  • 耗时操作放在非主线程(UI线程)中执行

  我们都知道Android是单线程模式,只有主线程才能对UI操作,简称UI线程。

  当然这样的好处是:保证UI的稳定性、准确性,避免多线程同时对UI的操作,导致UI的混乱

  但同时Android是一个多线程的操作系统,不可能把全部的事情放在主线程。

  如果任务堵塞,当时间过长,会抛出ANR(Application Not Responding)错误。

  AsyncTask  能够适当的,简单的用于 UI 线程。这个类不需要操作线程(Thread)就可以完成后台操作将结果返回 UI

  异步任务的定义是一个在后台线程上运行,其结果是在UI线程上发布的计算。

2.AsyncTask为何而生

  • 子线程中更新UI
  • 封装、简化异步操作

  结构

    继承关系

      public abstract class AsyncTask extends Object

    java.lang.Object

      android.os.AsyncTask <params,Progress,Result>

3.构建AsyncTask子类的参数
  AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:

    Params : 启动任务时输入参数的类型。

    Progress : 后台任务执行中返回进度值的类型(后台人数执行的百分比)

    Result : 后台执行任务完成后返回结果的类型(后台计算的结果类型)

  注:在一个异步任务中,不是所有的类型总被用。假如一个类型不被使用,可以简单地使用void 类型。

4.构建AsyncTask子类的回调方法

  doInBackground :

    必须重写,异步执行后台程序将要完成的任务。

    用于在执行异步任务,不可以更改主线程中UI。

    当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。

    所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)

  onPreExecute :

    执行后台耗时操作前被调用,通常用户完成一些初始化操作。

    用于在执行异步任务前,主线程做一些准备工作

    被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

    在UI线程上调用任务后立即执行。这步通常被用于设置任务,例如在用户界面显示一个进度条。

  onPostExecute :

    当doInBackground() 完成后,系统自动调用onPostExecute()方法,并将doInBackground方法返回的值传给该方法。

    用于异步任务执行完成后,在主线程中执行的操作

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

    当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。

  onProgressUpdate :

    在doInBackground() 方法调用publishProgress() 方法更新任务的执行进度后,就会触发该方法。

    用于更新异步执行中,在主线程中处理异步任务的执行信息

    此方法被执行,直接将进度信息更新到UI组件上。

    后台线程执行onPreExecute()完后立即调用,这步被用于执行较长时间的后台计算。异步任务的参数也被传到这步。

    计算的结果必须在这步返回,将传回到上一步。在执行过程中可以调用publishProgress(Progress...)来更新任务的进度。

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

  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执行异步任务操作,我们先建立一个项目,结构如下:

Android中AsyncTask异步

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

package com.example.multithreadind01;

import java.util.ArrayList;
import java.util.List; import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast; public class MainActivity extends Activity { private String fromDb_str1 = "";
private Button btn;
private Button btn2;
private TextView tv;
private ListView lv;
private BaseAdapter adapter;
private List<User> userList = new ArrayList<User>();
private MyTask mt; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 模拟数据访问产生数据
for (int i = 0; i < 5; i++) {
User u = new User();
u.setUsername("模拟" + i);
u.setSex("女" + i);
userList.add(u);
} tv = (TextView) findViewById(R.id.textView1);
btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// 注意:
// 1 每次需new一个实例,新建的任务只能执行一次,否则会出现异常
// 2 异步任务的实例必须在UI线程中创建
// 3 execute()方法必须在UI线程中调用。
mt = new MyTask(MainActivity.this);
mt.execute(userList, adapter);// 里面的参数是传给 doInBackground }
}); btn2 = (Button) findViewById(R.id.Button2);
btn2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// 取消一个正在执行的任务,onCancelled()方法将会被调用
mt.cancel(true);
Toast.makeText(getApplicationContext(), "onCancelled()取消", 1)
.show();
}
}); adapter = new BaseAdapter() { public int getCount() {
return userList.size();// listView 循环数量
} public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = MainActivity.this.getLayoutInflater();
View view;
if (convertView == null) {
view = inflater.inflate(R.layout.item, null); // 创建convertView
} else {
view = convertView; // 复用
} TextView tv_username = (TextView) view
.findViewById(R.id.username);
TextView tv_sex = (TextView) view.findViewById(R.id.sex);
tv_username.setText(userList.get(position).getUsername());
tv_sex.setText(userList.get(position).getSex());
return view;
} public Object getItem(int position) {
return null;
} public long getItemId(int position) {
return 0;
}
};
lv = (ListView) findViewById(R.id.listView1);
lv.setAdapter(adapter);
} }

MainActivity.java

MyTask.java的代码:

package com.example.multithreadind01;

import java.util.List;

import android.os.AsyncTask;
import android.widget.BaseAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast; //构造函数AsyncTask<Params, Progress, Result>参数说明:
//Params 启动任务执行的输入参数
//Progress 后台任务执行的进度
//Result 后台计算结果的类型 public class MyTask extends AsyncTask { private BaseAdapter adapter;
private List<User> userList;
private MainActivity activity;
private ProgressBar progressBar; public MyTask(MainActivity activity) {
this.activity = activity;
} // 1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)
// 2.绝对不能碰UI
// doInBackground()方法用于在执行异步任务,不可以更改主线程中UI
protected Object doInBackground(Object... params) { System.out.println("调用doInBackground()方法--->开始执行异步任务");
userList = (List<User>) params[0];
adapter = (BaseAdapter) params[1];
for (int i = 0; i < userList.size(); i++) {
try {
// 为了演示进度,休眠1000毫秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} userList.get(i).setUsername("更改" + i);
userList.get(i).setSex("男" + i);
// publishProgress()为AsyncTask类中的方法
// 常在doInBackground()中调用此方法 用于通知主线程,后台任务的执行情况.
// 此时会触发AsyncTask中的onProgressUpdate()方法
publishProgress(i);
} // userlist,adapter // 返回给前端
return "天气:22度";
} // 准备
// onPreExecute()方法用于在执行异步任务前,主线程做一些准备工作
protected void onPreExecute() {
Toast.makeText(activity, "开始准备", Toast.LENGTH_SHORT).show();
System.out.println("调用onPreExecute()方法--->准备开始执行异步任务");
} // 做完后执行
// onPostExecute()方法用于异步任务执行完成后,在主线程中执行的操作
protected void onPostExecute(Object result) {
String r = result.toString();
TextView tv = (TextView) activity.findViewById(R.id.textView1);
// textView显示请求结果
tv.setText("访问完成!" + r);
System.out.println("调用onPostExecute()方法--->异步任务执行完毕");
} // 分步完成
// onProgressUpdate()方法用于更新异步执行中,在主线程中处理异步任务的执行信息
protected void onProgressUpdate(Object... values) { // 0,1,2,3,4
int bar = Integer.parseInt(values[0].toString());
bar = (bar + 1) * 20;
progressBar = (ProgressBar) activity.findViewById(R.id.progressBar1);
// 更改ProgressBar
progressBar.setProgress(bar);
adapter.notifyDataSetChanged();
System.out.println("调用onProgressUpdate()方法--->更新异步执行中");
} // onCancelled()方法用于异步任务被取消时,在主线程中执行相关的操作
protected void onCancelled() { progressBar = (ProgressBar) activity.findViewById(R.id.ProgressBar2);
// 更改进度条进度为0
progressBar.setProgress(0); System.out.println("调用onCancelled()方法--->异步任务被取消");
} }

MyTask.java

User.java的代码:

package com.example.multithreadind01;

public class User {
private String username;
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
} }

User.java

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.multithreadind01.MainActivity" > <TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" /> <ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"
android:layout_marginTop="44dp" >
</ListView> <Button
android:id="@+id/Button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/listView1"
android:layout_alignTop="@+id/button1"
android:text="取消异步任务" /> <Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="15dp"
android:text="开始异步任务" /> <ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/listView1"
android:layout_alignRight="@+id/button1"
android:layout_below="@+id/button1" /> <ProgressBar
android:id="@+id/ProgressBar2"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/progressBar1"
android:layout_alignLeft="@+id/Button2"
android:layout_alignRight="@+id/Button2"
android:layout_below="@+id/Button2" /> </RelativeLayout>

activity_main.xml

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/username"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:textSize="45dp"
/> <TextView
android:id="@+id/sex"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:textSize="45dp"
/>
</LinearLayout>

item.xml

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

Android中AsyncTask异步

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

Android中AsyncTask异步

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

Android中AsyncTask异步

Android中AsyncTask异步

可以看到onCancelled()方法将会被调用,onPostExecute(Result result)方法将不再被调用。

详解Android中AsyncTask的使用: http://blog.csdn.net/liuhe688/article/details/6532519

关于asynctask的取消操作: http://blog.csdn.net/isamu/article/details/9381139