Многопоточность, Работа с
сетью
Android
Константин Золотов
Приоритеты
1. Foreground
2. Visible
3. Service
4. Backgorund
Main Thread (UI Thread)
По умолчанию все работает в имеено в нем
UI потоконебезопасен: попытка что-либо сделать с View из другого потока выбросит
исключение.
Если занять поток больше чем на 5 секунд, появится диалог ANR (Application Not
Responding) с предложением убить приложение.
Начиная с Android 3.0 в инструментах разработчика появился strict mode, который бросает
исключение если разработчик сделал что-то не так. Полез в интернет из главного потока
или начал читать файл? Получай исключение!
Мораль
Долгие операции в главном потоке — это плохо!
Асинхронное поведение
Thread/Executor, т.е., все, что есть в java
AsyncTask - стандартный класс для асинхронных задач
Service/IntentService - компонент приложения, который не имеет UI и работает как фоновая
задача
Loader (API >= 11) - более современный механизм запуска асинхронных задач
Handler - компонент, ассоциированный с потоком, который позовляет работать с очередью
сообщений.
Main Thread
Handler
Handler — это ассоциированный с потоком объект, обрабатывающий сообщения. Обработка
сообщений происходит либо в объекте, который передается в конструкторе:
1 Handler handler =
2 new Handler(Looper.getMainLooper(), new Handler.Callback(){...});
Либо расширением самого Handler и реализацией его метода handleMessage(final Message msg)
1 class DemoHandler extends Handler {
2 @Override
3 public void handleMessage(final Message msg) {
4 //
5 }
6 }
Альтернативой является использование Runnable, в нем же и содержится код, выполняемый
при обработке сообщения.
Handler
По умолчанию Runnable отправляется в конец очереди. Однако, можно отправить с
задержкой, в указанное время и в самое начало очереди:
handler.post(Runnable r)
handler.postDelayed(Runnable r, long delay)
handler.postAtTime(Runnable r, long time)
handler.postAtFrontOfQueue(Runnable r)
То же самое можно проделать и с сообщением;
handler.sendMessage(Message msg)
handler.sendMessageDelayed(Message msg, long delay)
handler.sendMessageAtTime(Message msg, long time)
handler.sendMessageAtFrontOfQueue(msg)
AsyncTask
Типичный пример асинхронного поведения:
1. Сделать что-то в главном потоке, например вывести диалог начала загрузки
2. Запустить задачу в другом потоке и время от времени обновлять статус задачи (например
прогресбар)
3. После завершения задачи сообщить о результате
AsyncTask
AsyncTask
1 public class DemoAsyncTask extends AsyncTask<String, Integer, Boolean> {
2 @Override
3 protected Boolean doInBackground(final String... params) {
4 Boolean result = false;
5 // Do something
6 for (int i = 0; i < 10; i++) {
7 publishProgress(i);
8 }
9 return result;
10 }
11
12 @Override
13 protected void onPostExecute(final Boolean aBoolean) {
14 // Show result
15 }
16
17 @Override
18 protected void onProgressUpdate(final Integer... values) {
19 // Update progress
20 }
21 }
AsyncTask
И использование:
1 DemoAsyncTask demoTask= new DemoAsyncTask();
2 demoTask.execute("Argument 1", "Argument 2");
Чем параметризуется AsycTask:
1. Тип массива входных аргументов в методе execute и doInBackground
2. Тип обновления прогресса
3. Тип результата
Если что-то из этого не предполагается, можно использовать Void.
AsyncTask
В зависимости от версии API AsyncTask ведет себя по-разному:
4 < API < 13 — каждый новый AsyncTask работает в новом потоке
API ≥ 13 (3.2+) - на все AsyncTask выделяется один поток, но добавили поле
AsyncTask.THREAD_POOL_EXECUTOR.
Причем, THREAD_POOL_EXECUTOR неодинаков для разных API:
API < 19 от 5 до 128 потоков, длина очереди — 10.
API ≥ 19 от N+1 до 2N+1 потоков, длина очереди — 128. N - количество ядер процессора
устройства.
Service
Service
Сервис обязательно объявляется в манифесте
1 <manifest ... >
2 ...
3 <application ... >
4 <service android:name=".service.DemoService" />
5 ...
6 </application>
7 </manifest>
Также у тега <service> есть ряд атрибутов:
name - имя класса, реализующего Service
exported - могут ли другие приложения взаимодействовать с этим сервисом
isolatedProcess - будет ли сервис работать как отдельный процесс
permission - разрешение, требуемое для запуска этого сервиса
process - имя процесса, в котором сервис будет запущен.
label - имя процесса, отображаемое пользователю
Приоритеты Service
По умолчанию сервисы имеют приоритет background
Чтобы повысить приоритет до foreground или понизить обратно нужно воспользоваться
методами:
startForeground()
stopForeground()
Также можно узнать о том, что системе не хватает памяти и она может убить сервис:
onLowMemory()
onTrimMemory() — API >=14
IntentService example
1 public class DemoIntentService extends IntentService {
2 public DemoIntentService() {
3 // Service name is used for debugging
4 super("DemoIntentService");
5 }
6
7 @Override
8 protected void onHandleIntent(final Intent intent) {
9 // Do stuff
10 }
11 }
Loader
Появился в API 11
Привязан к методам жизненного цикла activity или fragment
Основные компоненты
LoaderManager - создает, уничтожает, запускает, пересоздает Loader
LoaderManager.LoaderCallbacks - интерфейс для связи клиента и менеджера
Loader - объект, асинхронно выполняющий задачу.
Работа с сетью
Чаще всего под работой с сетью подразумевают взаимодействие с REST-подобными
cервисами, т.е. HTTP + JSON/XML
Для доступа в сеть требуется разрешение android.permission.INTERNET. Также может
понадобиться разрешение android.permission.ACCESS_NETWORK_STATE для проверки
доступности сети.
Для отображения web-страниц можно использовать WebView - практически полноценный
браузер. Начиная с Android 4.4 основан на Chromium и регулярно обновляется.
Стандартные средства
HttpClient - deprecated in API 22 (API >=23 доступно только в виде legacy-модуля)
HttpUrlConnection
HttpClient
Типичный use-case
Создается HttpClient (DefaultHttpClient или AndroidHttpClient)
Создается и настраивается запрос – объект класса HttpUriRequest (обычно HttpGet или
HttpPost)
Выполняется запрос HttpClient.execute и получаем в ответ HttpResponse
Разбирается HttpResponse – заголовки, строка ответа и т.д.
Получается HttpEntity и разбирается тело ответа HttpClient
HttpUrlConnection
Типичный use-case
Создается объект URL, вызывается openConnection() и делается приведение к
HttpURLConnection
Подготавливается запрос – устанавливается хедеры, параметры запроса, тип контента и
т.д.
По необходимости подгатавливается тело запроса (POST запрос). setDoOutput(true) и
getOutputStream()
Читается ответ сервера. Строку ответа (код, сообщение), заголовки, cookie и т.д.
Закрывается соединение и освобождаются ресурсы
HttpClient vs HttpUrlConnection
Для API ≥ 9 (2.3+) предпочтительнее HttpUrlConnection т.к. в нем меньше багов
Для API < 9 лучше HttpClient
OkHttp / Retro t
OkHttp - библиотека для работы с HTTP от SquareUp.
Retro t - библиотека для работы с REST API от SquareUp, на Android по умолчанию
использует HttpUrlConnection. Однако лучше подключать OkHttp, из-за багов в
HttpUrlConnection.
Бочка дегтя
1. Асинхронность через callback приводит к росту вложенности, отсюда вытекает
запутанность кода, проблемы с сопровождением и т.д. (т. н. Callback-hell)
2. Компонент, запустивший AsyncTask запросто может благополучно умереть, что вызывает
проблемы с доставкой результата
3. Регулярно приходится загружать картинки, зачастую адаптерах ListView
4. К бекэнду часто нужно строить кеш на стороне приложения
5. Часто результат выполнения сетевого запроса нужен в разных местах приложения
Решения
1. RxJava
2. Проверять, жив ли компонент, подпирать все это костылями / использовать EventBus
3. Picasso / Glide / ...
4. ORM, сохранение в файлы, SharedPreferences — по ситуации
5. Otto / Event bus
Дополнительные материалы
Более подробная презентация о многопоточности и асинхронности
Более подробная статья о сервисах
Цикл статей о Loader с примерами
Реактивные расширения RxJava
Picasso
Retro t
Еще примеры

Многопоточность, работа с сетью (Lecture 12 – multithreading, network)

  • 1.
  • 2.
  • 3.
    Main Thread (UIThread) По умолчанию все работает в имеено в нем UI потоконебезопасен: попытка что-либо сделать с View из другого потока выбросит исключение. Если занять поток больше чем на 5 секунд, появится диалог ANR (Application Not Responding) с предложением убить приложение. Начиная с Android 3.0 в инструментах разработчика появился strict mode, который бросает исключение если разработчик сделал что-то не так. Полез в интернет из главного потока или начал читать файл? Получай исключение!
  • 4.
    Мораль Долгие операции вглавном потоке — это плохо!
  • 6.
    Асинхронное поведение Thread/Executor, т.е.,все, что есть в java AsyncTask - стандартный класс для асинхронных задач Service/IntentService - компонент приложения, который не имеет UI и работает как фоновая задача Loader (API >= 11) - более современный механизм запуска асинхронных задач Handler - компонент, ассоциированный с потоком, который позовляет работать с очередью сообщений.
  • 7.
  • 8.
    Handler Handler — этоассоциированный с потоком объект, обрабатывающий сообщения. Обработка сообщений происходит либо в объекте, который передается в конструкторе: 1 Handler handler = 2 new Handler(Looper.getMainLooper(), new Handler.Callback(){...}); Либо расширением самого Handler и реализацией его метода handleMessage(final Message msg) 1 class DemoHandler extends Handler { 2 @Override 3 public void handleMessage(final Message msg) { 4 // 5 } 6 } Альтернативой является использование Runnable, в нем же и содержится код, выполняемый при обработке сообщения.
  • 9.
    Handler По умолчанию Runnableотправляется в конец очереди. Однако, можно отправить с задержкой, в указанное время и в самое начало очереди: handler.post(Runnable r) handler.postDelayed(Runnable r, long delay) handler.postAtTime(Runnable r, long time) handler.postAtFrontOfQueue(Runnable r) То же самое можно проделать и с сообщением; handler.sendMessage(Message msg) handler.sendMessageDelayed(Message msg, long delay) handler.sendMessageAtTime(Message msg, long time) handler.sendMessageAtFrontOfQueue(msg)
  • 10.
    AsyncTask Типичный пример асинхронногоповедения: 1. Сделать что-то в главном потоке, например вывести диалог начала загрузки 2. Запустить задачу в другом потоке и время от времени обновлять статус задачи (например прогресбар) 3. После завершения задачи сообщить о результате
  • 11.
  • 12.
    AsyncTask 1 public classDemoAsyncTask extends AsyncTask<String, Integer, Boolean> { 2 @Override 3 protected Boolean doInBackground(final String... params) { 4 Boolean result = false; 5 // Do something 6 for (int i = 0; i < 10; i++) { 7 publishProgress(i); 8 } 9 return result; 10 } 11 12 @Override 13 protected void onPostExecute(final Boolean aBoolean) { 14 // Show result 15 } 16 17 @Override 18 protected void onProgressUpdate(final Integer... values) { 19 // Update progress 20 } 21 }
  • 13.
    AsyncTask И использование: 1 DemoAsyncTaskdemoTask= new DemoAsyncTask(); 2 demoTask.execute("Argument 1", "Argument 2"); Чем параметризуется AsycTask: 1. Тип массива входных аргументов в методе execute и doInBackground 2. Тип обновления прогресса 3. Тип результата Если что-то из этого не предполагается, можно использовать Void.
  • 14.
    AsyncTask В зависимости отверсии API AsyncTask ведет себя по-разному: 4 < API < 13 — каждый новый AsyncTask работает в новом потоке API ≥ 13 (3.2+) - на все AsyncTask выделяется один поток, но добавили поле AsyncTask.THREAD_POOL_EXECUTOR. Причем, THREAD_POOL_EXECUTOR неодинаков для разных API: API < 19 от 5 до 128 потоков, длина очереди — 10. API ≥ 19 от N+1 до 2N+1 потоков, длина очереди — 128. N - количество ядер процессора устройства.
  • 15.
  • 16.
    Service Сервис обязательно объявляетсяв манифесте 1 <manifest ... > 2 ... 3 <application ... > 4 <service android:name=".service.DemoService" /> 5 ... 6 </application> 7 </manifest> Также у тега <service> есть ряд атрибутов: name - имя класса, реализующего Service exported - могут ли другие приложения взаимодействовать с этим сервисом isolatedProcess - будет ли сервис работать как отдельный процесс permission - разрешение, требуемое для запуска этого сервиса process - имя процесса, в котором сервис будет запущен. label - имя процесса, отображаемое пользователю
  • 17.
    Приоритеты Service По умолчаниюсервисы имеют приоритет background Чтобы повысить приоритет до foreground или понизить обратно нужно воспользоваться методами: startForeground() stopForeground() Также можно узнать о том, что системе не хватает памяти и она может убить сервис: onLowMemory() onTrimMemory() — API >=14
  • 18.
    IntentService example 1 publicclass DemoIntentService extends IntentService { 2 public DemoIntentService() { 3 // Service name is used for debugging 4 super("DemoIntentService"); 5 } 6 7 @Override 8 protected void onHandleIntent(final Intent intent) { 9 // Do stuff 10 } 11 }
  • 19.
    Loader Появился в API11 Привязан к методам жизненного цикла activity или fragment Основные компоненты LoaderManager - создает, уничтожает, запускает, пересоздает Loader LoaderManager.LoaderCallbacks - интерфейс для связи клиента и менеджера Loader - объект, асинхронно выполняющий задачу.
  • 20.
    Работа с сетью Чащевсего под работой с сетью подразумевают взаимодействие с REST-подобными cервисами, т.е. HTTP + JSON/XML Для доступа в сеть требуется разрешение android.permission.INTERNET. Также может понадобиться разрешение android.permission.ACCESS_NETWORK_STATE для проверки доступности сети. Для отображения web-страниц можно использовать WebView - практически полноценный браузер. Начиная с Android 4.4 основан на Chromium и регулярно обновляется.
  • 21.
    Стандартные средства HttpClient -deprecated in API 22 (API >=23 доступно только в виде legacy-модуля) HttpUrlConnection
  • 22.
    HttpClient Типичный use-case Создается HttpClient(DefaultHttpClient или AndroidHttpClient) Создается и настраивается запрос – объект класса HttpUriRequest (обычно HttpGet или HttpPost) Выполняется запрос HttpClient.execute и получаем в ответ HttpResponse Разбирается HttpResponse – заголовки, строка ответа и т.д. Получается HttpEntity и разбирается тело ответа HttpClient
  • 23.
    HttpUrlConnection Типичный use-case Создается объектURL, вызывается openConnection() и делается приведение к HttpURLConnection Подготавливается запрос – устанавливается хедеры, параметры запроса, тип контента и т.д. По необходимости подгатавливается тело запроса (POST запрос). setDoOutput(true) и getOutputStream() Читается ответ сервера. Строку ответа (код, сообщение), заголовки, cookie и т.д. Закрывается соединение и освобождаются ресурсы
  • 24.
    HttpClient vs HttpUrlConnection ДляAPI ≥ 9 (2.3+) предпочтительнее HttpUrlConnection т.к. в нем меньше багов Для API < 9 лучше HttpClient
  • 25.
    OkHttp / Retrot OkHttp - библиотека для работы с HTTP от SquareUp. Retro t - библиотека для работы с REST API от SquareUp, на Android по умолчанию использует HttpUrlConnection. Однако лучше подключать OkHttp, из-за багов в HttpUrlConnection.
  • 26.
    Бочка дегтя 1. Асинхронностьчерез callback приводит к росту вложенности, отсюда вытекает запутанность кода, проблемы с сопровождением и т.д. (т. н. Callback-hell) 2. Компонент, запустивший AsyncTask запросто может благополучно умереть, что вызывает проблемы с доставкой результата 3. Регулярно приходится загружать картинки, зачастую адаптерах ListView 4. К бекэнду часто нужно строить кеш на стороне приложения 5. Часто результат выполнения сетевого запроса нужен в разных местах приложения
  • 27.
    Решения 1. RxJava 2. Проверять,жив ли компонент, подпирать все это костылями / использовать EventBus 3. Picasso / Glide / ... 4. ORM, сохранение в файлы, SharedPreferences — по ситуации 5. Otto / Event bus
  • 28.
    Дополнительные материалы Более подробнаяпрезентация о многопоточности и асинхронности Более подробная статья о сервисах Цикл статей о Loader с примерами Реактивные расширения RxJava Picasso Retro t Еще примеры