为什么 AsyncTask 实例必须在UI线程里创建?
2015-09-28 20:40
826 查看
AsyncTask作为基础的多线程通信方案,在实际开发中广泛的使用。
具体的使用方式,在此不在过多说明,如果您不知道,请找度娘。
查到的资料中大多有类似的注意点:
Task的实例必须在UI thread中创建
execute方法必须在UI thread中调用
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
该task只能被执行一次,否则多次调用时将会出现异常
那么大家是否想过 为什么 AsyncTask 实例需要在UI thread中创建,execute方法必须在UI thread中调用?
无他,为UI更新耳~
我们知道更新UI需要在主线程中进行,而在AsyncTask的众多方法中,有很多方法,可能涉及到UI的更新,如:onPreExecute(),onProgressUpdate(),onPostExecute(), onCancelled 。
下面我们一起来看一下对应的方法定义及注释文档
onPreExecute
说明:
onPreExecute方法在doInBackground(一般用来写耗时流程如:Http请求)前调用,通常情况下,通过覆盖( @Override) 此方法来显示空的进度条(在需要显示进度的情况下)
onProgressUpdate
说明:
onProgressUpdate的作用为显示处理进度,通常情况下,在doInBackground中触发,通过调用publishProgress(会回调onProgressUpdate更新UI)推送进度,在onProgressUpdate中更新UI。
onPostExecute
说明:onPostExecute的作用是,对doInBackground的的结果进行后续整理,一般情况下,在处理中可能会进行UI的操作,如更新doInBackground方法中获取到的list列表数据。
onCancelled
看了这几个方法后,您可能有如下想法:
上面的几个方法的确涉及到UI更新,但是与UI线程创建实例和调用execute有毛关系?
哈哈,你如果也这样想,那么咱们就是同路人,我们一起来看一下AsyncTask的源码。
通过源码,首先映入眼帘的是
看到这,大家立马知道了,AsyncTask内部采用了线程池技术, 内部通信似乎使用了Handler。
Handler? 这个好熟悉啊,在看MESSAGE_POST_RESULT,MESSAGE_POST_PROGRESS 两个常量,不难想象出,通过Handler sentMessage的流程。
InternalHandler
说明:该对象,主要是将消息抛给UI线程,用于更新UI,finish和onProgressUpdate 为 AsyncTask 的方法,且均为UI操作方法。
构造方法
这里面主要有两个对象mWorker、mFuture 。
正常执行流程
创建对象
此处已经创建了用于更新UI的sHandler对象。
调用execute方法
我们看一下execute方法
继续
看到了,在 executeOnExecutor 中调用了onPreExecute,而此方法的功能为刷新UI操作,而刷新UI必须在主线程里面,所以execute 方法的调用,必须在主线程里面。
execute方法的具体执行流程是什么样的呢?
第一步:
onPreExecute()
在execute方法中触发
第二步:
调用doInBackground方法
触发流程
这个会回调mWork对象的call方法:
看到了吧,执行了doInBackground方法。
第三步:
回调onProgressUpdate 更新处理进度
触发流程:
如果在上一步的doInBackground方法中,需要更新处理进度,需要通过publishProgress方法来进行,
我们来跟一下publishProgress的源码。
将触发handler的MESSAGE_POST_PROGRESS分支,
第四步:
调用onPostExecute 或者 onCancelled 方法
触发流程如下:
触发handler的 MESSAGE_POST_RESULT 分支
其中,mTask为AsyncTask对象,故需要查找 finish方法
好了,到此为止,一个正常的AsyncTask执行过程已经完成。
通过执行过程的代码,可以看到onProgressUpdate, onPostExecute , onCancelled 的 触发是在Handler中抛出的,handler的创建线程,决定了是否可以执行UI更新操作。
总结:
AsyncTask实例必须UI线程创建的原因如下:
需要在主线程创建InternalHandler,以便onProgressUpdate, onPostExecute , onCancelled 可以正常更新UI。
AsyncTask实例的execute方法必须在主线程调用的原因如下:
保证 onPreExecute 正常的更新UI。
具体的使用方式,在此不在过多说明,如果您不知道,请找度娘。
查到的资料中大多有类似的注意点:
Task的实例必须在UI thread中创建
execute方法必须在UI thread中调用
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
该task只能被执行一次,否则多次调用时将会出现异常
那么大家是否想过 为什么 AsyncTask 实例需要在UI thread中创建,execute方法必须在UI thread中调用?
无他,为UI更新耳~
我们知道更新UI需要在主线程中进行,而在AsyncTask的众多方法中,有很多方法,可能涉及到UI的更新,如:onPreExecute(),onProgressUpdate(),onPostExecute(), onCancelled 。
下面我们一起来看一下对应的方法定义及注释文档
onPreExecute
/** * Runs on the UI thread before {@link #doInBackground}. * * @see #onPostExecute * @see #doInBackground */ protected void onPreExecute() { }
说明:
onPreExecute方法在doInBackground(一般用来写耗时流程如:Http请求)前调用,通常情况下,通过覆盖( @Override) 此方法来显示空的进度条(在需要显示进度的情况下)
onProgressUpdate
/** * Runs on the UI thread after {@link #publishProgress} is invoked. * The specified values are the values passed to {@link #publishProgress}. * * @param values The values indicating progress. * * @see #publishProgress * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) protected void onProgressUpdate(Progress... values) { }
说明:
onProgressUpdate的作用为显示处理进度,通常情况下,在doInBackground中触发,通过调用publishProgress(会回调onProgressUpdate更新UI)推送进度,在onProgressUpdate中更新UI。
onPostExecute
/** * <p>Runs on the UI thread after {@link #doInBackground}. The * specified result is the value returned by {@link #doInBackground}.</p> * * <p>This method won't be invoked if the task was cancelled.</p> * * @param result The result of the operation computed by {@link #doInBackground}. * * @see #onPreExecute * @see #doInBackground * @see #onCancelled(Object) */ @SuppressWarnings({"UnusedDeclaration"}) protected void onPostExecute(Result result) { }
说明:onPostExecute的作用是,对doInBackground的的结果进行后续整理,一般情况下,在处理中可能会进行UI的操作,如更新doInBackground方法中获取到的list列表数据。
onCancelled
/** * <p>Applications should preferably override {@link #onCancelled(Object)}. * This method is invoked by the default implementation of * {@link #onCancelled(Object)}.</p> * * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and * {@link #doInBackground(Object[])} has finished.</p> * * @see #onCancelled(Object) * @see #cancel(boolean) * @see #isCancelled() */ protected void onCancelled() { }
看了这几个方法后,您可能有如下想法:
上面的几个方法的确涉及到UI更新,但是与UI线程创建实例和调用execute有毛关系?
哈哈,你如果也这样想,那么咱们就是同路人,我们一起来看一下AsyncTask的源码。
通过源码,首先映入眼帘的是
/** * 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); private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final InternalHandler sHandler = new InternalHandler();
看到这,大家立马知道了,AsyncTask内部采用了线程池技术, 内部通信似乎使用了Handler。
Handler? 这个好熟悉啊,在看MESSAGE_POST_RESULT,MESSAGE_POST_PROGRESS 两个常量,不难想象出,通过Handler sentMessage的流程。
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; } } }
说明:该对象,主要是将消息抛给UI线程,用于更新UI,finish和onProgressUpdate 为 AsyncTask 的方法,且均为UI操作方法。
构造方法
/** * Creates a new asynchronous task. This constructor must be inv cc37 oked 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); } } }; }
这里面主要有两个对象mWorker、mFuture 。
正常执行流程
创建对象
此处已经创建了用于更新UI的sHandler对象。
调用execute方法
我们看一下execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
继续
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(); mWorker.mParams = params; exec.execute(mFuture); return this; }
看到了,在 executeOnExecutor 中调用了onPreExecute,而此方法的功能为刷新UI操作,而刷新UI必须在主线程里面,所以execute 方法的调用,必须在主线程里面。
execute方法的具体执行流程是什么样的呢?
第一步:
onPreExecute()
在execute方法中触发
第二步:
调用doInBackground方法
触发流程
exec.execute(mFuture);
这个会回调mWork对象的call方法:
public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); }
看到了吧,执行了doInBackground方法。
第三步:
回调onProgressUpdate 更新处理进度
触发流程:
如果在上一步的doInBackground方法中,需要更新处理进度,需要通过publishProgress方法来进行,
我们来跟一下publishProgress的源码。
/** * This method can be invoked from {@link #doInBackground} to * publish updates on the UI thread while the background computation is * still running. Each call to this method will trigger the execution of * {@link #onProgressUpdate} on the UI thread. * * {@link #onProgressUpdate} will note be called if the task has been * canceled. * * @param values The progress values to update the UI with. * * @see #onProgressUpdate * @see #doInBackground */ protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); }
将触发handler的MESSAGE_POST_PROGRESS分支,
case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;
第四步:
调用onPostExecute 或者 onCancelled 方法
触发流程如下:
@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); } } private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
触发handler的 MESSAGE_POST_RESULT 分支
case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]);
其中,mTask为AsyncTask对象,故需要查找 finish方法
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
好了,到此为止,一个正常的AsyncTask执行过程已经完成。
通过执行过程的代码,可以看到onProgressUpdate, onPostExecute , onCancelled 的 触发是在Handler中抛出的,handler的创建线程,决定了是否可以执行UI更新操作。
总结:
AsyncTask实例必须UI线程创建的原因如下:
需要在主线程创建InternalHandler,以便onProgressUpdate, onPostExecute , onCancelled 可以正常更新UI。
AsyncTask实例的execute方法必须在主线程调用的原因如下:
保证 onPreExecute 正常的更新UI。
相关文章推荐
- Eval 函数 | Execute 语句 | ExecuteGlobal 语句使用说明
- ASP中Server.Execute和Execute实现动态包含(include)脚本的区别
- ASP Eval、Execute、ExecuteGlobal区别分析
- JavaScript类继承及实例化的方法
- EXECUTE IMMEDIATE用法小结
- 为什么 Windows2003 的 IIS6.0 不能上传超过 200K 的文件?
- Android中AsyncTask的用法实例分享
- C#事务处理(Execute Transaction)实例解析
- Android的异步任务AsyncTask详解
- Android后台线程和UI线程通讯实例
- asynctask的用法详解
- Android中AsyncTask详细介绍
- Android开发笔记之:AsyncTask的应用详解
- android教程之使用asynctask在后台运行耗时任务
- Android中子线程和UI线程通信详解
- MYSQL存储过程<二>:执行用字符串拼成的sql语句 3ff8
- 自定义可以下拉刷新的listview
- Android中的异步任务——AsyncTask
- oracle11g 安装 实例化 EM 配置文件时出错
- Android中AsyncTask的简单用法