Если вам в проекте необходимо загружать картинки и/или отправлять http-запросы, выполнять любую другую долгую операцию, которая может заблокировать UI поток, то как ни крути придется использовать решение для выполнения асинхронных запросов.
С самого начала я, по старинке, расскажу про стандартные способы AsyncTask/Loaders и объясню, почему их лучше не использовать. Затем расскажу про продвинутые методы решения этой задачи.
Самое тривиально решение AsyncTask
Плюсы:
1. Простой для понимания.
2. Решение из коробки.
Минусы:
1. Memory leak при перевороте экрана — старая activity не уничтожиться пока задача не выполниться, и результат придет в старую activity, и, скорее всего, вы словите exception.
2. Нет обработки ошибок.
3. Долгие операции будут прерваны как только система решит уничтожить ваше приложение (если вы его свернули например).
Пример кода:
requestDataTask = new AsyncTask<Void, Void, JSONObject>() {
@Override
protected JSONObject doInBackground(Void... params) {
//Для обработки ошибок обычно заводят поля Exception. (не лучшее решение)
final String requestResult = apiService.getData();
final JSONObject json = JsonUtils.parse(requestResult);
lruCache.cacheJson(json);
return json;
}
};
Более современное решение — Loaders
Тоже самое, что и AsyncTask, за исключением одной вещи: при перевороте экрана можно результат получить в новой activity. Но 2-я и 3-я проблемы все равно присутствуют. И хотя Google рекомендует использовать loaders, не рекомендую этого делать. Вам придется писать много boilerplate code.
Пример можно посмотреть здесь, либо можно просто поверить мне и не использовать Loaders. Код не копирую сюда, так как уж очень многословные эти лоадеры.
А теперь самое лучшее решение на мой взгляд — Robospice
Плюсы:
1. Robospice выполняет все операции в service. Это значит, что если activity уничтожится, то задача все равно будет продолжать выполняться. Лучшее решение для долгих задач.
2. Есть методы для обработки ошибок.
3. Кеширование запросов, интеграция с Retrofit.
Но не все так радужно… Есть и минусы:
1. Нет метода для проверки исполняется задача или нет, а это очень важная вещь для меня. На github есть пул реквест с решением этой проблемы — #383. Кому надо — используйте этот форк.
2. Не самый удобный api в мире, но сойдёт.
Пример кода:
spiceManager.execute(new SpiceRequest<String>(String.class) {
@Override
public String loadDataFromNetwork() throws Exception {
Thread.sleep(1000);// поддерживается обработка ошибок
return "It works!!!!";
}
}, "key", DurationInMillis.ALWAYS_RETURNED,
new RequestListener<String>() {
@Override
public void onRequestFailure(SpiceException spiceException) {
}
@Override
public void onRequestSuccess(String s) {
}
});
Достойное внимания решение — RxJava
Хорошое решение с кучей разных фич, подробнее можно прочитать на Хабре в публикации «Реактивное программирование под Android».
Вкратце от меня: есть обработка ошибок, поддержка жизненного цикла activity/fragment. Но не подходит для долгих операций (в отличие от Robospice), плюс лично мне было тяжело с ней разобраться, да и до сих пор кажется, что я не понимаю всего замысла.
Мой собственный велосипед — Slige Task
Плюсы:
1. Простой для понимания т.к. основан на AsyncTask.
2. Есть обработка ошибок.
3. Поддержка жизненного цикла activity.
4. Api немного поудобней.
5. Легко проверить, исполняется задача или нет.
Минусы:
1. Скудный функционал в отличии от RxJava.
2. Не подходит для долгих задач (см. Robospice).
Пример кода:
new SligeTask<String,Integer,String>((notifier, strings) -> {
try {
Thread.sleep(1000);
return "executed successfully!!!";
} catch (InterruptedException e) {
notifier.publishError(e); //сообщит об ошибке в ErrorListener
return null;
}
},LOADER_ID)
.setPreExecuteListener(() -> setLoading(true))
.setResultListener(this)
.setErrorListener(this)
.setCancelListener(this)
.setDefaultCallbackLimiter(this) //Предотвратит выполнение коллбеков, если activity умрёт
.execute();
Заключение
В заключение хотелось бы подвести итог. Лично я буду использовать в своих проектах форк Robospice и SligeTask, если задачи не долгие и не требуют кеширования. Вы же вольны выбирать всё, что вам угодно.
Да, библиотеки рассмотрены, конечно, не все (их очень много). Я рассмотрел самые популярные решения. Здесь вы можете найти кучу других.
Пишите в комментариях, какие вы используете библиотеки и почему.
Если кто-то заметил лямбды в примерах кода, но не знает, что это, как и зачем — обратите внимание на этот проект.
Автор: qux