Выполнение задачи Async для Android

Это было задано в одном из интервью с Android. Меня спросили, можно ли запустить другую задачу async (пусть это будет Task2) из ​​метода doInBackground () async-задачи 1 (пусть это будет Task1). Я прошел через документы, которые говорят следующее:

Экземпляр задачи должен быть создан в потоке пользовательского интерфейса.

Execute (Params …) должен быть вызван в потоке пользовательского интерфейса.

В соответствии с этими утверждениями, я думаю, что не должно быть возможности запустить задачу из фонового метода другой задачи. Кроме того, в async-задаче есть методы пользовательского интерфейса (которые нельзя использовать в фоновом потоке), что усилило мои аргументы, и я ответил, что это невозможно.

При проверке простого демонстрационного приложения я увидел, что это действительно возможно. Некоторые демо-коды:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; init(); Log.v ("gaurav", "Thread is : " + Thread.currentThread().getName()); Task1 task = new Task1(); task.execute(); } 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(); 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; } } 

Я получаю следующие журналы, указывающие на успешное выполнение:

 > 08-07 09:46:25.564: V/gaurav(2100): Thread is : main 08-07 > 09:46:25.564: V/gaurav(2100): Thread task 1 is : AsyncTask #3 08-07 > 09:46:25.564: V/gaurav(2100): Thread task 2 is : AsyncTask #4 08-07 > 09:46:25.564: V/gaurav(2100): Task 2 started 

Я проверил это на устройстве ICS, KK и L, и он отлично работает для всех.

Одна из причин, по которой я мог подумать, заключается в том, что я не переопределяю какие-либо методы пользовательского интерфейса и не делаю обновления в пользовательском интерфейсе в своей второй задаче, поэтому это не вызывает никаких проблем, но я не уверен. Даже если это так, оно нарушает правила потоков, упомянутые в руководстве разработчика.

В качестве ссылки я также проверил эту ссылку: запустите AsyncTask из другого AsyncTask doInBackground (), но в ответе говорится, что для запуска второй задачи используется метод runOnUiThread () внутри doInBackground (). Мне нужна помощь в том, что здесь происходит. Благодарю.

Давайте изменим ваш код на следующее:

 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) ), if (mActive == null) scheduleNext() , в противном случае scheduleNext() в блоке finally будет вызываться после окончания (по какой-либо причине) текущего выполняемого Runnable , Все Runnable s выполняются в отдельном потоке с помощью 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)