今天我们学习了 AsyncTack, 这是一个异步任务。
那么这个异步任务可以干什么呢?
因为只有UI线程,即主线程可以对控件进行更新操作。好处是保证UI稳定性,避免多线程对UI同时操作。
同时要把耗时任务放在非主线程中执行,否则会造成阻塞,抛出无响应异常。
那么在Android中实现异步任务机制有两种方式,Handler和AsyncTask。今天主要讲的是 asyncTack.
我们通过API 来学习下 整个 AsyncTack
一、结构
继承关系
public abstract class AsyncTask extends Object
java.lang.Object
android.os.AsyncTask <params,Progress,Result>
二、类概述
AsyncTask 能够适当的,简单的用于 UI 线程。这个类不需要操作线程(Thread)就可以完成后台操作将结果返回 UI
异步任务的定义是一个在后台线程上运行,其结果是在UI线程上发布的计算。
异步任务被定义成
三种泛型类型:
Params:启动任务执行的输入参数。
Progress:后台人数执行的百分比
Result:后台计算的结果类型
注:在一个异步任务中,不是所有的类型总被用。假如一个类型不被使用,可以简单地使用void 类型。
四个步骤:
1.onPreExecute():在UI线程上调用任务后立即执行。这步通常被用于设置任务,例如在用户界面显示一个进度条。
2.doInBackground(Params...):后台线程执行 onPreExecute()完成立即调用,这步被用于执行较长时间的后台计算。异步任务的参数也被传到这步。计算的结果必须在这步返回,将返回到上一步。在执行过程中可以调用 publishProgress(Progress...)来更新人无语的进度。
3.onProcessProgress():一次呼叫 publishProgress(Progress...)后调用UI线程。执行时间是不确定的。这个方法用于当后台计算还在进行时在用户界面显示进度。例如:这个方法可以被用于一个进度条动画或在文本域显示记录。
4.onPostExecute(Resule):当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。
接下来我们用一个栗子看看这个 AsyncTask 到底能做什么。
这是我们的xml 布局文件
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.example.multithreadind01.MainActivity" > 10 11 <TextView 12 android:id="@+id/textView1" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:text="@string/hello_world" /> 16 17 <Button 18 android:id="@+id/button1" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_alignParentRight="true" 22 android:layout_alignTop="@+id/textView1" 23 android:layout_marginRight="53dp" 24 android:text="Button" /> 25 26 <ListView 27 android:id="@+id/listView1" 28 android:layout_width="match_parent" 29 android:layout_height="wrap_content" 30 android:layout_below="@+id/button1" 31 android:layout_marginTop="84dp" > 32 </ListView> 33 34 <ProgressBar 35 android:id="@+id/progressBar1" 36 style="?android:attr/progressBarStyleHorizontal" 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:layout_alignLeft="@+id/listView1" 40 android:layout_alignRight="@+id/button1" 41 android:layout_below="@+id/button1" 42 android:layout_marginTop="28dp" /> 43 44 </RelativeLayout>
这是我们listView 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>
这是我们的 user.class
1 package com.example.multithreadind01; 2 3 public class User { 4 private String username; 5 private String sex; 6 public String getUsername() { 7 return username; 8 } 9 public void setUsername(String username) { 10 this.username = username; 11 } 12 public String getSex() { 13 return sex; 14 } 15 public void setSex(String sex) { 16 this.sex = sex; 17 } 18 19 }
MainActivity.class
1 package com.example.multithreadind01; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 import android.os.Bundle; 8 import android.view.LayoutInflater; 9 import android.view.Menu; 10 import android.view.MenuItem; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.view.ViewGroup; 14 import android.widget.BaseAdapter; 15 import android.widget.Button; 16 import android.widget.ListView; 17 import android.widget.TextView; 18 19 20 public class MainActivity extends Activity { 21 22 private String fromDb_str1 = ""; 23 private Button btn; 24 private TextView tv; 25 private ListView lv; 26 private BaseAdapter adapter; 27 private List<User> userList = new ArrayList<User>(); 28 29 @Override 30 protected void onCreate(Bundle savedInstanceState) { 31 super.onCreate(savedInstanceState); 32 setContentView(R.layout.activity_main); 33 34 //模拟数据访问产生数据 35 for (int i = 0; i < 5; i++) { 36 User u = new User(); 37 u.setUsername("小明"+i); 38 u.setSex("女"+i); 39 userList.add(u); 40 } 41 42 tv =(TextView)findViewById(R.id.textView1); 43 btn =(Button)findViewById(R.id.button1); 44 btn.setOnClickListener(new OnClickListener() { 45 46 @Override 47 public void onClick(View v) { 48 MyTask mt = new MyTask(MainActivity.this); 49 mt.execute(userList,adapter);//里面的参数是传给doInBackground 50 /* 51 Thread t1 = new Thread(new Runnable() { 52 @Override 53 public void run() { 54 fromDb_str1 = "测试"; 55 } 56 }); 57 t1.start(); 58 tv.setText(fromDb_str1); 59 */ 60 61 } 62 }); 63 adapter = new BaseAdapter(){ 64 65 @Override 66 public int getCount() { 67 // TODO Auto-generated method stub 68 return userList.size(); 69 } 70 71 @Override 72 public View getView(int position, View convertView, ViewGroup parent) { 73 LayoutInflater inflater = MainActivity.this.getLayoutInflater(); 74 View view; 75 if (convertView==null){ 76 view = inflater.inflate(R.layout.item, null); 77 } 78 else{ 79 view = convertView; 80 } 81 82 TextView tv_username = (TextView)view.findViewById(R.id.username); 83 TextView tv_sex = (TextView)view.findViewById(R.id.sex); 84 tv_username.setText(userList.get(position).getUsername()); 85 tv_sex.setText(userList.get(position).getSex()); 86 return view; 87 } 88 89 @Override 90 public Object getItem(int position) { 91 // TODO Auto-generated method stub 92 return null; 93 } 94 95 @Override 96 public long getItemId(int position) { 97 // TODO Auto-generated method stub 98 return 0; 99 } 100 }; 101 lv = (ListView)findViewById(R.id.listView1); 102 lv.setAdapter(adapter); 103 } 104 }
最后是我们异步类 MyTask.class
1 package com.example.multithreadind01; 2 3 import java.util.List; 4 5 import android.os.AsyncTask; 6 import android.view.View; 7 import android.widget.BaseAdapter; 8 import android.widget.ProgressBar; 9 import android.widget.TextView; 10 import android.widget.Toast; 11 12 public class MyTask extends AsyncTask { 13 14 private BaseAdapter adapter; 15 private List<User> userList; 16 private MainActivity activity; 17 public MyTask(MainActivity activity){ 18 this.activity = activity; 19 } 20 21 //1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务) 22 //2.绝对不能碰UI 23 @Override 24 protected Object doInBackground(Object... params) { 25 26 userList = (List<User>) params[0]; 27 adapter = (BaseAdapter) params[1]; 28 for (int i = 0; i < userList.size(); i++) { 29 try { 30 Thread.sleep(1000); 31 } catch (InterruptedException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 userList.get(i).setUsername("小红"+i); 36 userList.get(i).setSex("男"+i); 37 publishProgress(i); 38 } 39 40 41 //userlist,adapter 42 43 //返回给前端 44 return "天气:22度"; 45 } 46 47 //准备 48 @Override 49 protected void onPreExecute() { 50 Toast.makeText(activity, "hello ,今晚约不约", Toast.LENGTH_SHORT).show(); 51 52 } 53 54 //做完后执行 55 @Override 56 protected void onPostExecute(Object result) { 57 String r = result.toString(); 58 TextView tv = (TextView)activity.findViewById(R.id.textView1); 59 tv.setText("访问完成!"+r); 60 61 } 62 63 //分步完成 64 @Override 65 protected void onProgressUpdate(Object... values) { 66 67 //0,1,2,3,4 68 int bar = Integer.parseInt(values[0].toString()); 69 bar = (bar+1)*20; 70 ProgressBar progressBar = (ProgressBar)activity.findViewById(R.id.progressBar1); 71 progressBar.setProgress(bar); 72 adapter.notifyDataSetChanged(); 73 } 74 75 }
以上案例使我们做到后台数据加载到前端的过程中,使用异步,不会出现所谓的“卡顿”。使得整个程序运行的效果是畅通的,用户体验性也提高。