Android потоки
Потоки дозволяють виконувати кілька завдань одночасно, не заважаючи один одному, що дає можливість ефективно використовувати системні ресурси. Потоки використовуються в тих випадках, коли одне довгограюче дію не повинно заважати іншим діям. Наприклад, у нас є музичний програвач з кнопками відтворення та паузи. Якщо ви натискаєте кнопку відтворення і у вас запускається музичний файл в окремому потоці, то ви не можете натиснути на кнопку паузи, поки файл не відтворить повністю. За допомогою потоків ви можете обійти це обмеження.
Використання фонових потоків
Щоб бути впевненим, що ваш додаток не втрачає чуйності, хорошим рішенням стане переміщення всіх повільних, трудомістких операцій з головного потоку додатки в дочірній.
Всі компоненти програми в Android, включаючи активності, сервіси та приймачі широкомовних намірів, починають роботу в головному потоці програми. В результаті трудомісткі операції в будь-якому з цих компонентів блокують всі інші частини програми, включаючи сервіси та активності на передньому плані.
Застосування фонових потоків - необхідна умова, якщо ви хочете уникнути появи діалогового вікна для примусового закриття програми. Коли активність в Android протягом 5 секунд не відповідає на події призначеного для користувача введення (наприклад, натискання кнопки) або приймач широкомовних намірів завершує роботу обробника onReceive () протягом 10 секунд, вважається, що додаток зависло. Подібні ситуації слід уникати будь-яку ціну. Використовуйте фонові потоки для всіх трудомістких операцій, включаючи роботу з файлами, мережеві запити, транзакції в базах даних і складні обчислення.
Android надає кілька механізмів переміщення функціональності в фоновий режим.
- Activity.runOnUiThread (Runnable)
- View.post (Runnable)
- View.postDelayed (Runnable, long)
- Handlers
- AsyncTask
Хоча використання AsyncTask - гарне рішення, трапляється, що для роботи у фоновому режимі доводиться створювати власні потоки і управляти ними.
У Java є стандартний клас Thread. який ви можете використовувати в такий спосіб:
Даний спосіб підходить тільки для операцій, пов'язаних з часом. Але ви не зможете оновлювати графічний інтерфейс програми.
Якщо вам потрібно оновлювати інтерфейс програми, то потрібно використовувати AsyncTask. про який говорилося вище, або ви можете реалізувати ваш власний клас, успадкованих від Thread. використовуючи об'єкт Handler з пакета android.os для синхронізації з потоком GUI перед оновленням інтерфейсу.
Ви можете створювати дочірні потоки і управляти ними за допомогою класу Handler. а також класів, які доступні в просторі імен java.lang.Thread. Нижче показаний простий каркас для перенесення операцій в дочірній потік.
погане додаток
Напишемо "погане" додаток, неправильно використовує основний потік. Одного разу ми писали програму для підрахунку ворон. На цей раз будемо вважати чорних котів, які перебігають нам дорогу. Навіщо вони це роблять - мовчить наука. Може бути зібрана статистика допоможе розгадати таємницю. Додамо на екран активності кнопки і текстову мітку. Код для клацання кнопки.
Для імітації важкої роботи програма робить паузу на двадцять секунд, а потім виводить текст з підрахунком котів. Якщо натиснути на кнопку один раз і почекати двадцять секунд, то програма відпрацює як годиться. Але уявіть собі, що ви натиснули на кнопку один раз. Програма запустила паузу. Ви, не чекаючи закінчення паузи, знову натиснули на кнопку. Програма повинна виконати вашу команду, але попередня команда ще не відпрацювала і настає конфлікт. Спробуйте натиснути на кнопку кілька разів з невеликими перервами. У якийсь момент додаток зависне і виведе системне діалогове вікно:

У реальних додатках таке вікно може розлютити користувача і він поставить низьку оцінку вашого додатком.
Вам необхідно перенести трудомістку задачу в окремий потік. Для цього створюється екземпляр класу Runnable. у якого є метод run (). Далі створюється об'єкт Thread. в конструкторі у якого вказується створений Runnable. Після цього можна запускати новий потік за допомогою методу start (). Перепишемо приклад.
Весь код ми перенесли в метод run (). Тепер ви можете без упину клацати по кнопці. На цей раз додаток збереже свою працездатність. Щоб в цьому переконатися, в код додано протоколювання логів Log.i (). При кожному натисканні створюється новий потік, в якому виконується код. Потоки один одному не заважають і чекають своєї черги, коли система дозволить їм відпрацювати.
Потрібен якийсь посередник між створюваними потоками і основним UI-потоком. У ролі такого посередника є клас Handler (повна назва класу android.os.Handler. Не переплутайте). Вам потрібно створити екземпляр класу і вказати код, який потрібно виконати.
Після рядки коду з Log.i () додайте виклик методу посередника.
Потік викликає посередника, який в свою чергу оновлює інтерфейс. У нашому випадку посередник посилає пусте повідомлення від потоку.
Але буває так, що від потоку потрібно отримати інформацію для обробки. Нижче спрощений приклад.
запуск потоку
Припустимо, ми розробляємо власний програвач. У нас є кнопка Play. яка викликає метод play () для відтворення музики:
Тепер запустимо метод в іншому потоці. Спочатку створюється новий потік. Далі описується об'єкт Runnable в конструкторі потоку. А всередині створеного потоку викликаємо наш метод play (). І, нарешті, запускаємо потік.
приспати потік
Іноді необхідно тимчасово призупинити потік ( "приспати"):
пріоритет потоків
Щоб встановити пріоритетність процесу використовується метод setPriority (). який викликається до запуску потоку. Значення пріоритету може варіюватися від Thread.MIN_PRIORITY (1) до Thread.MAX_PRIORITY (10):
Скасування виконання потоку
У потоку є метод stop (). але використовувати його не рекомендується, оскільки він залишає додаток в невизначеному стані. Зазвичай використовують такий підхід:
Існує й інший спосіб, коли всі, що запускаються потоки оголошуються демонами. В цьому випадку всі запущені потоки будуть автоматично завершені при завершенні основного потоку програми: