Android thread view hierarchy

Only the original thread that created a view hierarchy can touch its views

Fix Android CalledFromWrongThreadException

In this tutorial, we shall learn to fix Android Exception CalledFromWrongThreadException : Only the original thread that created a view hierarchy can touch its views.

You might have come across this error while from a thread you are trying to set some property of a View or extract some property of a View, that belong to UI thread.

An important point to be remembered while trying to do any operations with UI views is that, only UI thread that created a view hierarchy can touch its views.

Following is simple example, where we try to access a TextView (that belong to UI thread) in another thread.

MainActivity.kt

You might see below exception in Logcat

Following is the line of code that caused this exception

In general, you touched an UI View from another thread, which you should not do without any help.

To fix this error, wrap the code that has to be executed on UI thread in a Runnable instance passed to runOnUiThread() method.

Following is the corrected MainActivity.kt

MainActivity.kt

Conclusion

In this Kotlin Android Tutorial, we have learnt how to fix Android Exception CalledFromWrongThreadException : Only the original thread that created a view hierarchy can touch its views.

Источник

Android «Только исходный поток, создавший иерархию представлений, может касаться его представлений».

Я построил простой музыкальный проигрыватель в Android. Представление для каждой песни содержит SeekBar, реализованный следующим образом:

Это отлично работает. Теперь я хочу, чтобы таймер отсчитывал секунды / минуты хода песни. Так что я положил TextView в макете, получить его findViewById() в onCreate() , и поместить его в run() после того, как progress.setProgress(pos) :

Но эта последняя строка дает мне исключение:

android.view.ViewRoot $ CalledFromWrongThreadException: только исходный поток, создавший иерархию представлений, может касаться его представлений.

Тем не менее, я делаю здесь в основном то же самое, что я делаю SeekBar — создавая представление onCreate , затем касаясь его run() — и это не вызывает у меня жалобы.

Вы должны переместить часть фоновой задачи, которая обновляет пользовательский интерфейс, в основной поток. Для этого есть простой фрагмент кода:

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

Я решил это, вставив runOnUiThread( new Runnable()< .. внутрь run() :

Мое решение этого:

Вызовите этот метод в фоновом потоке.

Обычно любое действие, связанное с пользовательским интерфейсом, должно выполняться в основном потоке или потоке пользовательского интерфейса, то есть в том, в котором onCreate() выполняется обработка событий. Один из способов убедиться в этом — использовать runOnUiThread () , другой — использовать обработчики.

ProgressBar.setProgress() имеет механизм, который он всегда будет выполнять в основном потоке, поэтому он работал.

Я был в этой ситуации, но я нашел решение с помощью объекта Handler.

В моем случае я хочу обновить ProgressDialog с помощью шаблона наблюдателя . Мой взгляд реализует наблюдателя и переопределяет метод обновления.

Итак, мой основной поток создает представление, а другой поток вызывает метод update, который обновляет ProgressDialop и .

Только исходный поток, создавший иерархию представлений, может касаться его представлений.

Возможно решить проблему с Объектом Обработчика.

Ниже приведены различные части моего кода:

Это объяснение можно найти на этой странице , и вы должны прочитать «Пример ProgressDialog со вторым потоком».

Читайте также:  Last day on earth survival не загружается до конца android

Вы можете использовать Handler для удаления вида, не нарушая основной поток пользовательского интерфейса. Вот пример кода

Я вижу, что вы приняли ответ @ провидения. На всякий случай, вы также можете использовать обработчик тоже! Сначала сделайте поля int.

Затем создайте экземпляр обработчика как поле.

Наконец, поместите это в onCreate() метод.

У меня была похожая проблема, и мое решение некрасиво, но оно работает:

Я использую Handler с Looper.getMainLooper() . Это работало хорошо для меня.

Используйте этот код, и не нужно runOnUiThread функционировать:

Это явно выдает ошибку. В нем говорится, какой поток создал вид, только тот, который может касаться его. Это потому, что созданный вид находится внутри пространства этого потока. Создание представления (GUI) происходит в потоке пользовательского интерфейса (основного). Таким образом, вы всегда используете поток пользовательского интерфейса для доступа к этим методам.

На рисунке выше переменная прогресса находится внутри пространства потока пользовательского интерфейса. Таким образом, только поток пользовательского интерфейса может получить доступ к этой переменной. Здесь вы получаете доступ к прогрессу через новую Thread (), и именно поэтому вы получили ошибку.

Это случилось с моим , когда я назвал для изменения пользовательского интерфейса из doInBackground из Asynctask вместо того , чтобы использовать onPostExecute .

Работа с пользовательским интерфейсом onPostExecute решает мою проблему.

Сопрограммы Kotlin могут сделать ваш код более кратким и читабельным, например:

Источник

Android UI thread

Большая часть кода Android приложения работает в контексте компонент, таких как Activity, Service, ContentProvider или BroadcastReceiver. Рассмотрим, как в системе Android организованно взаимодействие этих компонент с потоками.

При запуске приложения система выполняет ряд операций: создаёт процесс ОС с именем, совпадающим с наименованием пакета приложения, присваивает созданному процессу уникальный идентификатор пользователя, который по сути является именем пользователя в ОС Linux. Затем система запускает Dalvik VM где создаётся главный поток приложения, называемый также «поток пользовательского интерфейса (UI thread)». В этом потоке выполняются все четыре компонента Android приложения: Activity, Service, ContentProvider, BroadcastReceiver. Выполнение кода в потоке пользовательского интерфейса организованно посредством «цикла обработки событий» и очереди сообщений.

Рассмотрим взаимодействие системы Android с компонентами приложения.

Activity. Когда пользователь выбирает пункт меню или нажимает на экранную кнопку, система оформит это действие как сообщение (Message) и поместит его в очередь потока пользовательского интерфейса (UI thread).

Service. Исходя из наименования, многие ошибочно полагают, что служба (Service) работает в отдельном потоке (Thread). На самом деле, служба работает так же, как Activity в потоке пользовательского интерфейса. При запуске локальной службы командой startService, новое сообщение помещается в очередь основного потока, который выпонит код сервиса.

BroadcastReceiver. При создании широковещательного сообщения система помещает его в очередь главного потока приложения. Главный поток позднее загрузит код BroadcastReceiver который зарегистрирован для данного типа сообщения, и начнёт его выполнение.

ContentProvider. Вызов локального ContentProvider немного отличается. ContentProvider также выполняется в основном потоке но его вызов является синхронным и для запуска кода ContentProvider не использует очередь сообщений.

Исходя из вышесказанного можно заметить, что если главный поток в данный момент обрабатывает пользовательский ввод или выполняет иное действие, выполнение кода, полученного в новом сообщении, начнётся только после завершения текущей операции. Если какая либо операция в одном из компонентов потребует значительного времени выполнения, пользователь столкнётся или с анимацией с рывками, или с неотзывчивыми элементами интерфейса или с сообщением системы «Приложение не отвечает» (ANR).

Для решения данной проблемы используется парадигма параллельного программирования. В Java для её реализации используется понятие потока выполнения (Thread).

Thread: поток, поток выполнения, иногда ещё упоминается как нить, можно рассматривать как отдельную задачу, в которой выполняется независимый набор инструкций. Если в вашей системе только один процессор то потоки выполняются поочередно (но быстрое переключение системы между ними создает впечатление параллельной или одновременной работы). На диаграмме показано приложение, которое имеет три потока выполнения:

Читайте также:  Connect android and google

Но, к сожалению, для взаимодействия с пользователем, от потока мало пользы. На самом деле, если вы внимательно посмотрите на диаграмму выше, вы поймёте, что как только поток выполнить все входящие в него инструкции он останавливается и перестаёт отслеживать действия пользователя. Чтобы избежать этого, нужно в наборе инструкций реализовать бесконечный цикл. Но возникает проблема как выполнить некое действие, например отобразить что-то на экране из другого потока, иными словами как вклиниться в бесконечный цикл. Для этого в Android можно использовать Android Message System. Она состоит из следующих частей:

Looper: который ещё иногда ещё называют «цикл обработки событий» используется для реализации бесконечного цикла который может получать задания используется. Класс Looper позволяет подготовить Thread для обработки повторяющихся действий. Такой Thread, как показано на рисунке ниже, часто называют Looper Thread. Главный поток Android на самом деле Looper Thread. Looper уникальны для каждого потока, это реализованно в виде шаблона проектирования TLS или Thread Local Storage (любопытные могут посмотреть на класс ThreadLocal в Java документации или Android).

Message: сообщение представляет собой контейнер для набора инструкций которые будут выполнены в другом потоке.

Handler: данный класс обеспечивает взаимодействие с Looper Thread. Именно с помощью Handler можно будет отправить Message с реализованным Runnable в Looper, которая будет выполнена (сразу или в заданное время) потоком с которым связан Handler. Код ниже иллюстрирует использование Handler. Этот код создаёт Activity которая завершиться через определённый период времени.

HandlerThread: написание кода потока реализующего Looper может оказаться не простой задачей, чтобы не повторять одни и те же ошибки система Android включает в себя класс HandlerThread. Вопреки названию этот класс не занимается связью Handler и Looper.

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

Выполнение операций в отдельном потоке, не означает, что вы можете делать все что угодно, не влияя на производительность системы. Никогда не забывайте, что написанный вами код работает на машинах, как правило, не очень мощных. Поэтому всегда стоит использовать возможности предоставляемые системой для оптимизации.

Подготовлено на основе материалов AndroidDevBlog

Источник

Hierarchy Viewer

В папке tools есть файл hierarchyviewer.bat, который запускает программу для отладки и оптимизации пользовательского интерфейса (под Windows).

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

В Android Studio немного изменился порядок работы и, к сожалению, у многих эта программа не запускается. У меня также возникла проблема с ним. Поэтому внизу дано старое описание, которое работало под Eclipse.

Чтобы приступить к отладке пользовательского интерфейса, запустите приложение в эмуляторе и откройте в нём шаблон. Затем запустите hierarchyviewer.bat. У вас откроется окно для просмотра иерархии устройства.

В левой части области Devices отображается набор устройств. При выборе устройства в правой области отображается список его окон. Чтобы просмотреть иерархию видо для отдельного взятого окна, выберите это окно в правой области и затем нажмите кнопку Load View Hierarchy.

В левой области окна просмотра иерархии видов вы видите иерархию видов конкретного окна. При выборе элемента вида в левой области можно просмотреть свойства этого вида, открывающиеся справа, а также расположение вида относительно других видов. Вокруг выбранного вида появляется красная рамка.

В нижнем левом углу окна инструмента для просмотра иерархии видов расположены две кнопки. Левая позволяет отобразить древовидную структуру видов, правая — даёт возможность показать актуальный шаблон в режиме Pixel Perfect (Идеальная проекция). В таком режиме вы получаете представление шаблона с точностью до пикселя.

Читайте также:  Ccleaner для андроид планшет

Начало работы

Создадим простое приложение с тремя экранами, которые содержат различные комбинации компонентов для иллюстрации работы с Hierarchy Viewer. Каждый экран использует разметки LinearLayout и FrameLayout с видами TextView и ImageView. Для изображений используются три картинки: кот, сосуд с водой, рыба:

Первый экран состоит из вертикального LinearLayout с двумя «ячейками»: вид TextView со словом Safe и компоновка FrameLayout, которая содержит два ImageView: сосуд и рыбу.

Второй экран использует вертикальную компоновку LinearLayout с двумя «ячейками»: вид TextView со словом Unsafe и компоновку LinearLayout с горизонтальной ориентацией, который в свою очередь также содержит два элемента: в первом находится FrameLayout, который содержит два ImageView с сосудом и рыбой, а второй элемент — ImageView с котом.

На третьем экране используется вертикальная компоновка LinearLayout с двумя дочерними элементами: TextView со словом Yum и FrameLayout, который содержит два ImageView: кот и рыба.

Запускаем приложение в эмуляторе. Hierarchy Viewer можно запустить из перспективы Eclipse, как плагин или через командную строку.

При первой загрузке нужно немного подождать. Когда данные загрузятся, то можно приступать к работе. Есть два варианта работы в Hierarchy Viewer, между которыми можно переключаться:

  • Load View Hierarchy
  • Inspect Screenshot

Inspect Screenshot

В режиме Inspect Screenshot вы получаете представление шаблона с точностью до пикселя.

Вы можете увеличивать определенные участки экрана, проверить расположение компонентов на экране и так далее. Также можно сохранить скриншот в PNG-файл. Используйте этот режим для дизайна вашего интерфейса, используя наложение (перемещая ваши картинки).

Load View Hierarchy

Рассмотрим режим Load View Hierarchy.

Данный режим имеет несколько панелей:

  • Слева находится панель Tree Hierarchy
  • Справа сверху: панель Loupe
  • Справа в центре: панель свойств
  • Справа внизу: панель Wire-Frame

Изучаем древовидную структуру

В основной левой панели мы видим карту элементов управления в древовидном интерфейсе. Все элементы текущей разметки показаны слева направо в соответствии с вашим шаблоном.

Например, если брать первый экран, то справа мы видим два ImageView с сосудом и рыбой. Двигаясь влево, вы видите, что они находятся в FrameLayout с идентификатором @id/frameLayoutFishbowl. Продолжая движение влево на следующий уровень, мы попадаем на корневой элемент LinearLayout, содержащий TextView и FrameLayout с идентификатором @id/frameLayoutFishbowl.

Вы можете щёлкнить по любому элементу в Tree Hierarchy. Появится специальное окно с различной информацией о выбранном элементе. Например, для TextView можно узнать тип (TextView), системный идентификатор и выводимый текст. Цветные точки помогают определить производительность.

Для ImageView с сосудом:

Нужный элемент можно выделить через панель Loupe. Это удобно, когда древовидная структура слишком большая.

Выделим элемент с рыбой. На панели свойств можно увидеть различные свойства к данному элементу.

Если вы обновляете свой графический интерфейс, то необходим обновить содержание и в Hierarchy Viewer.

Посмотрим на следующую картинку:

Обратите внимание на три цветные кружочки (точки) под каждым элементом:

Они являются индикаторами производительности:

  • Левая точка определяет, как долго проходит операция измерения для данного элемента.
  • Средняя точка определяет длительность отрисовки разметки для данного элемента.
  • Правая точка определяет длительность отрисовки элемента на экране.

Точки могут быть красными, жёлтыми и зелёными. Красная свидетельствует о том, что рисование на экране идёт слишком медленно, жёлтая — удовлетворительно, зелёная — хорошая скорость отрисовки.

Если щелкнуть на элементе в дереве иерархии, вы также увидите актуальную информацию о производительности.

Если у объекта все точки зелёного цвета, то объект загружается очень быстро и вам не нужно заботиться о производительности. Желтый и красный цвет — повод задуматься и пересмотреть проект.

Например, для ImageView нужно попробовать использовать PNG вместо JPG (или наоборот), а также изменить размеры изображения, если это возможно.

Пересмотрите разметку. Например, RelativeLayout потребляет меньше ресурсов, чем TableLayout. Избегайте больших вложений одной разметки в другую.

Не перезагружайте экран элементами. Будьте проще.

Источник

Оцените статью