问题描述
有5个线程规则:
为了使此类正常工作,必须遵循一些线程规则:
必须在UI线程上加载AsyncTask类。 从Build.VERSION_CODES.JELLY_BEAN开始自动完成。
必须在UI线程上创建任务实例。
必须在UI线程上调用execute(Params ...)。
不要手动调用onPreExecute(),onPostExecute(Result),doInBackground(Params ...),onProgressUpdate(Progress ...)。
该任务只能执行一次(如果尝试第二次执行,则会引发异常。)
但是,我对规则2和3不太了解。 我已经在以下代码上尝试过它们:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
log(Thread.currentThread().getName());
new Thread(new Runnable() {
@Override
public void run() {
log(Thread.currentThread().getName());
Task task = new Task();
task.execute();
}
}).start();
}
public class Task extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
return null;
}
}
结果如下:
09-15 21:27:10.179 3310-3310/com.xxx.test D/com.xxx.test.MainActivity: main
09-15 21:27:10.179 3310-3329/com.xxx.test D/com.xxx.test.MainActivity: Thread-264
我有一个问题:为什么我可以创建任务实例并在UI线程(主线程)之外的另一个线程(线程264)中调用execute()
方法?
我读了 ,但没有解释原因。 非常感谢!
1楼
从
异步任务由在后台线程上运行的计算定义,并且其结果发布在UI线程上。
我们需要澄清一些要点。
- 我们的计算将在后台线程上运行
- 计算结果将发布在UI线程上
-
它们不会阻止开发人员从非UI线程创建或调用
AsyncTask
步骤1:致电时
Task task = new Task();
查看 。
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
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);
}
}
};
}
首先,他们创建一个处理程序,该处理程序引用UI线程的处理程序,然后创建一个Runnable,它调用doInBackground
方法(此处为我们的计算),然后返回Future(将来将在某个时候返回计算结果)。
步骤2:然后致电
task.execute();
查看 。
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;
}
onPreExecute()
将在调用AsyncTask的调用线程(在本例中为您的匿名线程)中被调用。
然后,它在其执行程序中执行Future。
计算完成后,它将调用postResult
方法。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
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;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在这种情况下, getHandler
引用UI线程的处理程序,因此onPostExecute
将始终在UI线程上被调用。
结论:
通过AsyncTask,可以正确,轻松地使用UI线程。 此类允许您执行后台操作并在UI线程上发布结果,而无需操纵线程和/或处理程序。
2楼
用户代码可以期望通过UI运行3种保护方法
-
onPreExecute()
-
onProgressUpdate(Progress...)
-
onPostExecute(Result)
尽管onPreExecute()
将在调用execute()
任何线程上运行,但是其余2个方法将由Handler
运行。
Handler
类将与创建它的线程关联,它允许用户代码将Runable
发布到该特定线程上运行。
在AsyncTask
出现之前,想要更新UI(必须在UI线程上更新)的用户代码将必须首先在UI线程上创建Handler
,然后将Runable
发布到该Handler
以在UI线程上执行其任务。
AsyncTask
旨在简化这些繁琐的工作,它们的内部静态Handler
是在创建AsyncTask
实例之前由UI /主线程创建的。
即使您可以在辅助线程中使用AsyncTask
( onPreExecute()
除外onPreExecute()
,我还是建议您遵循文档并在UI线程上创建/运行AsyncTask
。