深入理解异步任务AsyncTask

                                                           异步任务AsyncTask

        1、Android 的UI线程主要负责处理用户的按键事件、用户触屏事件及品目绘图事件等,不能处理耗时操作,否则UI界面会停止响应。(Android UI想成超过20秒就会出现ANR,但是让用户等6秒钟就会非常反感,所以这里就不要记时间了)

          

        2、为了避免失去响应,我通常是新开启个线程去处理耗时任务,但是问题又来了,我们往往需要在执行完耗时任务时需要更新UI,例如在网上获取个网页,显示在Android的UI中,这里因为是新的线程,就不能直接修改主线程里的UI所以我们一种方法是通过Handler.发送消息去及时更新,Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制,但是还有另一种较好的方法就是用AsyncTask异步任务,此方法较为轻量级。

        3、AsyncTask是一个抽象类,通常用于被继承,继承时需要制定三个范型参数。

                 public class AsyncTask<Params,Progress,Result>

                  1)Params:启动任务执行的输入参数。

                  2)Progress:后台完成进度值的类型。

                  3)Result:后台执行任务完成后返回结果的类型。

        4、使用步骤

               1)创建AsyncTask子类,并为三个泛型参数指定类型。如果某个泛型参数不需要指定类型,可将它指定为Void。

               2)根据需要实现对应的方法。提供的操作方法如下。

                           ①doInBackground(Params...):重写该方法就是后台线程将要完成的任务,这里可以调用publishProgress(Progress...valuers)方法更新执行的进度。

                           ②onProgressUpdate(Progress... values):在doInBackground()方法中调用publishProgress()方法更新任务的执行进度后,将会触发此方法,直接将进度信息更新到UI组件上。

                           ③onPreExecute():该方法将在执行后台耗时操作前调用。因此他是完成一些初始化操作的,也就是准备工作,比如在界面上显示进度条等等。

                           ④onPostExecute(Result result):当doInBackground()完成后,系统会自动调用onPostExecute()方法,并将doInBackground()方法的返回值传给该方法。此方法用来将最后结果显示的UI中

                            ⑤onCancelled()方法:当task调用cancel(True)时,被调用,是编写取消任务对应UI的更改操作

                      3)使用AsyncTask时必须遵守如下原则。

                           ① 必须在UI线程中创建AsyncTask的实例。

                           ②必须在Ui线程中调用AsyncTask的execute方法。

                           ④AsyncTask的onPreExecute().onPostExecute(Result result),doInBackground(Params...params),onProgressUpdate(Progress....values)方法不应该由程序员代码调用,而是由Android系统负责调用。

                           ⑤每个AsyncTask只能被执行一次,多次调用将会引发异常,就是一个任务实例只能执行一次,如果执行第二次将会抛出异常。

                执行的顺序,onPreExecute()---->>doInBackground()--->>内部调用publishProgress(Progress...values)--->>结束触发onProgressUpdate()--->>onPostExecute()

                如果中途取消,执行顺序

                                    onPreExecute()---->>doInBackground()--->>内部调用publishProgress(Progress...values)--->>结束触发onProgressUpdate()--->onCancelled()(这个方法是程序员调用cancel时才触发的,和其他不一样)

               下面Demo,


                新建一个AsyncTaskTest 类,

package org.crazyit.handler;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.view.View;
import android.widget.TextView;

public class AsyncTaskTest extends Activity
{
	private TextView show;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		show = (TextView) findViewById(R.id.show);
	}

	// 重写该方法,为界面的按钮提供事件响应方法
	public void download(View source) throws MalformedURLException
	{
		DownTask task = new DownTask(this);
		task.execute(new URL("http://www.crazyit.org/ethos.php"));
	}

	class DownTask extends AsyncTask<URL, Integer, String>
	{
		// 可变长的输入参数,与AsyncTask.exucute()对应
		ProgressDialog pdialog;
		// 定义记录已经读取行的数量
		int hasRead = 0;
		Context mContext;

		public DownTask(Context ctx)
		{
			mContext = ctx;
		}

		@Override
		protected String doInBackground(URL... params)
		{
			StringBuilder sb = new StringBuilder();
			try
			{
				URLConnection conn = params[0].openConnection();
				// 打开conn连接对应的输入流,并将它包装成BufferedReader
				BufferedReader br = new BufferedReader(
					new InputStreamReader(conn.getInputStream()
					, "utf-8"));
				String line = null;
				while ((line = br.readLine()) != null)
				{
					sb.append(line + "\n");
					hasRead++; 
					publishProgress(hasRead);
				}
				return sb.toString();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onPostExecute(String result)
		{
			// 返回HTML页面的内容
			show.setText(result);
			pdialog.dismiss();
		}

		@Override
		protected void onPreExecute()
		{
			pdialog = new ProgressDialog(mContext);
			// 设置对话框的标题
			pdialog.setTitle("任务正在执行中");
			// 设置对话框 显示的内容
			pdialog.setMessage("任务正在执行中,敬请等待...");
			// 设置对话框不能用“取消”按钮关闭
			pdialog.setCancelable(false);
			// 设置该进度条的最大进度值
			pdialog.setMax(202);
			// 设置对话框的进度条风格
			pdialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			// 设置对话框的进度条是否显示进度
			pdialog.setIndeterminate(false);
			pdialog.show();
		}

		@Override
		protected void onProgressUpdate(Integer... values)
		{
			// 更新进度
			show.setText("已经读取了【" + values[0] + "】行!");
			pdialog.setProgress(values[0]);
		}
	}
}

                  main.xml

<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	tools:context=".AsyncTaskTest">
	
	<TextView
		android:id="@+id/show"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:textSize="14dp" />
	<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_alignParentBottom="true"
		android:layout_centerHorizontal="true"
		android:text="下载"
		android:onClick="download" />
	
</RelativeLayout>


AndroidManifest.xml ,添加权限


<?xml version="1.0" encoding="utf-8"?>
<manifest
	xmlns:android="http://schemas.android.com/apk/res/android"
	package="org.crazyit.handler"
	android:versionCode="1"
	android:versionName="1.0">
	<uses-sdk
		android:minSdkVersion="10"
		android:targetSdkVersion="17" />
	<uses-permission android:name="android.permission.INTERNET"/>
	<application
		android:allowBackup="true"
		android:icon="@drawable/ic_launcher"
		android:label="@string/app_name">
		<activity
			android:name=".AsyncTaskTest"
			android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
</manifest>

 

                有些朋友也许会有疑惑,AsyncTask内部是怎么执行的呢,它执行的过程跟我们使用Handler又有什么区别呢?答案是:AsyncTask是对Thread+Handler良好的封装,在android.os.AsyncTask代码里仍然可以看到Thread和Handler的踪迹。下面就向大家详细介绍一下AsyncTask的执行原理。

           看看这个图,发现 只有doInBackground()是抽象方法,其余几个是空方法,我们需要的时候可以选择性的覆写它们;publishProgress(Progress... values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params... params)中调用此方法;


                  另外,我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类代码段如下:

                          

        //初始状态
	private volatile Status mStatus = Status.PENDING;
	
	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,
    }

/**
     * Returns the current status of this task.
     *
     * @return The current status.
     */
    public final Status getStatus() {
        return mStatus;
    }

可以看到,AsyncTask的初始状态为PENDING,代表待定状态,RUNNING代表执行状态,FINISHED代表结束状态,这几种状态在AsyncTask一次生命周期内的很多地方被使用,非常重要。

介绍完大纲视图相关内容之后,接下来,我们会从execute(Params... params)作为入口,重点分析一下AsyncTask的执行流程,我们来看一下execute(Params... params)方法的代码段:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
					//如果该任务正在被执行则抛出异常
					//值得一提的是,在调用cancel取消任务后,状态仍未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)");
            }
        }
		
		//改变状态为RUNNING
        mStatus = Status.RUNNING;

		//调用onPreExecute方法
        onPreExecute();

        mWorker.mParams = params;
        sExecutor.execute(mFuture);

        return this;
    }

             代码中涉及到三个陌生的变量:mWorker、sExecutor、mFuture,我们也会看一下他们的真面目:

关于sExecutor,它是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行。代码如下

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 128;
    private static final int KEEP_ALIVE = 10;

	//新建一个队列用来存放线程
    private static final BlockingQueue<Runnable> sWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);
	//新建一个线程工厂
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
		//新建一个线程
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
	//新建一个线程池执行器,用于管理线程的执行
    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

mWorker实际上是AsyncTask的一个的抽象内部类的实现对象实例,它实现了Callable<Result>接口中的call()方法,代码如下

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
而mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息:

 

/**
 * A cancellable asynchronous computation.
 * ...
 */
public class FutureTask<V> implements RunnableFuture<V> 
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

可以看到FutureTask是一个可以中途取消的用于异步计算的类。

下面是mWorker和mFuture实例在AsyncTask中的体现:

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
	
	public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            //call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法
			public Result call() throws Exception {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return doInBackground(mParams);
            }
        };

		//在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                Message message;
                Result result = null;

                try {
                    result = 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) {
					//发送取消任务的消息
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
                    message.sendToTarget();
                    return;
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }

				//发送显示结果的消息
                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                        new AsyncTaskResult<Result>(AsyncTask.this, result));
                message.sendToTarget();
            }
        };
    }

 我们看到上面的代码中,mFuture实例对象的done()方法中,如果捕捉到了CancellationException类型的异常,则发送一条“MESSAGE_POST_CANCEL”的消息;如果顺利执行,则发送一条“MESSAGE_POST_RESULT”的消息,而消息都与一个sHandler对象关联。这个sHandler实例实际上是AsyncTask内部类InternalHandler的实例,而InternalHandler正是继承了Handler,下面我们来分析一下它的代码:


    private static final int MESSAGE_POST_RESULT = 0x1;	//显示结果
    private static final int MESSAGE_POST_PROGRESS = 0x2;	//更新进度
    private static final int MESSAGE_POST_CANCEL = 0x3;	//取消任务

    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
					//调用AsyncTask.finish方法
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //调用AsyncTask.onProgressUpdate方法
					result.mTask.onProgressUpdate(result.mData);
                    break;
                case MESSAGE_POST_CANCEL:
					//调用AsyncTask.onCancelled方法
                    result.mTask.onCancelled();
                    break;
            }
        }
    }
我们看到,在处理消息时,遇到“MESSAGE_POST_RESULT”时,它会调用AsyncTask中的finish()方法,我们来看一下finish()方法的定义:
private void finish(Result result) {
        if (isCancelled()) result = null;
        onPostExecute(result);	//调用onPostExecute显示结果
        mStatus = Status.FINISHED;	//改变状态为FINISHED
 }

原来finish()方法是负责调用onPostExecute(Result result)方法显示结果并改变任务状态的啊。

另外,在mFuture对象的done()方法里,构建一个消息时,这个消息包含了一个AsyncTaskResult类型的对象,然后在sHandler实例对象的handleMessage(Message msg)方法里,

AsyncTaskResult result = (AsyncTaskResult) msg.obj;


AsyncTask的一个内部类,是用来包装执行结果的一个类

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;
        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }


总结以下,当我们调用execute(Params... params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个FutureTask任务,这个过程中doInBackground(Params... params)将被调用,如果被开发者覆写的doInBackground(Params... params)方法中调用了publishProgress(Progress... values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress... values)方法将被调用;如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。

经过上面的介绍,相信朋友们都已经认识到AsyncTask的本质了,它对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率,希望朋友们能多多体会一下。








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值