Процеси і потоки, android developers

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

У цьому документі обговорюється робота процесів і потоків в додатку Android.

За замовчуванням всі компоненти однієї програми працюють в одному процесі, і більшість додатків не повинно змінювати цю поведінку. Однак, якщо необхідно контролювати, до якого процесу належать певний компонент, можна зробити це в файлі маніфесту.

Запис маніфесту для кожного типу елементів компонента - . . і -підтримує атрибут android: process. дозволяє задавати процес, в якому слід виконувати цей компонент. Можна встановити цей атрибут так, щоб кожен компонент виконувався у власному процесі, або так, щоб тільки деякі компоненти спільно використовували один процес. Можна також налаштувати процес android: process так, щоб компоненти різних додатків виконувалися в одному процесі, за умови що додатки спільно використовують один ідентифікатор користувача Linux і виконують вхід з одним сертифікатом.

елемент також підтримує атрибут android: process. дозволяє задати значення за замовчуванням, яке застосовується до всіх компонентів.

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

Приймаючи рішення про переривання процесів, система Android зважує їх відносну важливість для користувача. Наприклад, більш імовірно вимикання процесів, що містять дії, які не відображаються на екрані, в порівнянні з процесом, що містить видимі дії. Отже, рішення про переривання процесу залежить від стану компонентів, що працюють в цьому процесі. Нижче обговорюються правила, на підставі яких приймається рішення про вибір перериваються процесів.

Життєвий цикл процесу

Система Android намагається зберігати процес додатки якомога довше, але в кінцевому рахунку змушена видаляти старі процеси, щоб відновити пам'ять для нових або більш важливих процесів. Щоб визначити, які процеси зберегти, а які видалити, система поміщає кожен процес в «ієрархію важливості» на основі компонентів, що виконуються в процесі, і стану цих компонентів. Процеси з найнижчим рівнем важливості виключаються в першу чергу, потім виключаються процеси наступного рівня важливості і т. Д. Наскільки це необхідно для відновлення ресурсів системи.

В ієрархії важливості передбачено п'ять рівнів. У наступному списку представлені різні типи процесів в порядку важливості (перший процес є найбільш важливим і віддаляється в останню чергу):

  1. Процес переднього плану

Процес, необхідний для поточної діяльності користувача. Процес вважається процесом переднього плану, якщо виконується будь-яка з наступних умов:

  • Він містить дію Activity. з яким взаємодіє користувач (викликаний метод Activity onResume ()).
  • Він містить службу Service. пов'язану з дією, з яким взаємодіє користувач.
  • Він містить службу Service. яка виконується "на передньому плані", - службу, яка називається startForeground ().
  • Він містить службу Service. яка виконує один з зворотних викликів життєвого циклу (onCreate (). onStart () або onDestroy ()).
  • Він містить ресивер BroadcastReceiver. який виконує метод onReceive ().

Зазвичай одночасно працює лише кілька процесів переднього плану. Вони знищуються тільки в крайньому випадку, якщо пам'яті залишається так мало, що вони не можуть продовжувати спільну роботу. Зазвичай в цей момент пристрій досягло стану розбиття пам'яті на сторінки, тому для того, щоб користувальницький інтерфейс відгукувався на дії користувача, необхідно видалення деяких процесів на передньому плані.

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

  • Він містить дію Activity. яка не перебуває на передньому плані, але видно користувачеві (викликаний метод onPause ()). Наприклад, це може відбуватися, якщо дія на передньому плані запустило діалогове вікно, яке дозволяє бачити попередні дії позаду нього.
  • Він містить службу Service. пов'язану з видимим дією або дією переднього плану.

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

Процес, що містить дії, які не видно користувачеві в даний час (викликаний метод onStop () дії). Ці процеси не роблять безпосереднього впливу на роботу користувача, і система може видалити їх в будь-який момент, щоб звільнити пам'ять для процесів переднього плану, видимих ​​або службових процесів. Зазвичай виконується безліч фонових процесів, тому вони зберігаються в списку LRU (недавно використані), щоб процеси, що містять самі недавні дії, які бачив користувач, віддалялися в останню чергу. Якщо для дії правильно реалізовані методи життєвого циклу, і дія зберігає поточний стан, видалення процесу цієї дії не надає видимого впливу на роботу користувача, так як коли користувач повертається до цього дійства, воно відновлює всі елементи свого видимого стану. Інформацію про збереження та відновлення стану см. В документі Дії.

Процес, який не містить ніяких компонентів активного застосування. Єдина причина зберігати процес такого типу - це кешування, яке покращує час наступного запуску компонента в цьому процесі. Система часто видаляє ці процеси для рівномірного розподілу всіх системних ресурсів між кешем процесу і кешем базового ядра.

Система Android відносить процес до максимально високого рівня на основі важливості компонентів, активних в процесі в даний час. Наприклад, якщо процес містить службове і видиме дію, процес вважається видимим, а не службовим процесом.

Крім того, рівень процесу може бути підвищений, оскільки є інші процеси, залежні від нього. Наприклад, процес, який обслуговує інший процес, не може мати рівень нижче рівня обслуговується процесу. Наприклад, якщо постачальник контенту в процесі A обслуговує клієнта в процесі B або службовий процес A пов'язаний з компонентом в процесі B, процес A завжди вважається не менш важливим, ніж процес B.

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

При запуску програми система створює потік виконання для додатка, який називається «головним». Цей потік дуже важливий, так як він відповідає за диспетчеризацію подій на віджети відповідного інтерфейсу користувача, включаючи події графічного представлення. Він також є потоком, в якому додаток взаємодіє з компонентами з набору інструментів призначеного для користувача інтерфейсу Android (компонентами з пакетів android.widget і android.view). По суті, головний потік - це те, що іноді називають потоком користувальницького інтерфейсу.

Система не створює окремого потоку для кожного екземпляра компонента. Всі компоненти, які виконуються в одному процесі, створюють екземпляри в потоці призначеного для користувача інтерфейсу, і системні виклики кожного компонента відправляються з цього потоку. Тому методи, які відповідають на системні зворотні виклики (такі як метод onKeyDown () для повідомлення про дії користувача або метод зворотного виклику життєвого циклу), завжди виконуються в потоці призначеного для користувача інтерфейсу процесу.

Наприклад, коли користувач натискає кнопку на екрані, потік користувальницького інтерфейсу вашої програми відправляє подія натискання в віджет, який, в свою чергу, встановлює кнопку в натисканні стан і відправляє запит на анулювання в чергу подій. Потік призначеного для користувача інтерфейсу виключає запит з черги і повідомляє віджет, що він повинен відобразитися повторно.

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

Крім того, набір інструментів для користувача інтерфейсу Andoid не є потокобезпечна. Тому, ви не повинні працювати з призначеним для користувача інтерфейсом з робочого потоку. Маніпуляції з призначеним для користувача інтерфейсом необхідно виконувати з потоку призначеного для користувача інтерфейсу. Таким чином, існує тільки два правила однопоточному моделі Android:

  1. Чи не блокуйте потік призначеного для користувача інтерфейсу
  2. Не звертайтесь до набору інструментів призначеного для користувача інтерфейсу Android зовні потоку призначеного для користувача інтерфейсу

робочі потоки

Внаслідок описаної вище однопоточному моделі для динамічності призначеного для користувача інтерфейсу ваших додатків дуже важливо не блокувати потік призначеного для користувача інтерфейсу. Щоб здійснити операції, що займають певний час, обов'язково виконуйте їх в окремих потоках ( "фонових» або «робочих» потоках).

Наприклад, нижче наведено код контролю натискань, який завантажує зображення з окремого потоку і відображає їх у віджеті ImageView:

На перший погляд, він повинен працювати добре, так як він створює новий потік для обробки мережевої операції. Однак, він порушує друге правило однопоточному моделі: не звертайтеся до набору інструментів призначеного для користувача інтерфейсу Android зовні потоку призначеного для користувача інтерфейсу - цей приклад змінює ImageView з робочого потоку, а не з потоку призначеного для користувача інтерфейсу. Це може привести до невизначеного і непередбаченого поведінки, відстежити яке буде важко.

Для усунення цієї проблеми Android пропонує кілька шляхів доступу до потоку призначеного для користувача інтерфейсу з інших потоків. Нижче наведено список корисних методів:

Наприклад, можна виправити наведений вище код за допомогою методу View.post (Runnable):

Тепер реалізація є потокобезпечна: мережева операція виконується з окремого потоку, тоді як ImageView працює з потоку призначеного для користувача інтерфейсу.

Однак у міру зростання складності, код такого типу може ставати заплутаним і складним для підтримки. Щоб обробляти більш складні взаємодії з робочим потоком, можна використовувати метод Handler в робочому потоці для обробки повідомлень, що надходять з потоку призначеного для користувача інтерфейсу. Ймовірно, найкращим рішенням є розширення класу AsyncTask. яке спрощує виконання завдань робочого потоку, які повинні взаємодіяти з призначеним для користувача інтерфейсом.

Використання AsyncTask

Метод AsyncTask дозволяє виконувати асинхронну роботу в інтерфейсі. Він виконує операції блокування в робочому потоці і потім публікує результати в потоці призначеного для користувача інтерфейсу без необхідності самостійно обробляти потоки і / або обробники.

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

Наприклад, можна реалізувати попередній приклад за допомогою методу AsyncTask наступним чином:

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

Прочитайте статтю AsyncTask. щоб повністю зрозуміти використання цього класу. Тут наведено короткий огляд його роботи:

  • Можна вказувати тип параметрів, значення ходу виконання і кінцеве значення завдання за допомогою універсальних компонентів
  • Метод doInBackground () виконується автоматично в робочому потоці
  • Методи onPreExecute (). onPostExecute () і onProgressUpdate () запускаються в потоці призначеного для користувача інтерфейсу
  • Значення, повернене методом doInBackground (). відправляється в метод onPostExecute ()
  • Можна викликати publishProgress () в будь-який момент в doInBackground () для виконання onProgressUpdate () в потоці призначеного для користувача інтерфейсу
  • Завдання можна скасувати в будь-який момент з будь-якого потоку

Попередження! Інша проблема, з якою ви можете зіткнутися при використанні робочого потоку, складається в непередбачуваному перезапуску дії внаслідок зміни конфігурації в режимі виконання. (Наприклад, коли користувач змінює орієнтацію екрану), що може зруйнувати робочий потік. Щоб побачити, як можна зберегти завдання під час одного з подібних перезапусків і як правильно скасувати завдання при руйнуванні дії, вивчіть вихідний код прикладу додатки Shelves.

потокобезпечна методи

У деяких ситуаціях реалізовані методи можуть викликатися з декількох потоків і, отже, повинні бути написані зі збереженням потокобезпечна.

В першу чергу це відноситься до методів, які можна викликати віддалено, наприклад, до методів в пов'язаної службі. Коли виклик методу реалізується в класі IBinder. що відбувається з того ж процесу, в якому виконується IBinder. метод виконується в потоці викликає методу. Однак, коли виклик відбувається з іншого процесу, метод виконується в потоці, обраному з пулу потоків, які система підтримує в тому ж процесі, що і IBinder (він не виконується в потоці призначеного для користувача інтерфейсу процесу). Наприклад, оскільки метод onBind () служби буде викликатися з потоку призначеного для користувача інтерфейсу процесу служби, методи, реалізовані в об'єкті, який повертає onBind () (наприклад, підклас, який реалізує методи RPC), будуть викликатися з потоків в пулі. Так як служба може мати кілька клієнтів, кілька потоків з пулу можуть одночасно використовувати один і той же метод IBinder. Тому методи IBinder повинні бути реалізовані з збереженням потокобезпечна.

Аналогічним чином постачальник контенту може отримувати запити даних, які відбуваються з іншого процесу. Хоча класи ContentResolver і ContentProvider приховують подробиці управління взаємодією процесів, методи ContentProvider. які відповідають на ці запити, -Методи query (). insert (). delete (). update () і getType ()-викликають з пулу потоків в процесі постачальника контенту, а не в процесі потоку призначеного для користувача інтерфейсу. Оскільки ці методи можуть викликатися з будь-якого числа потоків одночасно, вони також повинні бути реалізовані з збереженням потокобезпечна.

взаємодія процесів

Для виконання IPC програма має бути прив'язане до служби за допомогою методу bindService (). Додаткові відомості представлені в розділі Служби керівництва для розробників.

Follow @AndroidDev on Twitter

Follow Android Developers on Google+

Check out Android Developers on YouTube

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is. You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.