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执行异步任务操作,我们先建立一个项目,结构如下:
结构相对简单一些,让我们先看看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); } }
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()方法--->异步任务被取消"); } }
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; } }
布局文件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>
布局文件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>
我们来看一下运行时的界面:
以上几个截图分别是初始界面、执行异步任务时界面、执行成功后界面、取消任务后界面。执行成功后,整个过程日志打印如下:
如果我们在执行任务时按下了“取消异步任务”按钮,日志打印如下:
可以看到onCancelled()方法将会被调用,onPostExecute(Result result)方法将不再被调用。
详解Android中AsyncTask的使用: http://blog.csdn.net/liuhe688/article/details/6532519
关于asynctask的取消操作: http://blog.csdn.net/isamu/article/details/9381139