Android的UI线程主要负责处理用户的按键事件、用户触屏事件及屏幕绘图事件等,对于其他的操作尽量不要在UI线程中实现,因为这些操作很有可能会阻塞UI线程,导致UI界面停止响应,从而降低了用户的体验,严重时出现ANR(无响应)。为了避免UI线程失去响应的问题,Android建议将耗时操作移动到新线程中完成,但新线程也可能需要动态更新UI组件(比如需要从网上获取一个网页,然后在TextView中将其源代码显示出来,此时就应该将连接网络,获取网络数据的操作放在新线程中完成.)为此Android封装了一个简单实用的异步任务类AsyncTask来负责耗时任务的 处理,同时方便后台线程和UI线程进行通讯。
先来看看AsyncTask的定义:
public abstract class AsyncTask<Params, Progress, Result> {
//A client will call this method
AsyncTask<Params,Progress,Result>
excute(Params...Params);
//Do your work here
//Frequently trigger onProgressUpdate()
Result doInBackGround(Params...params);
//Callback: After the work is complete
void onPostExecute(Result result);
//Callback:As the work is progressing
void OnProgressUpdate(Progress...progressValueArray);}
三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。
1、excute方法的参数类型:在扩展AsynTask时需要指出要传递给excute方法的参数类型,假设类型为string,那么调用时要求各个字符串由逗号隔开
2、onProgressUpdate方法的参数类型:报告进度时向调用者回传的数值类型,支持数组。满足在异步任务中存在多个子任务的场景。
3、onPostExecute方法的参数类型:异步任务执行最终的返回结果类型。
2.Async类方法
(1)onPreExecute():
功能:做一些任务执行前的初始化工作,如在界面上展示一个进度条等
执行时机:执行实际工作前被UI线程调用。
(2)doInBackground(Params…values):
功能:负责执行比较耗时的处理工作,并通过publishProgress()将任务处理进度通知至UI线程。子类必须实现该方法。
执行时机:onPreExcute()函数之后被调用,在工作线程被执行。
(3)onProgressUpdate(Progress…values):
功能:定期的向UI线程反馈任务执行的进度。
执行时机:运行在UI线程。
(4)onPostExecute(Result):
功能:将工作线程的执行结果返回给UI线程。
执行时机:doInBackground()函数的执行结果返回给UI线程。运行在UI线程。
(5) onCancelled():
功能:取消后台工作线程。在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。
执行时机:UI线程调用。
注意:
另外,使用AsyncTask类需要遵守的以下准则:
(1)AsyncTask的实例必须在UI线程中创建;
(2)execute(Params…)方法必须在UI线程中调用;
(3)禁止调用onPreExecute(), onPostExecute(Result),doInBackground(Params…),onProgressUpdate(Progress…)方法;
(4)每个AsynTask只允许被执行一次,多次调用时将会出现异常。
3、AsyncTask用法实战
package com.example.AsyncTask;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
//import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.View;
import android.util.Log;
import android.os.AsyncTask;
public class MyActivity extends Activity {
private static final String TAG = "ASYNC_TASK";
private Button excute;
private Button cancle;
//private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
excute = (Button)findViewById(R.id.execute);
//excute.setOnClickListener(new View.OnClickListener(){
excute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
//注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
//mTask = new MyTask();
//mTask.execute("http://www.baidu.com");
MyTask tmpTask = new MyTask();
tmpTask.execute("nothing");
excute.setEnabled(false);
//cancel.setEnabled(true);
}
});
textView = (TextView) findViewById(R.id.text_view);
}
private class MyTask extends AsyncTask<String,Integer,Integer> {
@Override
protected void onPreExecute(){
Log.i(TAG,"OnPreExcute called");
textView.setText("loading...");
}
@Override
protected Integer doInBackground(String... param){
Log.i(TAG, "doInBackground called");
try{
for (int i=0;i<3;++i) {
Thread.currentThread().sleep(2000);
publishProgress(i);
}
}catch (InterruptedException e){
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... progress){
Integer i = progress[0];
textView.setText("load progress" + i);
}
@Override
protected void onPostExecute(Integer result) {
textView.setText("load sucessful");
}
}
}
Java开发与c++开发有很大差异,在上述代码中我故意将task设置为局部变量,而程序仍然可以正常工作,可见ava对象不具备和基本类型一样的生命周期,在对象超出作用域后,系统并未对对象进行销毁
当你用new创建一个Java对象时,它可以存活于作用域之外。所以假如你有下面的代码:
{
String s = new String(‘a string’);
} // End of scope
引用s在作用域终点就消失了。然而,s指向的String对象仍继续占据内存空间。在这一小段代码中,我们似乎无法再访问这个对象,因为对它唯一的引用已超出了作用域的范围。
事实证明,由new创建的对象,只要你需要,就会一直保留下去。这样,许多C++编程问题在Java中就完全消失了。在C++中,最难的问题似乎在于:程序员并不能从语言本身中获得任何帮助,以确保在需要调用对象时,该对象仍可用。更重要的是:在C++中,一旦使用完对象后,你必须确保要销毁对象。
这样便带来一个有趣的问题。如果Java让对象继续存在,那么靠什么才能防止这些对象填满内存空间,进而阻塞你的程序呢?这正是C++里可能会发生的问题。则正是Java神奇之所在。Java有一个“ 垃圾回收器”,用来监视用new创建的所有对象,并辨别那些不会再被引用的对象。随后,释放这些对象的内存空间,以便供其它新的对象使用。也就是说,你根本不必担心内存回收的问题。你只需要创建对象,一旦不再需要,它们就会自行消失。这样做就消除了这类编程问题:即所谓的“内存溢出”,即由于程序员忘记释放内存而产生的问题。
3、生命周期与AsyncTask
当由异步任务启动的进度对话框可见时,用户单击back按钮,会出现什么情况呢?按照Android的指导方针,这个操作会取消对话框。然而如果没有取消异步任务,那么它会继续执行,所以需要在捕捉对话框的oncacel事件,取消异步任务。
如果用户不使用进度对话框,而使用其它进度展示方式,又会出现什么情况?如果用户通过back或者home键离开activity,又会出现什么情况?在这两种情况下我们都无法确定用户何时返回,这个时候正确的做法是确认Activity的生命周期,然后相应的处理异步任务。
总之,异步任务需要充分意识到Activity的生命周期状态,并在何时的时机处理异步任务。