Процеси та потоки в Android пишемо AsyncTask правильно

Продовжую свої розповіді про Android. І цього разу хочу поділитися цінною інформацією про процеси та потоки, яка має бути добре засвоєна і завжди залишатися під рукою, щоб уникнути помилок та непорозуміння при написанні додатків. Наприкінці статті наведу приклад реалізації AsyncTask, який завантажує в ImageView картинку натисканням на кнопку. Насамперед зазначу, що докладніше про цю тему можна прочитати в цьому посібнику — developer.android.com/guide/topics/fundamentals/processes-and-threads.html

На замітку про процеси та потоки в Android

Коли запускається компонент програми та програма не має інших запущених компонентів, Android створює новий процес для програми з одним потоком виконання. За замовчуванням усі компоненти однієї програми запускаються в одному процесі, у потоці званому «головний». Якщо компонент програми запускається і вже існує процес для цієї програми (якийсь компонент із програми існує), тоді компонент запущений у цьому процесі і використовує його потік виконання. Ви можете змінити цю поведінку, задавши різні процеси для різних компонентів вашої програми. Крім того, ви можете додати потоки в будь-який процес.

Задати окремий процес компонента можна за допомогою файлу маніфесту. Кожен тег компонента(activity, service, receiver та provider) підтримує атрибут android:process. Цей атрибут дозволяє задати процес, у якому виконуватиметься компонент. Також ви можете задати процес, в якому будуть виконуватися компоненти різних програм. Також цей атрибут підтримується тегом application, що дозволяє встановити певний процес для всіх компонентів програми.

Android намагаєтьсяпідтримувати процес програми якомога довше, але коли будуть потрібні ресурси старі процеси будуть витіснені по ієрархії важливості.

Існує 5 рівнів ієрархії важливості: (процеси першого рівня зі списку будуть видалені останніми)

1.Процес з яким взаємодіє користувач (Foreground process)До таких процесів відноситься наприклад: активіти з яким взаємодіє користувати; сервіс(примірник Service), з яким взаємодіє користувач; сервіс запущений методом startForeground(); сервіс, який виконує один із методів свого життєвого циклу; BroadcastReceiver який виконує метод onReceive().

2.Очевидний процесПроцес, в якому не виконані умови з пункту №1, але який впливає на те, що користувач бачить на екрані. Наприклад, викликаний спосіб onPause() активити.

3.Сервісний процесСлужба запущена методом startService()

4.Фоновий процесПроцес виконується у фоновому режимі, який не бачив користувачу.

Зазначу, що в компонентах програми існує метод onLowMemory(), але покладатися на те, що даний метод буде викликаний не можна, також як не можна на 100% покладатися на метод onDestroy(), тому логіку збереження даних або налаштувань можна здійснити в методі onStop(), який(як запевняють) точно викликається.

Коли запускається програма, система створює "головний" потік виконання для цієї програми, який також називається UI-потоком. Цей потік дуже важливий, оскільки саме в ньому відбувається малювання віджетів (кнопочок, списків), обробка подій вашої програми. Система не створює окремого потоку для кожного екземпляра компонента. Всі компоненти, які запущені в одному процесі, будуть створені в потоці UI. Бібліотека користувачаінтерфейсу Android не є потоково-безпечною, тому необхідно дотримуватися двох важливих правил:

1) Не блокувати потік UI 2) Не звертатися до компонентів інтерфейсу користувача не з UI-потоку

Тепер, припустимо, що у нас виникло завдання - завантажити картину в ImageView з мережі і відразу її відобразити. Як ми вчинимо? За логікою: ми створимо окремий потік, який і зробить всю нашу роботу приблизно так:

Виглядає правдоподібно, оскільки ми винесли операцію завантаження картинки окремий потік. Проблема у тому, що ми порушили правило №2. Виправити цю проблему можна за допомогою таких методів:

Activity.runOnUiThread(Runnable) View.post(Runnable) View.postDelayed(Runnable, long)

Наприклад, скористаємося першою з них:

Тепер реалізація потоково-безпечна: мережна операція виконується в окремому потоці, а до ImageView звертаємось із потоку UI. На щастя, дані операції можна об'єднати за допомогою успадкування класу Handler та реалізації потрібної логіки, але найкраще рішення - успадкувати клас AsyncTask.

AsyncTask дозволяє виконати асинхронну роботу і робити оновлення інтерфейсу користувача. Для оновлення реалізуйте метод onPostExecute(), а всю фонову роботу включіть у метод doInBackground(). Після того, як ви реалізуєте своє завдання, необхідно її запустити методом execute().

Наводжу обіцяний приклад AsyncTask, в якому реалізовано завдання завантаження та відображення картинки (варіант з анотаціями та відхиленням від застосування стандартного протоколу діалогів):

А тепер розглянемо найправильніший варіант з погляду роботи з діалогами:

Кода побільшало, але краще використовувати стандартний протокол роботи з діалогами. Також я прибрав всеІнструкції, щоб новачкам було простіше спробувати цей код.

Не забудьте додати до своєї розмітки кнопочки атрибут із зазначеним значенням: andro > І додам: в офіційному документі(Тиц) також, як і в моєму випадку, не використовується preExecute(), але якщо вам знадобиться виконати якісь дії з вашим інтерфейсом користувача до початку виконання завдання, то сміливо використовуйте даний метод.

Код досить простий: всю фонову роботу ми виконуємо у методі doInBackGround(), викликаючи метод publishProgress(), щоб під час завантаження картинки крутився наш ProgressDialog під час виклику методу onProgressUpdate(). Після виконання фонової роботи викликається метод onPostExecute() у який передається результат роботи методу doInBackGround(), у нашому випадку це об'єкт Bitmap і ми його завантажуємо в ImageView. Зазначу пару важливих моментів, які потрібно враховувати:

1) Метод doInBackGround() виконується у фоновому потоці, тому доступу до потоку UI всередині цього методу немає. 2) Методи onPostExecute() та onProgressUpdate() виконуються в потоці UI, тому ми можемо сміливо звертатися до наших компонентів UI.

Так, я знову застосував бібліотеку android-annotations, так що не лякайтеся інструкцій.

Хочу наголосити на важливості розуміння моделі роботи процесів в Android, щоб не допустити помилок при розробці додатків.

UPDСтаття оновлена ​​додаванням більш правильної версії щодо роботи з діалогами.