我们知道AysncTask是Android提供的给我们进行异步操作的类,我们可以利用这个类在子线程中执行耗时操作,并将结果更新到UI线程,网上很多说法是AysncTask必须在UI线程中创建并执行器execute方法,其实我觉得这个说法不太对。
AsyncTask具体源码可以参考:https://mp.csdn.net/postedit/81234625
这里仅仅说一些和本篇内容有关的代码:
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper); // 最终都是调用这句来创建Handler
...
}
如果我们调用无参的构造函数,即传入的Looper默认为空,则mHandler将会赋值为getMainHandler()方法返回的值:
private static InternalHandler sHandler;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
通过单例模式创建一个InternalHandler对象,并且传入的Looper为主线程的Looper,InternalHandler中定义了处理消息的逻辑,主要会调用AsyncTask的onProgressUpdate、onPostExecute方法回调更新进度和结果:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
因为InternalHandler对象构造时传入的Looper是主线程的Looper,所以其处理消息会在主线程中处理,即AsyncTask的onProgressUpdate、onPostExecute会在主线程中调用,从这里也可以看出,我们可以在子线程中创建AysncTask对象,再来看一下execute方法:
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
...
onPreExecute(); // 回调onPreExecute方法
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
execute方法主要有两个操作:
1、回调onPreExecute()方法;
2、通过线程池执行任务;
一般的,我们会在onPreExecute方法中执行异步任务开始前的操作,从代码中可以看出,这个方法是在调用execute方法的线程中执行的,所以,如果execute方法是在子线程中执行的,onPreExecute方法也会在子线程中执行,不能进行UI相关的操作,从这个角度来说,我们不能在非UI线程中调用execute方法开始异步任务,有两种情况例外:
1、在onPreExecute方法中不需要进行UI相关的操作;
2、如果在onPreExecute方法中需要进行UI相关的操作,可以通过Handler将其切换到主线程执行。
请看如下代码:
new Thread() {
@Override
public void run() {
String imagePath = "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1641460231,985790943&fm=27&gp=0.jpg";
mDownloadImageAsyncTask = new DownloadImageAsyncTask();
mDownloadImageAsyncTask.execute(imagePath);
}
}.start();
public class DownloadImageAsyncTask extends AsyncTask {
// 在UI线程中执行,任务开始前需要进行的UI操作,比如弹出加载框
@Override
protected void onPreExecute() {
super.onPreExecute();
runOnUiThread(new Runnable() { // 在非UI线程中通过Handler更新UI
@Override
public void run() {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(AsyncTaskTestActivity.this);
mProgressDialog.setTitle("提示信息");
mProgressDialog.setMessage("正在下载中,请稍后......");
// 设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
mProgressDialog.setCancelable(false);
// 设置ProgressDialog样式为水平的样式
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
}
mProgressDialog.show();
}
});
}
...
}
我们在子线程中创建了AysncTask实例并调用其execute方法,AysncTask的onPostExecute方法、onProgressUpdate等方法还是会在UI线程中调用,这是因为创建InternalHandler对象传入的是主线程的Looper;但是onPreExecute方法会在调用execute方法的线程中调用,所以在onPreExecute方法中如果要更新UI,需要通过Handler来完成。
AsyncTask在某些情况下可以在子线程中执行execute,在子线程中执行execute并不会影响onPostExecute方法、onProgressUpdate等方法的执行线程,但是会影响onPreExecute方法的执行线程,所以如果我们不需要在onPreExecute方法中更新UI,或者将UI相关的操作通过Handler交给主线程处理,我们是可以在子线程中执行execute方法的。