当前位置: 代码迷 >> Android >> 【转】 android 异步处置AsynTask实例讲解
  详细解决方案

【转】 android 异步处置AsynTask实例讲解

热度:39   发布时间:2016-05-01 20:44:01.0
【转】 android 异步处理AsynTask实例讲解

http://tech.it168.com/a2012/0312/1323/000001323193.shtml


? ? android提供了一套专门用于异步处理的类。即:AynsTask类。使用这个类可以为耗时程序开辟一个新线程进行处理,处理完时返回。
  其实,AsynTask类就是对Thread类的一个封装,并且加入了一些新的方法。编程时,两者都可以实现同样的功能。本文后面将对AsynTask和Thread进行比较。

1、AsynTask类结构
  asysTask类主要用到的几个内部回调函数有:
doInBackGround()
onPreExecute()
onPostExecute()
onProgressUpdate()
正是这几个回调函数构成了AsynTask类的使用逻辑结构。
  注意:每个AsynTask子类必须至少要重写doInBackGround()方法。
2、回调逻辑关系
  1>主线程调用AsynTask子类实例的execute()方法后,首先会调用onPreExecute()方法。onPreExecute()在主线程中运行,可以用来写一些开始提示代码。
  2>之后启动新线程,调用doInBackground()方法,进行异步数据处理。
  3>处理完毕之后异步线程结束,在主线程中调用onPostExecute()方法。onPostExecute()可以进行一些结束提示处理。
  补充:在doInBackground()方法异步处理的时候,如果希望通知主线程一些数据(如:处理进度)。这时,可以调用publishProgress()方法。这时,主线程会调用AsynTask子类的onProgressUpdate()方法进行处理。
3、各个函数间数据的传递
  通过上面的调用关系,我们就可以大概看出一些数据传递关系。如下:
execute()向doInBackground()传递。
doInBackground()的返回值会传递给onPostExecute()
publishProgress()progressUpdate()传递。
要点:为了调用关系明确及安全,AsynTask类在继承时要传入3个泛型。
第一个泛型对应execute()向doInBackground()的传递类型。
第二个泛型对应doInBackground()的返回类型和传递给onPostExecute()的类型。
第三个泛型对应publishProgress()向progressUpdate()传递的类型。

  传递的数据都是对应类型的数组,数组都是可变长的哦。可以根据具体情况使用。
4、实例:http://cw1057.blogspot.com/2011/12/android-asynctask.html
点击按钮,执行异步操作,执行过程中不断刷新显示内容。
开始界面只有一个按钮.点击按钮后,效果如下:
[Android]异步处理AsynTask实例讲解

可以看到Progress后面的数字不停改变:
[Android]异步处理AsynTask实例讲解

一直到变为100为止。变为100后,界面如下:
[Android]异步处理AsynTask实例讲解

?
?
首先写一个类用来记录线程信息。方便我们跟踪调试。
package com.example.helloAndroid;
import android.util.Log;

public class LogUtils {
??? private static final String TAG = "LogUtils";
???
??? public static final void thread()
??? {
??????? LogUtils.thread(null);
??? }
???
??? public static final void thread(String msg){
??????? Thread t = Thread.currentThread();
??????? Log.d(TAG, "<" + t.getName() + ">id: " + t.getId() + ", Priority: " + t.getPriority() + ", Group: " + t.getThreadGroup().getName()
??????????????? + (msg != null? ",Msg:" + msg : ""));
??? }
}
?接着我们新建一个layout的xml文件。内容如下:
?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
??? android:id="@+id/widget33"
??? android:layout_width="fill_parent"
??? android:layout_height="fill_parent"
??? android:orientation="vertical"
??? xmlns:android="http://schemas.android.com/apk/res/android">
<Button android:id="@+id/btnNotify"
??????? android:layout_width="wrap_content"
??????? android:layout_height="wrap_content"
??????? android:text="Start" />
<TextView
??? android:id="@+id/txt1"
??? android:layout_width="fill_parent"
??? android:layout_height="wrap_content"
??? android:text="" />????????
</LinearLayout>
现在我们建一个类用来异步处理数据:
package com.example.helloAndroid;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.TextView;

public class Calculator extends AsyncTask<List<Integer>, Integer, Integer> {

??? private static final String TAG = "Calculator";
??? private Context context;
??? private TextView txt1;
??? private Integer nResult;
??? private Integer nProgress;
???
??? public Calculator(Context context)
??? {
??????? this.setContext(context);
??? }
???
??? public void setContext(Context context)
??? {
??????? this.context = context;
??????? this.txt1 = (TextView)((Activity)this.context).findViewById(R.id.txt1);
??????? this.progressUI();
??? }
???
??? //在处理完毕后,修改界面。只修改了一次界面。
??? public void syncUI(){
??????? //用来看调试信息
??????? LogUtils.thread("update ui...");
??????? //修改界面显示内容
??????? this.txt1.setText("calculate ..." + this.nResult);
??? }
???
??? @Override
??? protected Integer doInBackground(List<Integer>... params)
??? {
??????? //用来看调试信息
??????? LogUtils.thread("execute calculating...");
???????
??????? this.nResult = 0;
??????? Integer p;
??????? for(int i=0; i<params[0].size(); i++)
??????? {
??????????? p = params[0].get(i); //p为数字1,2,3,...10
??????????? this.nResult += p.intValue(); //nResult = 1+2+3+...10
??????????? LogUtils.thread("calculating..." + this.nResult);
??????????? //update progress
??????????? this.publishProgress(100 * (i+1)/params[0].size());
??????????? try
??????????? {
??????????????? Thread.sleep(1000);
??????????? }
??????????? catch(InterruptedException e)
??????????? {}
??????? }
??????? LogUtils.thread("completed calculator..." + this.nResult);
??????? return this.nResult;
??? }
???
??? @Override
??? protected void onPreExecute() {
??????? super.onPreExecute();
??????? LogUtils.thread("before calculating...");
??????? // 在 Main Thread 裡執行,可更新 UI
??????? this.nProgress = 0;
??? }
???
??? @Override
??? protected void onPostExecute(Integer result)
??? {
??????? super.onPostExecute(result);
??????? this.nResult = result;
??????? LogUtils.thread("calcuted result..." + this.nResult);
??????? // 在 Main Thread 裡執行,可更新 UI
??????? this.syncUI();
??? }
???
??? @Override
??? protected void onProgressUpdate(Integer...values)
??? {
??????? super.onProgressUpdate(values);
??????? this.nProgress = values[0];
??????? LogUtils.thread("Progress..." + this.nProgress);
??????? // 在 Main Thread 裡執行,可更新 UI
??????? this.progressUI();
??? }
???
??? //在处理过程中,数字不断改变。即不断刷新界面
??? private void progressUI()
??? {
??????? //用来看调试信息
??????? LogUtils.thread("Update Progress...");
??????? //修改界面显示内容
??????? this.txt1.setText("Progress:" + this.nProgress);
??? }
}
?
然后就是最直观的界面操作了。新建一个Activity。
public class TestActivity extends Activity {
??? private Calculator task;
??? private String TAG = "AsyncTask";
???
???
??? @Override
??? public void onCreate(Bundle savedInstanceState) {
??????? super.onCreate(savedInstanceState);
??????? setContentView(R.layout.main);
??????? Log.d(TAG, "onCreate");
??????? // 重新連線
??????? this.task = (Calculator)this.getLastNonConfigurationInstance();
??????? if(this.task != null)
??????? {
??????????? Log.d(TAG, "reconnect with asyncTask");
??????????? this.task.setContext(this);
??????????? // 在 Activity 重起過程中已計算完畢,所以要呼叫 AsyncTask 來更新 UI
??????????? if(this.task.getStatus() == AsyncTask.Status.FINISHED)
??????????? {
??????????????? this.task.syncUI();
??????????? }
??????? }
??????? else
??????????? Log.d(TAG, "task is null");
???????
??????? findViewById(R.id.btnTest).setOnClickListener(calculatorClick);
}
???
??? @Override
??? public Object onRetainNonConfigurationInstance(){
?????? // 保留 Activity 與 AsyncTask 的關聯
??????? // 供 Activity 因故重起後,可以找回已在執行的 AsyncTask
??????? return this.task;
??? }
???
??? private View.OnClickListener calculatorClick = new View.OnClickListener() {
??????? public void onClick(View v) {
??????????? // TODO Auto-generated method stub
??????????? executeTask();
??????? }
??? };
???
??? public void executeTask() {
??????? Log.d(TAG, "Clicked");
??????? if(task != null)
??????? {
??????????? AsyncTask.Status status = task.getStatus();
??????????? Log.d(TAG, "Now status: " + status);
??????????? if(status != AsyncTask.Status.FINISHED)
??????????? {
????????????? //前一計算未完成,不可重起計算
??????????????? Log.d(TAG, "continue last calculating ...");
??????????????? return;
??????????? }
??????????? //前一計算已完成,可重起計算
??????????? Log.d(TAG, "completed last calculator");
??????? }
??????? Log.d(TAG, "new calculator");
??????? task = new Calculator(this);
??????? List<Integer> params = new ArrayList<Integer>();
??????? for(int i=0; i<10; i++)
??????????? params.add(i);
??????? // 只能呼叫一次 execute(...)
??????? task.execute(params);
??? }
}
?
执行后效果如下:
?[Android]异步处理AsynTask实例讲解
5、总结
  初次看到这个异步调用关系可能觉得很复杂,但其实熟悉了之后会发现这种结构很好用。这种结构将所有的线程通信都封装成回调函数,调用逻辑容易书写。
??? 尤其是在异步处理结束之后,有回调函数进行收尾处理。
??? 如果是使用Thread的run()方法,run()结束之后没有返回值。所以必须要自己建立通信机制。
??? 但是,其实使用Handler+Thread机制其实完全可以替代AsynTask的这种调用机制。只要将Handler对象传给Thread,就可以进行方便的异步处理。且这种MVC模式结构更加明显,方便管理。所以我觉得,使用asynTask还是Handler+Thread结构,个人喜好吧。
?
实际应用可以参考这篇博文:用AsyncTask从服务器下载文件并显示进度
  相关解决方案