Android中AsyncTask使用教程及源码分析

        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

       下面主要介绍一下AsyncTaskAsyncTask是一个泛型的抽象类,属于轻量级的异步任务,其本质是Hanler+Thread,它可以在线程池中执行后台任务,然后把执行的进度和最终结果通过Handler发送消息的方式通知给主线程并在主线程中更新UI界面。要使用的话通常需要被继承,继承时需要指定三个泛型参数:

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文件中授予网络访问权限:

 

	
	
我们来看下运行时的界面:

Android中AsyncTask使用教程及源码分析_第1张图片

Android中AsyncTask使用教程及源码分析_第2张图片

 

例程源码(github)

       上面简单地介绍了一下AsyncTask的使用过程,我们在前面简单地提到过:AsyncTask是一个泛型的抽象类,属于轻量级的异步任务,其本质是Hanler+Thread,它可以在线程池中执行后台任务,然后把执行的进度和最终结果通过Handler发送消息的方式通知给主线程并在主线程中更新UI界面。下面我们以Android 4.3(API 18)版本中的AsyncTask源码来进行分析。

       首先来看下AsyncTask的大纲视图:

Android中AsyncTask使用教程及源码分析_第3张图片

       我们可以看到几个比较重要的方法都在里面,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类型变量设置传入参数,查看WorkerRunnable源码可知

 

    private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
    }

WorkerRunnable实现了Callable接口。exec.execute(mFuture)启动目标参数为FutureTask类型的线程开始执行。

 

       这里有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(this, values)).sendToTarget();
        }
    }


       在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,含注释)

 

你可能感兴趣的:(Android)