Ответ 1
Измените свой код на следующее:
class Task1 extends AsyncTask {
@Override
protected Object doInBackground(Object... params) {
// TODO Auto-generated method stub
Log.v ("gaurav", "Thread task 1 is : " + Thread.currentThread().getName());
Task2 task = new Task2();
task.execute();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.v ("gaurav", "Log after sleeping");
return null;
}
}
class Task2 extends AsyncTask {
@Override
protected Object doInBackground(Object... params) {
// TODO Auto-generated method stub
Log.v ("gaurav", "Thread task 2 is : " + Thread.currentThread().getName());
Log.v ("gaurav", "Task 2 Started");
return null;
}
}
Теперь LogCat возвращает:
08-07 06:13:44.208 3073-3073/testapplication V/gaurav﹕ Thread is : main
08-07 06:13:44.209 3073-3091/testapplication V/gaurav﹕ Thread task 1 is : AsyncTask #1
08-07 06:13:49.211 3073-3091/testapplication V/gaurav﹕ Log after sleeping
08-07 06:13:49.213 3073-3095/testapplication V/gaurav﹕ Thread task 2 is : AsyncTask #2
08-07 06:13:49.213 3073-3095/testapplication V/gaurav﹕ Task 2 Started
Как вы видите, Task 2
выполняется после завершения выполнения Task 1
(даже после сна в течение 5 секунд). Это означает, что вторая задача не будет запущена до тех пор, пока не будет выполнена первая.
Почему?
Причина заключается в исходном коде AsyncTask. Пожалуйста, рассмотрите метод execute()
:
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
и scheduleNext()
:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
Наиболее важным ключевым словом в этих методах является synchronized
, который гарантирует, что эти методы будут выполняться только в одном потоке одновременно. Когда вы вызываете метод execute
, он предлагает новый Runnable
to mTask
, который является экземпляром класса ArrayDeque<Runnable>
, который работает как сериализатор различных запросов в разных потоках [подробнее]. Если бы не было выполнено Runnable
(т.е. if (mActive == null)
), вызывается scheduleNext()
, в противном случае scheduleNext()
в блоке finally
вызывается после окончания (по какой-либо причине) завершенного текущего Runnable
. Все Runnable
выполняются в отдельном потоке с помощью THREAD_POOL_EXECUTOR
.
Что случилось с выполнением AsyncTask из других потоков? Начиная с Jelly Bean, AsyncTask
загружается в класс при запуске приложения в потоке пользовательского интерфейса, так что обратные вызовы гарантируются однако, перед выпуском Jelly Bean, если другой поток создает AsyncTask
, обратные вызовы могут не встречаться в правильном потоке.
Итак, AsyncTask
реализация должна вызываться из потока пользовательского интерфейса только на платформах до Jelly Bean (+ и +).
Разъяснение: рассмотрите следующий пример, который просто разъясняет различия между различными выпусками платформы Android:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
new Thread() {
@Override
public void run() {
Task1 task = new Task1();
task.execute();
}
}.start();
}
class Task1 extends AsyncTask {
@Override
protected Object doInBackground(Object... params) {
return null;
}
}
Он отлично работает на Android 5.1, но на Android 2.3 падает со следующим исключением:
08-07 12:05:20.736 584-591/github.yaa110.testapplication E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-8
java.lang.ExceptionInInitializerError
at github.yaa110.testapplication.Main2Activity$1.run(Main2Activity.java:21)
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
at github.yaa110.testapplication.Main2Activity$1.run(Main2Activity.java:21)