AsyncTask你真的用对了吗?

前言

在之前的文章深入探究了Handler,《从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)》我们知道了Android的消息机制主要靠Handler来实现,但是在Handler的使用中,忽略内存泄露的问题,不管是代码量还是理解程度上都显得有点不尽人意,所以Google官方帮我们在Handler的基础上封装出了AsyncTask。但是在使用AsyncTask的时候有很多细节需要注意,它的优点到底体现在哪里?还是来看看源码一探究竟。

怎么使用

来一段平常简单使用AsyncTask来异步操作UI线程的情况,首先新建一个类继承AsyncTask,构造函数传入我们要操作的组件(ProgressBarTextView

class MAsyncTask extends AsyncTask<Void, Integer, String>{
        private ProgressBar mProgressBar;
        private TextView mTextView;

        public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
            this.mProgressBar = mProgressBar;
            this.mTextView = mTextView;
        }

        @Override
        protected void onPreExecute() {
            mTextView.setText("开始执行");
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(Void... params) {
            for(int i = 0; i <= 100; i++){
                publishProgress(i);//此行代码对应下面onProgressUpdate方法
                try {
                    Thread.sleep(100);//耗时操作,如网络请求
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "执行完毕";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgressBar.setProgress(values[0]);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(String s) {
            mTextView.setText(s);
            super.onPostExecute(s);
        }
    }

在Activity中创建我们新建的MAsyncTask实例并且执行(无关代码省略):

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
        asyncTask.execute();//开始执行
        ...
    }
}

看看原理

在上面的代码,我们开了个单一的线程来执行了一个简单的异步更新UI的操作(哈哈,可能会觉得AsyncTask有些大材小用了哈),现在来看看AsyncTask具体是怎么实现的,先从构造方法开始:

public abstract class AsyncTask<Params, Progress, Result> 

AsyncTask为抽象类,并且有三个泛型,我觉得这三个泛型是很多使用者不懂的根源:

  • params:参数,在execute() 传入,可变长参数,跟doInBackground(Void… params) 这里的params类型一致,我这里没有传参数,所以可以将这个泛型设置为Void
  • Progress:执行的进度,跟onProgressUpdate(Integer… values)values的类型一致,一般情况为Integer
  • Result:返回值,跟String doInBackground 返回的参数类型一致,且跟onPostExecute(String s)s参数一致,在耗时操作执行完毕调用。我这里执行完毕返回了个字符串,所以为String

看了这三个泛型,我们就基本上了解了AsyncTask的执行过程,主要就是上面代码重写的那几个方法,现在来仔细看,首先在继承AsyncTask时有个抽象方法必须重写:

@WorkerThread
protected abstract Result doInBackground(Params... params);

顾名思义,这个方法是在后台执行,也就是在子线程中执行,需要子类来实现,在这个方法里面我们可以调用publishProgress来发送进度给UI线程,并且在onProgressUpdate方法中接收。

根据调用顺序,我们一般会重写这几个方法:

//在doInBackground之前调用,在UI线程内执行
@MainThread
protected void onPreExecute() {
}
//在执行中,且在调用publishProgress方法时,在UI线程内执行,用于更新进度
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在doInBackground之后调用,在UI线程内执行
@MainThread
protected void onPostExecute(Result result) {
}

我们来看看这个publishProgress方法是怎么来调用onProgressUpdate方法的:

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

使用obtainMessage是避免重复创建消息,调用了getHandler()然后发送消息,这里是一个单例

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

返回了一个InternalHandler

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @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;
            }
        }
    }

在判断消息为MESSAGE_POST_PROGRESS后我们发现其实内部就是调用了Handler来实现这一切,包括执行结束时调用finish方法,这个我们后面再说。从头来看一下AsyncTask的执行过程,来到execute方法:

/**
This method must be invoked on the UI thread.(这行注释为Google官方注释)
*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

注意!此方法必须在UI线程调用,这里就不做测试了。在这里又调用executeOnExecutor:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    ......

    onPreExecute();

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

    return this;
}

我们发现在UI线程先调用了onPreExecute(),将传入的参数赋值给mWorker.mParams,然后调用了参数exec的execute方法,并且将mFuture作为参数传入,这里就设计到了三个对象:sDefaultExecutor(在executeOnExecutor中传入)、mWorkermFuture,来看看它们的赋值在哪里:

sDefaultExecutor:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
......
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
private static class SerialExecutor implements Executor {
   final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
   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);
       }
   }
}

我们发现sDefaultExecutor的赋值默认就是SERIAL_EXECUTOR,也就是一个顺序执行的线程池,内部实现有一个任务队列。

mWorker

private final WorkerRunnable<Params, Result> mWorker;

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };
        ......
}

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

在AsyncTask的构造方法中,给mWorker赋值为一个Callable(带返回参数的线程,涉及到java并发的一些基础知识,这里不赘述),并且在call方法中执行了doInBackground方法,最后调用postResult方法

mFuture

private final FutureTask<Result> mFuture;
public AsyncTask() {
        ......
        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 occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
}

mFuture为FutureTask类型,这里将mWorker传入,在mWorker执行完毕后调用postResultIfNotInvoked方法,我们先看看这个方法:

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

其实这个方法也最后调用了postResult,在这之前做了个有没调用的判断,确保任务执行完毕后调用此方法。来看看postResult方法:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

又看到了熟悉的obtainMessagesendToTarget发送消息,这次消息内容变为MESSAGE_POST_RESULT,再来看看我们刚才已经提到的InternalHandlerhandleMessage方法:

        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;
            }
        }

最后根据消息类型,这里调用了result.mTask.finish,result类型为AsyncTaskResult

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

mTask的类型为AsyncTask,找到AsyncTask的finish方法:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

最后如果没有取消的话调用了onPostExecute,也就是我们之前重写的那个方法,在执行完毕后调用,并且此方法也在子线程。

多线程并发

正如开题所说,AsyncTask本质上就是对Handler的封装,在执行之前,执行中,执行完毕都有相应的方法,使用起来也一目了然,不过这还并不是AsyncTask的最大的优点,AsyncTask最适合使用的场景是多线程,开始在代码中已经看到了在AsyncTask内部有自己维护的线程池,默认的是SERIAL_EXECUTOR

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

按照顺序执行,一个任务执行完毕再执行下一个,还提供有一个支持并发的线程池:

//获取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;
//空闲线程超时时间(单位为S)
private static final int KEEP_ALIVE = 1;
//线程工厂
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);

public static final Executor THREAD_POOL_EXECUTOR
 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

声明为static,多个实例同用一个线程池,这个是Googl官方自带的一个根据cpu数目来优化的线程池,使用方法如下:

for(int i = 0; i < 100; i++) {//模拟100个任务,不超过128
    MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

executeOnExecutor中我们还可以传入自己自定义的线程池:

//跟默认一样的按顺序执行
asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor());
//无限制的Executor
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
//同时执行数目为10的Executor
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));

总结

AsyncTask使用起来的确很简单方便,内部也是Android的消息机制,并且很快捷的实现了异步更新UI,特别是多线程时也可以很好的表现,这个是我们单独使用Handler时不具备的,但是在使用过程中注意内部方法的调用顺序以及调用的时机,比如asyncTask.execute() 要在UI主线程中调用,在子线程中调用是不可以的,还有就是在使用时根据情况来决定到底应该用哪种线程池。

时间: 2024-08-20 14:42:49

AsyncTask你真的用对了吗?的相关文章

使用AsyncTask的 误区

AsyncTask是一个很常用的API,尤其异步处理数据并将数据应用到视图的操作场合.其实AsyncTask并不是那么好,甚至有些糟糕.本文我会讲AsyncTask会引起哪些问题,如何修复这些问题,并且关于AsyncTask的一些替代方案. 生命周期 关于AsyncTask存在一个这样广泛的误解,很多人认为一个在Activity中的AsyncTask会随着Activity的销毁而销毁.然后事实并非如此.AsyncTask会一直执行doInBackground()方法直到方法执行结束.一旦上述方法

你真的理解了AsyncTask吗?

在Android应用开发的过程中,我们需要时刻注意保证应用程序的稳定和UI操作响应及时,因为不稳定或响应缓慢的应用将给应用带来不好的印象, 严重的用户卸载你的APP,这样你的努力就没有体现的价值了.本文试图从AsnycTask的作用说起,进一步的讲解一下内部的实现机制.如果有一些开发经验的人, 读完之后应该对使用AsnycTask过程中的一些问题豁然开朗,开发经验不丰富的也可以从中找到使用过程中的注意点. 为何引入AsnyncTask? 在Android程序开始运行的时候会单独启动一个进程,默认

Android开发者:你真的会用AsyncTask吗?

[**导读**]在Android应用开发的过程中,我们需要时刻注意保证应用程序的稳定和UI操作响应及时,因为不稳定或响应缓慢的应用将给应用带来不好的印象,严重的用户卸载你的APP,这样你的努力就没有体现的价值了.本文试图从AsnycTask的作用说起,进一步的讲解一下内部的实现机制.如果有一些开发经验的人,读完之后应该对使用AsnycTask过程中的一些问题豁然开朗,开发经验不丰富的也可以从中找到使用过程中的注意点. 为何引入AsnyncTask? 在Android程序开始运行的时候会单独启动一

你真的了解AsyncTask吗?AsyncTask源码分析

转载请注明出处:http://blog.csdn.net/yianemail/article/details/51611326 1,概述 Android UI是线程不安全的,如果想要在子线程很好的访问ui, 就要借助Android中的异步消息处理机制 http://blog.csdn.net/yianemail/article/details/50233373 通过Thread 执行耗时操作,通常利用Handler 发送消息给ui线程. 这种方式代码相对臃肿,并且不能对多任务执行很好的控制. 为

android AsyncTask使用

/** * 作者:crazyandcoder * 联系: * QQ : 275137657 * email: [email protected] * 转载请注明出处! */ 异步任务 AsyncTask使用 android中实现异步机制主要有Thread加Handler和AsyncTask,今天主要记录一下AsyncTask的学习过程,以备以后之需.  一.构建AsyncTask子类的参数         AsyncTask<Params,Progress,Result>是一个抽象类.通常用于

探讨:你真的会用Android的Dialog吗?

一个Bug前几日出现这样一个Bug是一个RuntimeException,详细信息是这样子的: 复制代码代码如下: java.lang.IllegalArgumentException: View not attached to window manager    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356)    at android.view.WindowManagerImpl.rem

AsyncTask原理

1.对于耗时的操作(如上传下载.读写数据库等),为了不阻塞主线程,我们的一般方法是开启“子线程”.如果需要更新UI,则需要使用handler 2.如果耗时的操作太多,那么我们需要开启太多的子线程,这就会给系统带来巨大的负担,随之也会带来性能方面的问题.在这种情况下我们就可以考虑使用类AsyncTask来异步执行任务,不需要子线程和handler,就可以完成异步操作和刷新UI. 3.AsyncTask的构造方法有三个模板参数 Params(传递给后台任务的参数类型) Progress(后台计算执行

Android中AsyncTask使用具体解释

在Android中我们能够通过Thread+Handler实现多线程通信.一种经典的使用场景是:在新线程中进行耗时操作.当任务完毕后通过Handler向主线程发送Message.这样主线程的Handler在收到该Message之后就能够进行更新UI的操作.上述场景中须要分别在Thread和Handler中编写代码逻辑,为了使得代码更加统一,我们能够使用AsyncTask类. AsyncTask是Android提供的一个助手类,它对Thread和Handler进行了封装,方便我们使用. Andro

【Android基础】AsyncTask学习——如何取消掉AsyncTask

在AsyncTask中,我们没有办法直接停止掉异步任务,只能通过cancel方法来将AsyncTask标记为cancel状态,即cancel方法只是传递了一个信号量,而不是真的cancel了异步任务. 所以如果希望cancel方法能直接取消掉异步任务,就需要在doInBackground中检测当前状态:当状态是cancel状态,则立刻跳出循环. 使用cancel: //将task的状态标记为cancel if(task!=null&& task.getStatus() == AsyncTa