Асинхронная задача Android execute
Это было задано в одном из интервью Android. Меня спросили, Можно ли запустить другую асинхронную задачу (пусть это будет Task2) из метода doInBackground() асинхронной задачи 1(пусть это будет Task1). Я прошел через документы, которые говорят следующее:
экземпляр задачи должен быть создан в UI-потоке.
execute(Params...) должен вызываться в потоке пользовательского интерфейса.
согласно этим утверждениям, я думаю, что это не должно быть можно запустить задачу из фонового метода другой задачи. Кроме того, асинхронная задача имеет UI-методы (которые нельзя использовать в фоновом потоке), так что усилил мой аргумент, и я ответил на него как невозможно.
при проверке простого демонстрационного приложения я увидел, что это действительно возможно. Некоторые демо-код:
@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(). Мне нужна помощь в том, что здесь происходит. Спасибо.
2 ответов
Давайте изменим ваш код на следующий:
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)
), the scheduleNext()
будет называться, в противном случае scheduleNext()
на finally
блок будет вызываться после (по любой причине) окончания текущего выполненного Runnable
. Все!--12-->s выполняются в отдельном потоке по THREAD_POOL_EXECUTOR
.
что не так с выполнением AsyncTask из других потоков? начиная с Jelly Bean, an 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.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.run(Main2Activity.java:21)
public class MainActivity extends Activity {
private final static String TAG = "ThreadingAsyncTask";
private ImageView mImageView;
private ProgressBar mProgressBar;
private int mDelay = 500;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView);;
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
final Button button = (Button) findViewById(R.id.loadButton);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
new LoadIconTask().execute(R.drawable.cheetah);
}
});
final Button otherButton = (Button) findViewById(R.id.otherButton);
otherButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "I'm Working",
Toast.LENGTH_SHORT).show();
}
});
}
class LoadIconTask extends AsyncTask<Integer, Integer, Bitmap> {
@Override
protected void onPreExecute() {
mProgressBar.setVisibility(ProgressBar.VISIBLE);
}
@Override
protected Bitmap doInBackground(Integer... resId) {
Bitmap tmp = BitmapFactory.decodeResource(getResources(), resId[0]);
// simulating long-running operation
for (int i = 1; i < 11; i++) {
sleep();
publishProgress(i * 10);
}
return tmp;
}
@Override
protected void onProgressUpdate(Integer... values) {
mProgressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(Bitmap result) {
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
mImageView.setImageBitmap(result);
}
private void sleep() {
try {
Thread.sleep(mDelay);
} catch (InterruptedException e) {
Log.e(TAG, e.toString());
}
}
}
}