Главная » Разработка для Android » Потоки в процессе Android

0

 

AsyncTask и ContentProvider вместе образуют очень мощную идиому, которая может быть адаптирована к разнообразным видам распространенных архитектур приложения. Почти любой паттерн «Модель-вид-контроллер» (MVC), в котором вид опрашивает модель, может (и, пожалуй, должен) реализовываться таким образом. Если архитектура приложения требует, чтобы модель отправляла изменения в вид, или в данной архитектуре модель является долгоживущей или работает непрерывно, одного только AsyncTask может быть недостаточно.

Вспомним основополагающее правило обмена данными между потоками. В самом общем виде это правило очень обременительное. Но исследование AsyncTask, сделанное нами в предыдущем подразделе, демонстрирует идиому, которая упрощает правильную координацию конкурентных задач в Android: сложная работа, заключающаяся в публикации состояния одного потока и предоставлении этой информации другому потоку, полностью скрыта в реализации класса-шаблона. В то же время в предыдущем подразделе мы дополнительно заострили внимание на некоторых ловушках, связанных с конкурентным программированием, в которые часто попадаются невнимательные разработчики. Существуют и другие безопасные идиомы, способные упростить решение некоторых проблем, связанных с конкурентным программированием. Одна из этих идиом – широко применяемая в Java вообще – внедрена во фреймворке Android. Иногда ее называют привязкой к потоку (thread confinement).

Предположим, что поток DBMinder создает объект и изменяет его в течение некоторого времени. Завершив работу, он должен передать объект другому потоку, DBViewer, для дальнейшей обработки. Для этого DBMinder и DBViewer, использующие привязку к потоку, должны совместно использовать точку сброса объекта (drop point) и связанную с ней блокировку. Процесс строится таким образом.

1. DBMinder захватывает блокировку и сохраняет ссылку на объект, находящийся в точке сброса.

2. DBMi nder уничтожает все ссылки на объект!

3. DBMi nder снимает блокировку.

4. DBViewer захватывает блокировку и обнаруживает в точке сброса ссылку на объект.

5. DBViewer восстанавливает ссылку из точки сброса, а потом очищает эту точку.

6. DBViewer снимает блокировку.

Такой процесс работает с любым объектом, независимо от того, гарантируется ли в самом этом объекте безопасность потоков. Это обусловлено тем, что поле сброса является единственным состоянием, которое когда-либо в ходе работы программы совместно используется несколькими потоками. В нашем случае оба потока корректно захватывают единственную блокировку, прежде чем получить доступ к точке сброса. Когда DBMinder завершает работу с объектом, он передает объект к DBViewer и не сохраняет никаких ссылок на этот объект: состояние переданного объекта никогда не используется совместно несколькими потоками.

Привязка к потоку – это удивительно мощный инструмент. Обычно реализации используют в качестве общей точки сброса упорядоченную очередь задач. Несколько потоков могут одновременно требовать захвата блокировки, но удержание блокировки одним потоком длится не дольше, чем необходимо для постановки задачи в очередь. Один или несколько потоков-исполнителей захватывают блокировку очереди, чтобы взять из нее задачи для выполнения. Подобный паттерн иногда называют «модель "поставщик/потребитель"» (producer/consumer model). Пока фрагмент работы может выполняться полностью в контексте потока-исполнителя, запросившего этот фрагмент, дальнейшей синхронизации не требуется. Внимательно изучив реализацию AsyncTask, вы поймете, как именно все это работает.

Привязка к потоку настолько полезна, что в Android она внедрена во фреймворк системы в виде класса под названием Looper. При инициализации Looper поток Java превращается в очередь задач. Весь свой жизненный цикл он тратит на взятие задач из локальной очереди и их выполнение. Другие потоки ставят задачи в очередь для обработки со стороны инициализированного процесса, как показано выше. Поскольку поток, ставящий задачи в очередь, удаляет все ссылки на объекты, которые в эту очередь ставит, оба потока можно программировать, не волнуясь о проблемах, связанных с конкурентной обработкой. Кроме того что такой подход радикально упрощает корректное написание программ, он еще и устраняет неэффективное использование ресурсов, которое может быть обусловлено чрезмерными затратами на синхронизацию.

Возможно, это описание очереди задач напоминает вам о той концепции, которую мы затрагивали в начале данной главы? Однопоточный событийно-управля-емый пользовательский интерфейс Android – это, в сущности, Looper. При запуске Context система выполняет некоторую регистрацию, а потом инициализирует поток запуска как Looper. Этот поток становится основным потоком службы и потоком пользовательского интерфейса для активности. В активности фреймворк пользовательского интерфейса сохраняет ссылку на этот поток, и его очередь задач становится очередью событий пользовательского интерфейса: все внешние драйверы, экран, клавиатура, обработчик вызовов и т. д. направляют задачи в эту очередь.

Вторая половина Looper называется Hand! er. Handl ег, создаваемый в потоке Looper, предоставляет портал для очереди Looper. Когда поток Looper хочет позволить какому-нибудь другому потоку, стоящему в очереди, получить доступ к очереди задач, он создает новый Handlеr и передает его другому потоку. Чтобы упростить работу с Handler, используется несколько кодовых комбинаций: View.post(Runnable), View. postDelayedCRunnable, long) и Activity. runOnUiThread(Runnable).

В инструментарии Android имеется еще одна удобная и мощная парадигма, предназначенная для межпроцессной коммуникации и разделения работы: ContentProvider. Прежде чем строить собственную архитектуру, основанную на низкоуровневых компонентах, рассматриваемых в этом разделе, подумайте, не достаточно ли для решения стоящей перед вами задачи обычного поставщика содержимого. Поставщики содержимого – гибкие и расширяемые компоненты, при этом параллельное исполнение с их применением протекает у достаточно быстро во всех приложениях, кроме большинства программ, чувствительных к задержкам (time-sensitive).

Источник: Android. Программирование на Java для нового поколения мобильных устройств

По теме:

  • Комментарии