Программирование приложений для мобильных устройств под управлением Android. Часть 2 - страница 4
«Слушатель» кнопки «Load Icon» вызывает метод loadIcon (), который показан чуть ниже. Этот код создает новый поток, который занимает время, загружая битовый массив. И затем пытается установить изображение в image view, который является частью лейаута.
Теперь, если запустить приложение и нажать кнопку «Load Icon», а затем сразу нажать «Other Button», видно, что есть реакция на «Other Button» и её нажатие не блокируется. Это хорошо, есть прогресс. Однако появилась большая проблема, через несколько секунд приложение рухнуло.
Если исследовать журнал logcat, мы увидим, что есть сообщение о том, что только тот поток, в котором создана иерархия вьюшек, может манипулировать своими вьюшками.
Это означает, что новый поток, который мы создали, чтобы загрузить битовый массив, может сделать эту работу, но он не сможет сделать последний шаг и вывести получившееся изображение на дисплей.
И так, какой же поток на самом деле создал иерархию вьюшек этого приложения? У всех Android приложений есть основной поток, который также называют UI thread – потоком пользовательского интерфейса. Все компоненты приложения, которые работают в одном и том же процессе, запущенном по умолчанию, используют тот же поток – UI thread. Все методы жизненного цикла: OnCreate, OnStart и т.д., работают в основном потоке. И, кроме того, сам инструментарий пользовательского интерфейса не ориентирован на многопотоковое исполнение. Все это означает, что, если вы заблокируете UI-поток любой продолжительной работой, это воспрепятствует реакции приложения на другие манипуляции пользователя. Следовательно, долго выполняющиеся операции должны быть помещены в фоновые потоки. Однако, вместе с этим, мы не сможем получить доступ к инструментарию пользовательского интерфейса от потока, не принадлежащего UI tread. Таким образом, мы должны делать длительную работу в фоновом потоке, но когда эта работа сделана, мы должны сделать обновление пользовательского интерфейса в потоке UI.
И Android на самом деле дает нам набор средств сделать это. В частности Android обеспечивает несколько методов, которые гарантированно будут работать в потоке пользовательского интерфейса. Два из них – это метод post класса View и runOnUiThread класса Activity. Оба этих метода получают параметр Runnable, содержащий код, который, например, обновляет экран в наших недавних примерах. Таким образом, если мы используем эти методы, мы могли бы загрузить битовый массив в фоновом потоке, и когда эта операция завершится, мы использовали бы один из этих методов, чтобы выполнить Runnable, который и выведет изображение на экран.
Давайте рассмотрим, как это реализуется в коде. Откроем основную Activity этого приложения и перейдем прямо к методу loadIcon, который вызывается, когда пользователь нажимает кнопку «Load Icon». Как и в предыдущем примере, этот код создает новый поток и затем загружает битовый массив. Но после загрузки данных, теперь происходит вызов view.post, запускающий новый Runnable, код которого и вызывает метод setImageBitmap, чтобы установить загруженное изображение в соответствующий imageView.
Следующий класс, обеспечивающий поточную обработку, это класс AsyncTask. Он обеспечивает правильное и простое использование потока пользовательского интерфейса. Этот класс служит общей основой для управления задачами, которые, как в наших предыдущих примерах, включают операции, которые должны выполняться в фоновом потоке, а их результат публикуется в UI потоке. Основой использования AsyncTask является то, что работа разделена между фоновым потоком и потоком UI. Фоновый поток выполняет длительную операцию и может дополнительно сообщать о ее прогрессе. Поток пользовательского интерфейса, с другой стороны, ответственен за начальную настройку длительно выполняемой операции, за публикацию промежуточной информации о прогрессе и за завершение операции после того, как фоновый поток сделал свою работу.