Android UI主线程通常用于处理屏幕绘图、按键事件响应、Touch触摸事件响应,而一些耗时的操作比如请求网络等不能放在UI主线程中,否则会阻塞UI主线程,发生ANR。因此为了避免UI主线程阻塞失去响应,Android建议将一些耗时操作放在新线程中进行,但是有的时候在新线程中进行的耗时操作需要动态的更新UI界面,而新线程不允许直接更新UI界面。为了解决新线程不能直接更新UI界面的问题,Android为我们提供了以下几种解决方案:
●使用Handler实现线程之间的通信
●Activity.runOnUiThread(Runnable action)
●View.post(Runnable action)
●View.postDelayed(Runnable action, long delayMillis)
●AsyncTask
下面主要介绍一下AsyncTask
Params:启动任务执行时需要输入的参数类型。
Progress:后台任务完成的进度值类型。
Result:后台任务完成后返回的结果类型。
使用AsyncTask需要以下步骤:
1、定义AsyncTask的实现子类,并指定泛型参数类型,如果某个泛型参数不需要指定类型,则将其指定为Void。
2、根据需要重写AsyncTask中的4个核心回调方法:
protected void onPreExecute() :该方法在主线程中执行,在异步任务执行之前此方法会被调用,一般用于做一些准备工作。
protected abstract Result doInBackground(Params... params):在线程池中执行,此方法用于执行异步任务,该方法传入参数类型为AsyncTask泛型Params,返回结果为AsyncTask泛型Result。在此方法中可以调用publishProgress来更新进度,而publishProgress会通过Handler对象发送消息调用onProgressUpdate方法更新UI界面中的任务执行的进度。注意:publishProgress被final修饰,不能被重写!
protected void onProgressUpdate(Progress... values):在主线程中执行,后台异步任务执行进度发生变化时publishProgress方法会回调该方法。
protected void onPostExecute(Result result):在主线程中执行,当异步任务执行完成后此方法会被回调。其中result是后台任务的返回值。
以上是AsyncTask中4个核心方法,当然AsyncTask中还有一些回调方法可根据需要进行重写,比如protected void onCancelled(),用于当AsyncTask对象执行AsyncTask.cancel方法时回调该方法。
3、在UI主线程中调用AsyncTask.execute启动异步任务的执行。
当然AsyncTask在使用过程中有一些限制条件需要注意,主要有以下几点:
1、AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程。
2、AsyncTask的对象必须在主线程中创建。
3、AsyncTask.execute必须在主线程中调用。
4、不要在程序中主动去直接调用onPreExecute、doInBackground、onProgressUpdate、onPostExecute这4方法。
5、一个AsyncTask对象只能执行一次,即只能调用一次AsyncTask.execute方法,否则就会报运行时的异常。
下面给出要给AsyncTask例程,具体源码如下:
package com.fxj.asynctasktest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.DefaultClientConnection;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
Button executeBtn;
Button cancelBtn;
TextView textView;
ProgressBar progressBar;
MyAsyncTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.executeBtn=(Button) findViewById(R.id.executeBtn);
this.cancelBtn=(Button) findViewById(R.id.cancelBtn);
this.textView=(TextView) findViewById(R.id.textView);
this.progressBar=(ProgressBar) findViewById(R.id.progressBar);
this.cancelBtn.setEnabled(false);
this.executeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
/*AsyncTask异步任务只能执行一次,为了每次点击该按钮生效可在此创建AsyncTask对象*/
task=new MyAsyncTask();
task.execute("http://www.ifeng.com/");
executeBtn.setEnabled(false);
cancelBtn.setEnabled(true);
}
});
this.cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
task.cancel(true);
}
});
}
public class MyAsyncTask extends AsyncTask
{
private static final String tag="MyAsyncTask";
/*在主线程中执行,在异步任务执行之前此方法会被调用,一般用于做一些准备工作*/
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
Log.i(tag,"onPreExecute");
textView.setText("loading……");
super.onPreExecute();
}
/*在线程池中执行异步任务,注意该方法传入参数类型为AsyncTash泛型Params,返回结果为AsyncTash泛型Result,
* 在此方法中可以调用publishProgress来更新进度,而publishProgress会通过Handler对象发送消息调用
* onProgressUpdate方法
* */
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
Log.i(tag,"doInBackground");
try {
/*创建HttpClient对象*/
HttpClient client=new DefaultHttpClient();
/*创建HttpGet请求*/
HttpGet get=new HttpGet(params[0]);
/*调用HttpClient.execute发送请求,HttpClient.execute返回一个HttpResponse对象*/
HttpResponse response=client.execute(get);
if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
/*从HttpResponse对象中调用HttpResponse.getEntity方法返回HttpEntity
* 对象,HttpEntity对象包含了服务器响应的内容
* */
HttpEntity entity=response.getEntity();
/*拿到服务器响应内容HttpEntity对象对象的InputStream输入流*/
InputStream is=entity.getContent();
long total=entity.getContentLength();
Log.i(tag,"doInBackground,服务器响应内容输入流总的长度total="+total);
byte[] buffer=new byte[1024];
ByteArrayOutputStream byteArrayOS=new ByteArrayOutputStream();
int length=-1;
long count=0;
while((length=is.read(buffer))!=-1){
byteArrayOS.write(buffer,0,length);
count+=length;
/*更新进度*/
int value=(int)(count*100/total);
Log.i(tag,"doInBackground,进度值value="+value);
publishProgress(value);
// /*线程休眠0.5秒*/
// Thread.sleep(500);
}
return new String(byteArrayOS.toByteArray(),"utf-8");
}
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (/*Interrupted*/Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*在主线程中执行,后台异步任务执行进度发生变化时回调该方法*/
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
Log.i(tag,"onProgressUpdate,当前进度值:"+values[0]);
progressBar.setProgress(values[0]);
super.onProgressUpdate(values);
}
/*在主线程中执行,当异步任务执行完成后此方法会被回调*/
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
Log.i(tag,"onPostExecute");
textView.setText(result);
executeBtn.setEnabled(true);
cancelBtn.setEnabled(false);
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
textView.setText(null);
progressBar.setProgress(0);
executeBtn.setEnabled(true);
cancelBtn.setEnabled(false);
super.onCancelled();
}
}
}
对应布局文件如下:
在以上源代码中,AsyncTask对象是在executeBtn点击事件中创建的,这样就确保了每次点击“执行”executeBtn按钮时重写创建了一个AsyncTask对象,满足AsyncTask对象只能执行一次的要求。在doInBackground方法中使用HttpClient发送请求、接收响应。
使用HttpClient发送请求、接收响应步骤如下:
1、创建HttpClient对象。
2、如果发送GET请求,则创建HttpGet对象;如果发送POST请求,则创建HttpPost对象。
3、如果需要发哦是哪个请求参数,可以调用HttpGet、HttpPost共同的setParams(HttpParams params)方法来添加请求参数;对于HttpPost对象而言,也可以调用setEntity(HttpEntity entity)方法来设置请求参数。
4、调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法返回一个HttpResponse。
5、调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
当然,因为还有访问网络,所以还需要在AndroidManifest.xml文件中授予网络访问权限:
我们来看下运行时的界面:
例程源码(github)
上面简单地介绍了一下AsyncTask的使用过程,我们在前面简单地提到过:AsyncTask
首先来看下AsyncTask的大纲视图:
我们可以看到几个比较重要的方法都在里面,doInBackground是一个抽象方法,AsyncTask子类必须实现的该方法。onPreExecute、onProgressUpdate、onPostExecute、onCancelled这几个方法的方法体为空,在实际的开发中根据需要重写。publishProgress方法被final修饰,不能被AsyncTask的子类重写,通常该方法在doInBackground被调用。
首先我们从AsyncTask.execute(Params... params)这个方法作为入口来分析AsyncTask的执行流程,
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
在该方法中实际调用的是executeOnExecutor(Executor exec,Params... params)方法,在execute(Params... params)方法中调用executeOnExecutor时传入了2个参数,其中Executor线程池类型参数是sDefaultExecutor,追踪这个sDefaultExecutor参数,发现这样一条复制语句:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
因此追踪sDefaultExecutor这个参数就要看SERIAL_EXECUTOR这个参数,通过以下代码片段
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
发现SERIAL_EXECUTOR参数是SerialExecutor类型,SerialExecutor是AsyncTask的一个静态的内部类,通过分析SerialExecutor静态内部类源码可知SERIAL_EXECUTOR是一个线程池用于任务的排队,而在mTasks是一个ArrayDeque队里,向mTasks对尾插入Runnable任务,而在同步方法scheduleNext中将从mTasks中取到的任务赋值给mActive并交个THREAD_POOL_EXECUTOR线程池对象执行。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以发现在这里有两个线程池对象SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR,其中SERIAL_EXECUTOR用于任务队列的排队,而THREAD_POOL_EXECUTOR才是真正地执行任务。
分析完AsyncTask.execute(Params... params)方法中调用executeOnExecutor方法时传入的参数sDefaultExecutor之后,我们再去看看AsyncTask.executeOnExecutor(Executor exec, Params... params)内部的实现。
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
mStatus是一个Status枚举类型变量,Status枚举类型定义如下:
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
Status枚举类型定义了3中状态,Status.PENDING表示待定状态,Status.RUNNING表示运行状态,Status.FINISHED表示结束状态,Status中几种枚举类型只能被定义一次,这从而也说明了AsyncTask对象只能被执行一次,否则会报运行时错误。在mStatus定义处会发现其初值为Status.PENDING,表示待定状态。
private volatile Status mStatus = Status.PENDING;
在executeOnExecutor方法体中如果当前mStatus任务状态不是Status.PENDING话则当前任务要么处于正在运行状态要么处于结束状态,无论处于哪种状态都会抛出异常从而保证了当前AsyncTask对象只能被执行一次。如果mStatus任务状态为Status.PENDING初始待定状态,则继续向后执行,赋值语句mStatus = Status.RUNNING将任务状态切换为正在运行状态,然后调用onPreExecute()进行任务执行之前的一些初始化准备工作。紧接着mWorker.mParams = params是给WorkerRunnable
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
WorkerRunnable
这里有2个变量mWorker和mFuture需要注意,mWorker变量是实现了Callable接口的实例对象,mFuture是FutureTask类型变量,这2个变量在AsyncTask构造函数中完成初始化,具体代码如下:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
从AsyncTask构造函数代码可以发现,在call方法中将任务级别设置为后台级别,然后调用AsyncTask.doInBackground方法,并将返回的Result结果传递给Result postResult(Result result)方法,在
Result postResult(Result result)方法中
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
通过Handler子类InternalHandler
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
finish方法代码如下:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在InternalHandler代码中可以知道发送MESSAGE_POST_PROGRESS消息可以调用onProgressUpdate方法,那么何时才会发送MESSAGE_POST_PROGRESS消息呢?追踪发现,当调用publishProgress方法时会发送此消息。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
在sHandler对象发送消息时有一个AsyncTaskResult类型的对象,查看其代码可以知道在AsyncTaskResult构造函数
private static class AsyncTaskResult {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
中需要传入2个参数,第1个参数为AsyncTask对象,第2个参数为携带的数据。
AsyncTask源码(版本:Android4.3,不含注释)AsyncTask源码(版本:Android4.3,含注释)