必威体育Betway必威体育官网
当前位置:首页 > IT技术

AsyncTask

时间:2019-09-15 23:40:00来源:IT技术作者:seo实验室小编阅读:79次「手机版」
 

asynctask

参考:

Android AsyncTask完全解析,带你从源码的角度彻底理解

Android源码分析—带你认识不一样的AsyncTask

AsyncTask的缺陷和问题 

译文:Android中糟糕的AsyncTask(http://droidyue.com/blog/2014/11/08/bad-smell-of-asynctask-in-android/)

Android实战技巧:深入解析AsyncTask

AsyncTask的基本用法

首先来看一下AsyncTask的基本用法,由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

1. params

在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

2. progress

后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

3. Result

当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

因此,一个最简单的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, integer, Boolean> {  
    ……  
}  

这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

当然,目前我们自定义的DownloadTask还是一个空任务,并不能进行任何实际的操作,我们还需要去重写AsyncTask中的几个方法才能完成对任务的定制。经常需要去重写的方法有以下四个:

1. onPreExecute()

这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

2. doInBackground(Params...)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。

3. onProgressUpdate(Progress...)

当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

4. onPostExecute(Result)

当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

因此,一个比较完整的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  
    @Override  
    protected void onPreExecute() {  
        progressdialog.show();  
    }  
  
    @Override  
    protected Boolean doInBackground(Void... params) {  
        try {  
            while (true) {  
                int downloadPercent = doDownload();  
                publishProgress(downloadPercent);  
                if (downloadPercent >= 100) {  
                    break;  
                }  
            }  
        } catch (Exception e) {  
            return false;  
        }  
        return true;  
    }  
  
    @Override  
    protected void onProgressUpdate(Integer... values) {  
        progressDialog.setmessage("当前下载进度:" + values[0] + "%");  
    }  
  
    @Override  
    protected void onPostExecute(Boolean result) {  
        progressDialog.dismiss();  
        if (result) {  
            toast.maketext(context, "下载成功", Toast.LENGTH_SHORT).show();  
        } else {  
            Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();  
        }  
    }  
}  

这里我们模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:

new DownloadTask().execute();  

以上就是AsyncTask的基本用法,怎么样,是不是感觉在子线程和UI线程之间进行切换变得灵活了很多?我们并不需求去考虑什么异步消息处理机制,也不需要专门使用一个handler来发送和接收消息,只需要调用一下publishProgress()方法就可以轻松地从子线程切换到UI线程了。

注意事项

There are a few threading rules that must be followed for this class to work properly:

1. The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.

2. The task instance must be created on the UI thread.

execute(Params...) must be invoked on the UI thread.

3. Do not call onPreExecute()onPostExecute(Result)doInBackground(Params...)onProgressUpdate(Progress...)manually.

4.The task can be executed only once (an exception will be thrown if a second execution is attempted.)

AsyncTask的缺陷和问题 

Handler和 AsyncTask如何选择:

AsyncTask实现的原理和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:

简单,快捷

过程可控      

使用的缺点:

在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework.AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.

Handler异步实现的原理和适用的优缺点:

在Handler异步实现时,涉及到Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程)运行并生成Message-Looper获取Message并传递给HandlerHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:

结构清晰,功能定义明确

对于多个后台任务时,简单,清晰

使用的缺点:

在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)

综上所述:

数据简单且不费时使用AsyncTask:实现代码简单;

数据量多且复杂,使用handler+thread :相比较AsyncTask来说能更好的利用系统资源且高效

源码分析

/*
 * Copyright (C) 2008 The android open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by APPlicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import java.util.ArrayDeque;
import java.util.concurrent.blockingqueue;
import java.util.concurrent.Callable;
import java.util.concurrent.cancellationException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";

	//获取当前的cpu核心数
    private static final int CPU_COUNT = runtime.getRuntime().availableProcessors();
	//线程池核心容量
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
	//线程池最大容量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
	//过剩的空闲线程的存活时间
    private static final int KEEP_ALIVE = 1;
	//ThreadFactory 线程工厂,通过工厂方法newThread来获取新线程
    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());
        }
    };
	//静态阻塞式队列,用来存放待执行的任务,初始容量:128个
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 静态并发线程池,可以用来并行执行任务,尽管从3.0开始,AsyncTask默认是串行执行任务
	 * 但是我们仍然能构造出并行的AsyncTask
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 静态串行任务执行器,其内部实现了串行控制,
	 * 循环的取出一个个任务交给上述的并发线程池去执行
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
	//消息类型:发送结果
    private static final int MESSAGE_POST_RESULT = 0x1;
	//消息类型:更新进度
    private static final int MESSAGE_POST_PROGRESS = 0x2;
	/**静态Handler,用来发送上述两种通知,采用UI线程的Looper来处理消息
	 * 这就是为什么AsyncTask必须在UI线程调用,因为子线程
	 * 默认没有Looper无法创建下面的Handler,程序会直接Crash
	 */
    private static final InternalHandler sHandler = new InternalHandler();
	//默认任务执行器,被赋值为串行任务执行器,就是它,AsyncTask变成串行的了
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
	//如下两个变量我们先不要深究,不影响我们对整体逻辑的理解
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
	//任务的状态 默认为挂起,即等待执行,其类型标识为易变的(volatile)
    private volatile Status mStatus = Status.PENDING;
    //原子布尔型,支持高并发访问,标识任务是否被取消
    private final AtomicBoolean mCancelled = new AtomicBoolean();
	//原子布尔型,支持高并发访问,标识任务是否被执行过
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

	/*串行执行器的实现,我们要好好看看,它是怎么把并行转为串行的
	 *目前我们需要知道,asyncTask.execute(Params ...)实际上会调用
	 *SerialExecutor的execute方法,这一点后面再说明。也就是说:当你的asyncTask执行的时候,
	 *首先你的task会被加入到任务队列,然后排队,一个个执行
	 */
    private static class SerialExecutor implements Executor {
		//线性双向队列,用来存储所有的AsyncTask任务
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
		//当前正在执行的AsyncTask任务
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
			//将新的AsyncTask任务加入到双向队列中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
						//执行AsyncTask任务
                        r.run();
                    } finally {
						//当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话
						//这一点很明显体现了AsyncTask是串行执行任务的,总是一个任务执行完毕才会执行下一个任务
                        scheduleNext();
                    }
                }
            });
			//如果当前没有任务在执行,直接进入执行逻辑
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
			//从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUtor.execute(mActive);
            }
        }
    }

    /**
     * 任务的三种状态
     */
    public enum Status {
        /**
         * 任务等待执行
         */
        PENDING,
        /**
         * 任务正在执行
         */
        RUNNING,
        /**
         * 任务已经执行结束
         */
        FINISHED,
    }

    /** 隐藏API:在UI线程中调用,用来初始化Handler */
    public static void init() {
        sHandler.getLooper();
    }

    /** 隐藏API:为AsyncTask设置默认执行器 */
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadpriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(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);
                }
            }
        };
    }

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
	//doInBackground执行完毕,发送消息
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    /**
     * 返回任务的状态
     */
    public final Status getStatus() {
        return mStatus;
    }

    /**
	 * 这个方法是我们必须要重写的,用来做后台计算
	 * 所在线程:后台线程
     */
    protected abstract Result doInBackground(Params... params);

    /**
	 * 在doInBackground之前调用,用来做初始化工作
	 * 所在线程:UI线程
     */
    protected void onPreExecute() {
    }

    /**
	 * 在doInBackground之后调用,用来接受后台计算结果更新UI
	 * 所在线程:UI线程
     */
    protected void onPostExecute(Result result) {
    }

    /**
     * Runs on the UI thread after {@link #publishProgress} is invoked.
     /**
	 * 在publishProgress之后调用,用来更新计算进度
	 * 所在线程:UI线程
     */
    protected void onProgressUpdate(Progress... values) {
    }

     /**
	 * cancel被调用并且doInBackground执行结束,会调用onCancelled,表示任务被取消
	 * 这个时候onPostExecute不会再被调用,二者是互斥的,分别表示任务取消和任务执行完成
	 * 所在线程:UI线程
     */
    @SuppressWarnings({"Unusedparameters"})
    protected void onCancelled(Result result) {
        onCancelled();
    }    
    
    protected void onCancelled() {
    }

    public final boolean isCancelled() {
        return mCancelled.get();
    }

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

    /**
     * 这个方法如何执行和系统版本有关,在AsyncTask的使用规则里已经说明,如果你真的想使用并行AsyncTask,
	 * 也是可以的,只要稍作修改
	 * 必须在UI线程调用此方法
     */
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
		//串行执行
        return executeOnExecutor(sDefaultExecutor, params);
		//如果我们想并行执行,这样改就行了,当然这个方法我们没法改
		//return executeOnExecutor(THREAD_POOL_EXECUTOR, params);
    }

    /**
     * 通过这个方法我们可以自定义AsyncTask的执行方式,串行or并行,甚至可以采用自己的Executor
	 * 为了实现并行,我们可以在外部这么用AsyncTask:
	 * asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params);
	 * 必须在UI线程调用此方法
     */
    public final AsyncTask<Params, Progress, Result> 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会最先执行
        onPreExecute();

        mWorker.mParams = params;
		//然后后台计算#doInBackground才真正开始
        exec.execute(mFuture);
		//接着会有#onProgressUpdate被调用,最后是#onPostExecute

        return this;
    }

    /**
     * 这是AsyncTask提供的一个静态方法,方便我们直接执行一个runnable
     */
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

    /**
	 * 打印后台计算进度,onProgressUpdate会被调用
     */
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

	//任务结束的时候会进行判断,如果任务没有被取消,则onPostExecute会被调用
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

	//AsyncTask内部Handler,用来发送后台计算进度更新消息和计算完成消息
    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;
            }
        }
    }

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

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

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
}

相关阅读

asyncTask详解

https://blog.csdn.net/gdutxiaoxu/article/details/57409326 api AsyncTask是个抽象类,需要子类继承,然后调用execute()方法,继

分享到:

栏目导航

推荐阅读

热门阅读