Android thread: Android: Потоки
Советы по созданию плавного и отзывчивого интерфейса для Android
Разработчикам необходимо, чтобы мобильные приложения не только выглядели красиво и имели богатый функционал, но были высокопроизводительными. Разработчики часто задумываются каким образом можно оптимизировать сетевые запросы и фоновые операции, чтобы они исполнялись быстрее. Но пользователи обычно связывают производительность с главным компонентом мобильного приложения, с которым они взаимодействуют: пользовательский интерфейс. В отношении пользовательского интерфейса плавность изображения может определяться как последовательность 60 кадров в секунду (fps), что означает обработку кадра каждые 16 мс. В этом посте будут представлены несколько советов и хитростей по идентификации и отладки пользовательского интерфейса для максимальной производительности.
Почему 60 fps?
Анимации и переходы приложения — это просто серии статичных изображений. Хитрость заключается в том, чтобы достаточно быстро отобразить эти отдельные изображения, чтобы обмануть человеческий мозг и заставить его думать, что это непрерывное движение. 60 кадров с секунду — это золотая середина между плавным изображением и падением частоты кадров, заметных человеческому глазу. Более подробно объясняет Кольт Макэнлис (Colt McAnlis) в этом видео. Соответственно на отображение одного кадра тратится около 16 мс. Теперь, когда понятно, к чему нужно стремится, как сделать так, чтобы приложение удовлетворяло требованиям производительности?
Поиск проблемы
Случалось ли что-нибудь подобное с вашим приложением?
Спасибо Дугу Силларсу за отличный пример.
Скроллинг происходит прерывисто, и в результате пользовательский интерфейс становится не чувствителен к действиям пользователя. В течение времени, которого пользовательский интерфейс остается нечувствительным и прерывистым, если попытки пользователя взаимодействовать с приложением занимают больше 5000 мс (5 секунд), то ОС Android выведет следующее сообщение:
Или может будет показано следующее в логах:
Skipped 60 frames! The application may be doing too much work on its main thread.
Skipped 60 frames! The application may be doing too much work on its main thread. |
Android Choreographer предупреждает, что приложение не обеспечивает плавное изображение, что и являлось целью. Собственно он сообщает, что пропускаются кадры и не получается изображения частотой 60 fps. Это можно исправить! Вот основные действия при идентификации и решения проблем пользовательского интерфейса:
- Всесторонне оценить производительность пользовательского интерфейса с помощью Systrace
- Использовать Hierarchy Viewer, чтобы просмотреть и настроить визуальное представление иерархий.
- Снизить нагрузку наложений настройкой лейаутов и отображением меньшего количества пикселей на экране.
- Включить StrictMode, чтобы найти и уменьшить количество возможно блокирующих запросов в потоке пользовательского интерфейса.
Начало
Подсчет пропущенных кадров
Разработчики Android называют эти пропущенные или заторможенные кадры — подвисшими. После того, как приложение было запущено и стало пытаться воспроизводить подвисшие кадры, нужно исполнить следующую команду:
adb shell dumpsys gfxinfo [PACKAGE_NAME]
adb shell dumpsys gfxinfo [PACKAGE_NAME] |
Эта команда выведет что-то вроде:
Stats since: 524615985046231ns
Total frames rendered: 8325
Janky frames: 729 (8.76%)
90th percentile: 13ms
95th percentile: 20ms
99th percentile: 73ms
Number Missed Vsync: 294
Number High input latency: 47
Number Slow UI thread: 502
Number Slow bitmap uploads: 44
Number Slow issue draw commands: 135
Stats since: 524615985046231ns Total frames rendered: 8325 Janky frames: 729 (8.76%) 90th percentile: 13ms 95th percentile: 20ms 99th percentile: 73ms Number Missed Vsync: 294 Number High input latency: 47 Number Slow UI thread: 502 Number Slow bitmap uploads: 44 Number Slow issue draw commands: 135 |
Цифры никогда не врут, так что очевидно, что есть некоторое количество подвисших кадров (~9%). Нужно продолжить дальше с помощью инструмента Systrace.
Использование инструмента Systrace
Systrace предоставляет обзор всей Android системы и показывает происходящие события в определенные интервалы времени.
Начало
Нужно запустить Systrace на устройстве. Для этого следует открыть Android Device Monitor. Здесь присутствует опция, с помощью которой можно запустить Systrace:
Затем нужно сделать запрос на то, что необходимо отследить. После выбора всех флажков Commonly Used и отмены выбора Advanced Options создастся файл trace.html для обозначенного периода отслеживания.
Вот пример Systrace на период около 30 секунд:
Отлично! Но что это все означает?
Здесь показано некоторое число предупреждений в виде круглых значков в строке Alerts. Если взглянуть непосредственно на сам процесс, можно увидеть строку кадров. Кадры,расцвеченные желтым и красным, превышают время рендеринга в 16 мс.
Если кликнуть на предупреждении, то появится обзор проблемы внизу окна отслеживания. Можно получить более подробную информацию об определенном кадре, кликнув на соответствующий ярлык кадра в самом процессе или просто кликнув Frame в описании предупреждения. Она покажет количество времени, затраченного на данное действие:
Наконец, можно пометить данный кадр, используя горячую клавишу m, и рассмотреть в приближении затронутый кадр, чтобы понять, что происходит в различных потоках: CPU потоках, потоке пользовательского интерфейса и потоке рендеринга.
На примере желтого кадра можно составить общее представление.
Из этого предупреждения ясно, что можно избежать значительного количества работы в View.OnDraw() или в Drawable.Draw(), особенно выделений и отрисовок в Bitmaps. Google по этому поводу дает совет в этом видео.
Понятно, что Adapter.GetView() должен переработать входящий View, вместо того, чтобы создать новый.
Systrace понятия
Цвет кадра
- Зеленый: отличная производительность
- Желтый: хуже идеальной производительности
- Красный: плохая производительность
Запланированная задержка
Запланированные задержки происходят, когда поток, исполняющий определенный срез, не был назначен в CPU на длительное время. Таким образом выделяется больше времени для выполнения этого потока.
Wall длительность
Количество времени, прошедшее с начала исполнения среза до его завершения.
CPU длительность
The amount of time the CPU spent processing that slice.
Количество времени, которое тратит CPU на обработку этого среза.
Перерисовка наложений
Перерисовка наложений — чрезмерная отрисовка пользовательского интерфейса, результатом которой является медленная работа пользовательского интерфейса (из-за раздутых лейаутов, анимаций и т.п.). Отладка этой проблемы довольна проста. Нужно включить диагностику в настройках Android устройства: Настройки (Settings) -> Параметры разработчика (Developer Options) -> Отладка наложения (Debug GPU overdraw) -> Показывать области наложения (Show overdraw areas).
После этого появится множество разных цветов на лейаутах.
- Белый: нет наложения
- Синий: 1кратное наложение пикселей
- Зеленый: 2кратное наложение пикселей
- Розовый: 3кратное наложение пикселей
- Красный: 4кратное наложение пикселей
Плохое исполнение лейаутов:
Хорошее исполнение лейаутов:
Далее можно выяснить, почему этот лейаут был перегружен наложениями с помощью инструмента Hierarchy Viewer .
Hierarchy Viewer
Для начала нужно узнать как глубоко вложены лейауты. Чем глубже вложены лейауты, тем больше нагрузка на GPU.
Рассмотрим предыдущий пример неэффективного исполнения лейаута:
Применяем Kotlin Coroutines в боевом Android-проекте — Axmor
Coroutines Kotlin VS RxJava в асинхронном коде
Думаю, для тех, кто не знаком с Kotlin, стоит сказать пару слов о нем и корутинах в частности. Об актуальности изучения Kotlin говорит то, что в мае 2017 года компания Google сделала его официальным языком разработки Android.
Проекты, стартующие в нашей компании, мы пишем на Kotlin, поэтому изучаем существующие возможности и следим за выходом новых. Когда создатели языка анонсировали корутины как новый инструмент асинхронного программирования, стало интересно протестировать их в боевых условиях. Судя по описанию возможностей, они как раз подходят для решения наших задач и отличаются в лучшую сторону от уже существующих решений.
Итак, для чего нужны корутины? Если требуется скачать что-то из сети, извлечь данные из базы данных или просто выполнить долгие вычисления и при этом не заблокировать интерфейс пользователю, можно использовать корутины.
В контексте Android в задачах обеспечения асинхронности их смело можно рассматривать как конкурента RxJava. Несмотря на то, что возможности RxJava гораздо шире (это довольно объемная библиотека со своим подходом и философией), работать с корутинами удобнее, потому что они — всего лишь часть языка программирования. Задачи, решенные на RxJava с помощью операторов (специальных методов библиотеки), на корутинах реализуются намного проще — через встроенные средства языка. К тому же операторы библиотек нужно не только знать, но и понимать, как они работают, правильно выбирать и применять. Конечно, средства языка знать и правильно применять тоже нужно, но, когда речь идет о сокращении времени на разработку, стоит задуматься, насколько изучение возможности библиотеки, которую используешь для решения небольшой задачи, актуально в сравнении с изучением языка, на котором пишется весь проект.
Примеры использования Coroutines Kotlin
Поддержка корутин встроена в Kotlin, но все классы и интерфейсы находятся в отдельной библиотеке. Для их использования нужно добавить зависимость в gradle:
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
}
Небольшой пример использования:
val job: Job = GlobalScope.launch(Dispatchers.IO) {
longRunningMethod()
}
Разберемся, что тут происходит.
longRunningMethod() — метод, который нам нужно выполнить асинхронно.
GlobalScope — жизненные рамки для корутины. В данном случае корутина будет жить, пока живо приложение, в котором она запущена. GlobalScope — конкретная реализация интерфейса CoroutineScope. Можно реализовать свой scope, например, в Activity, и это приведет к тому, что запущенные в Activity корутины будут автоматически отменяться в случае завершения или краша Activity.
launch — метод для асинхронного запуска корутины. Соответственно, метод longRunningMethod() запустится сразу же. Метод возвращает экземпляр класса Job. Этот объект можно использовать для того, чтобы, например, отменить корутину — job.cancel(). Альтернатива — метод asunc(). Он вернет Deferred<T> — отложенную корутину, которую можно запустить позднее.
Dispatchers.IO — один из параметров метода launch(). Здесь указывается диспетчер для созданной корутины. Конкретно диспетчер Dispatchers.IO используется для фоновых задач, не блокирующих основной поток. Если указать Dispatchers.Main, то корутина будет выполняться в основном потоке.
Что имеем в итоге? Простой метод запуска асинхронного кода. Но в этом кусочке кода есть скрытые преимущества, которые не видны на первый взгляд:
- корутины легковесны. Аналогичный код с созданием и запуском потока потребует много больше памяти:
thread {
longRunningMethod()
}
Корутины же мы можем создавать тысячами;
- корутину можно приостановить. Метод delay(timeout) приостановит выполнение корутины, но это никак не отразится на потоке, в котором она выполняется;
- в отличие от RxJava, для написания асинхронного кода не надо заучивать массу операторов типа merge, zip, andThen map, flatMap и т.д. Можно просто писать код, который будет запущен асинхронно, используя минимум дополнительных методов. Для реализации более сложной логики можно применять уже знакомые языковые конструкции, такие как foreach, repeat, filter и т.д.
Когда нужен асинхронный подход
Вернемся к нашей задаче использования корутин. Приложение под Android, над которым мы сейчас работаем, общается с сервером и хранит информацию в базе данных. На первый взгляд, ничего нового по функционалу, но интересен сам подход в реализации, поскольку тестируем новый инструмент разработки асинхронного кода — корутины Kotlin.
В своем приложении мы применяем асинхронный код. Почему? Дело в том, что общение с сервером и запросы в базу данных — довольно продолжительные операции. Пока выполняется одна, вполне можно успеть завершить еще несколько, не блокируя основной поток. Именно эту задачу и решает асинхронный подход. В случае синхронного программирования операции выполняются последовательно, т.е. следующая команда запускается только после того, как завершится предыдущая, и когда какая-нибудь из них выполняется слишком долго, программа может зависнуть. И хотя все понимают, что “зависание” вряд ли понравится пользователям, все же такую реализацию иногда можно встретить в приложениях. Повторюсь, мы решаем задачу с помощью асинхронного кода.
Применение Coroutines Kotlin в нашем проекте
Итак, запуск абстрактного асинхронного кода — это хорошо, но попробуем решить более насущную задачу. Допустим, надо сделать запрос на сервер и показать результат. Посмотрим, как это можно сделать с помощью корутин. Само скачивание будем для простоты выполнять во ViewModel, общаться с Activity будем с помощью LiveData.
Создадим класс-наследник ViewModel:
class LoginViewModel(application: Application) : BaseViewModel(application) {
private val loginLiveData = MutableLiveData<Resource<UserProfile>>()
fun getLoginLiveData(): LiveData<Resource<UserProfile>> {
return loginLiveData
}
fun login(name: String, password: String) {
runCoroutine(loginLiveData) {
val response = ServerApi.restApi.authorize(name, password).execute()
return@runCoroutine response.body()!!
}
}
}
Внутри модель содержит MutableLiveData с данными пользователя, который мы получаем после авторизации. Наружу отдаем неизменяемую LiveData, чтобы никто кроме ViewModel не мог изменять данные внутри. Профиль пользователя завернут в класс Resource. Это утилитарный класс для удобства передачи состояния процесса во View:
data class Resource<T>(
val status: Status,
val data: T?,
val exception: Exception?
) {
enum class Status {
LOADING,
COMPLETED
}
}
Как видно, во View мы можем передавать информацию о том, завершилось ли скачивание, и если завершилось, то с ошибкой или успешно.
Запуск корутины происходит в методе login(). Он вызывает метод базового класса runCoroutine() :
protected fun <T> runCoroutine(correspondenceLiveData: MutableLiveData<Resource<T>>, block: suspend () -> T) {
correspondenceLiveData.value = Resource(Resource.Status.LOADING, null, null)
GlobalScope.launch(Dispatchers.IO) {
try {
val result = block()
correspondenceLiveData.postValue(Resource(Resource.Status.COMPLETED, result, null))
} catch (exception: Exception) {
val error = ErrorConverter.convertError(exception)
correspondenceLiveData.postValue(Resource(Resource.Status.COMPLETED, null, error))
}
}
}
У этого метода 2 параметра. Первый — типизированный экземпляр LiveData, куда будут записаны данные. Второй — код, который нужно выполнить асинхронно. В методе login() мы передаем код, который передает на сервер данные для авторизации и получает от сервера профиль пользователя.
Как работает все вместе: View получает от ViewModel LiveData, подписывается на ее изменения. Изменения могут быть трех видов: идет какой-то процесс, все завершилось с ошибкой, все завершилось успешно. В нужный момент вызывается метод login(). Затем последовательно происходит: запись в LiveData информации о том, что “идет какой-то процесс”, запрос на сервер, получение данных, запись в LiveData полученных данных. Или ошибки, если запрос на сервер не удался.
Выводы
Естественно, в одной статье невозможно раскрыть все аспекты нового инструмента в асинхронном программировании. Тем, кто заинтересовался корутинами Kotlin, можно посоветовать изучить и протестировать, к примеру, комбинирование корутин, каналы, реализацию Actor model и другие возможности.
Несмотря на то, что в рамках примера показана довольно банальная задача — скачать данные с сервера, что делается почти в каждом приложении, он иллюстрирует принцип работы с корутинами. Вместо скачивания может быть что угодно. Пример показывает, как с помощью корутин удобно обернуть любую операцию.
Как мы видим, задачи асинхронного программирования под Android проще реализовать с помощью корутин: быстрее разработка, выше читаемость кода. Риски, конечно, тоже есть: для изучения корутин нужно некоторое время, а их возможности иногда могут не закрыть все требования задачи. Перед использованием в своем проекте рекомендуется внимательно ознакомиться с областью их применения.
Официальный сайт разработчиков Kotlin: http://kotlinlang.org/
Анонс релиза Kotlin 1.3.0 с Coroutines: https://blog.jetbrains.com/kotlin/2018/10/kotlin-1-3/
Работа с потоками через AsyncTask | android
Класс AsyncTask позволяет правильно и просто использовать главный поток приложения, обслуживающий интерфейс пользователя (UI thread). Этот класс позволяет выполнять фоновые операции в отдельном потоке и публиковать результаты этих операций для UI thread, без всякой необходимости вручную манипулировать потоками thread и/или обработчиками (handler). Здесь приведен перевод документации [1].
AsyncTask разработан, чтобы быть классом-оберткой (helper class) вокруг классов Thread и Handler, и он не составляет универсальную платформу для поточной обработки. AsyncTasks идеально подходит для коротких операций (занимающих по времени около нескольких секунд). Если Вам нужны потоки, работающие длительный промежуток времени, то рекомендуется использовать разное API, представленное в пакете java.util.concurrent с такими классами как Executor, ThreadPoolExecutor и FutureTask.
Как понятно уже из названия, класс AsyncTask предназначен для выполнения асинхронных по отношению к UI thread задач. Асинхронная задача — это некое вычисление, которое работает в фоновом потоке, и свои результаты публикует в UI thread. Асинхронная задача определена 3 стандартными типами, которые называются Params, Progress и Result, и 4 шагами, называемыми onPreExecute, doInBackground, onProgressUpdate и onPostExecute. Дополнительную информацию по использованию процессов и потоков см. в [2].
[Как применять AsyncTask]
Для того, чтобы использовать AsyncTask, от него необходимо породить подкласс (subclass). В подклассе должен быть переопределен как минимум один метод (doInBackground(Params…)). Также часто переопределяется второй метод (onPostExecute(Result)), и иногда другие методы. Вот пример порождения подкласса на основе родительского класса AsyncTask:
private class DownloadFilesTask extends AsyncTask < URL, Integer, Long > { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Ранний выход, если был вызван cancel(). if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
После создания выполнить задачу очень просто:
new DownloadFilesTask().execute(url1, url2, url3);
[Стандартные типы AsyncTask]
В асинхронной задаче используется три следующих типа:
1. Params, тип параметров, отправленных задаче на выполнение.
2. Progress, тип единиц прогресса, публикуемого во время фоновых вычислений.
3. Result, тип результата фоновых вычислений.
Не все эти типы используются в асинхронной задаче. Чтобы пометить, что тип не используется, используйте тип Void:
private class MyTask extends AsyncTask< Void, Void, Void > { ... }
[4 шага AsyncTask]
Когда выполняется асинхронная задача, то она проходит через 4 шага:
1. onPreExecute(), вызывается в UI thread перед тем, как задача начнет выполняться. Этот шаг обычно используется для настройки задачи, например для показывания полоски прогресса в интерфейсе пользователя.
2. doInBackground(Params…), вызывается в фоновом потоке задачи сразу после того, как завершит работу onPreExecute(). Этот шаг используется для фоновых вычислений, которые займут значительное время. На этот шаг передаются параметры асинхронной задачи. Результат вычисления должен быть возвращен на этом шаге, и он будет передан в последний шаг. Этот шаг также использует publishProgress(Progress…), чтобы опубликовать одну или большее количество единиц прогресса. Эти значения публикуются в UI thread, на шаге onProgressUpdate(Progress…).
3. onProgressUpdate(Progress…), вызывается в UI thread после вызова publishProgress(Progress…). Время выполнения не определено. Этот метод используется для отображения прогресса любой формы в интерфейсе пользователя — например для анимирования прогресс-бара, или для того, чтобы показать записи лога в текстовом поле.
4. onPostExecute(Result), вызывается в UI thread после завершения фоновых вычислений. Результат фонового вычисления передается на этом шаге как параметр.
[Отмена задачи]
Задача может быть отменена в любой момент вызовом cancel(boolean). После вызова этого метода вызов isCancelled() будет возвращать true. Также после вызова этого метода вместо onPostExecute(Object) будет вызван onCancelled(Object) после того, как завершится doInBackground(Object[]). Чтобы обеспечить максимально быстрое завершение задачи, Вы всегда должны периодически проверять значение isCancelled() из циклов doInBackground(Object[]).
[Правила работы с потоками]
Чтобы правильно работал класс AsyncTask, следует соблюдать несколько правил:
• Класс AsyncTask должен быть загружен в UI thread. С JELLY_BEAN это делается автоматически.
• Экземпляр задачи должен быть создан в UI thread.
• execute(Params…) должен быть вызван в UI thread.
• Не вызывайте onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) вручную.
• Задача может быть выполнена только один раз (при попытке повторного выполнения будет выброшено исключение (thrown exception).
[Видимость данных в памяти]
AsyncTask гарантирует, что все callback-вызовы синхронизируются таким образом, что последующие операции безопасны без явных синхронизаций.
• Установите поля экземпляра класса в конструкторе или в onPreExecute(), и обращайтесь к ним в doInBackground(Params…).
• Установите поля экземпляра класса в doInBackground(Params…), и обращайтесь к ним в onProgressUpdate(Progress…) и onPostExecute(Result).
[Порядок выполнения]
Когда AsyncTasks был представлен впервые, он выполнялся последовательно в одном фоновом потоке. Начиная с DONUT это было изменено на пул потоков, что позволило нескольким задачам выполняться параллельно. Начиная с HONEYCOMB задачи выполнятся в одном потоке, чтобы избежать общих ошибок приложения, вызываемых параллельными вычислениями.
Если Вам нужны по-настоящему параллельные вычисления, то Вы можете вызвать executeOnExecutor(java.util.concurrent.Executor, Object[]) с THREAD_POOL_EXECUTOR.
[Общий обзор методов и полей AsyncTask]
Встроенные (Nested) классы | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
enum | AsyncTask.Status | Показывает текущий статус задачи. |
Поля | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
public static final Executor | SERIAL_EXECUTOR | Executor , который выполняет задачи по одной в последовательном порядке. | |||||||||
public static final Executor | THREAD_POOL_EXECUTOR | Executor , который может использоваться для выполнения задач параллельно. |
Общедоступные (Public) конструкторы | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
AsyncTask()
Создает новую асинхронную задачу.
|
Общедоступные (Public) методы | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
final boolean | cancel(boolean mayInterruptIfRunning)
Делает попытку отменить выполнение этой задачи.
| ||||||||||
static void | execute(Runnablerunnable)
Удобная версия execute(Object) для использования с простым Runnable-объектом.
| ||||||||||
final AsyncTask< Params, Progress, Result > | execute(Params… params)
Выполняет задачу с указанными параметрами.
| ||||||||||
final AsyncTask< Params, Progress, Result > | executeOnExecutor(Executorexec, Params… params)
Выполняет задачу с указанными параметрами.
| ||||||||||
final Result | get(long timeout, TimeUnitunit)
Ждет при необходимости завершения вычисления в течение самого большого указанного времени, и затем получает результат.
| ||||||||||
final Result | get()
Ждет при необходимости результата вычисления, и затем получает результат.
| ||||||||||
final AsyncTask.Status | getStatus()
Возвращает текущий статус этой задачи.
| ||||||||||
final boolean | isCancelled()
Возвращает true, если эта задача была отменена до полного нормального завершения.
|
Защищенные (Protected) методы | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
abstract Result | doInBackground(Params… params)
Переопределите этот метод, чтобы выполнять какие-то вычисления в фоновом потоке.
| ||||||||||
void | onCancelled(Result result)
| ||||||||||
void | onCancelled()
Тот же самый обработчик без параметра. Приложения предпочтительно должны переопределять
| ||||||||||
void | onPostExecute(Result result)
| ||||||||||
void | onPreExecute()
| ||||||||||
void | onProgressUpdate(Progress… values)
| ||||||||||
final void | publishProgress(Progress… values)
Этот метод может быть вызван из doInBackground(Params...) , чтобы опубликовать данные о текущей работе и обновить данные в UI thread. Это обычно используется, чтобы определить, что фоновые вычисления еще идут, и на какой стадии они находятся (может быть перерисована, к примеру, полоска прогресс-бара).
|
Подробное описание полей и методов AsyncTask см. в документации [1].
[Ссылки]
1. AsyncTask site:developer.android.com.
2. Процессы и потоки.
Android Thread Kullanımı | CSD Android Kurs Notları
- CSD Android Kurs Notları — Oğuz Karan
Введение
1.
Деятельность
2.
Посмотреть Elemanları
3.
OnSaveInstanceState и OnResoreInstanceState
4.
Менеджер по верстке Sınıfları
5.
Java 8 Lambda Ifadeleri
6.
Dinamik GUI ekleme
7.
Намерение Каврами
8.
ListeTarzı Посмотреть elemanları
9.
Spinner View Elemanı
10.
GridView View Elemanı
11.
Вери Саклама Йонтемлери
12.
Android Projeleri İçin Kütüphane Oluşturma Ve Kullanımı
13.
Veri Tabanı İşlemleri (SQLite)
14.
Hazır Intent Nesnelerinin Kullanımı (Встроенный)
15.
SharedPreferences ile Uygulama Verilerinin Saklanması
16.
Kütüphane Oluşturma (AAR) ve Kullanımı
17.
Menü (ActionBar) İşlemleri
18,
Ориентация на деятельность Дурумунун Белирленмеси
19.
Android Thread Kullanımı
20.
BroadcastReceiver Sınıfı ve Broadcast İşlemleri
21.
Sistem Açılışında Uygulamanın Başlatılıması (Приложение для запуска)
22.
AlertDialog Sınıfı
Разработка под Android — Новости, пример, учебник, исходный код: синхронизация потоков в Java и Android
Я всегда любил Java.Нить была единственной вещью, которой боялись make, поэтому изучите ее как следует и теперь хотите убедиться, что этого не случится ни с кем сейчас. Я обсуждал, как создать простой поток. Но самое главное — это Синхронизация двух потоков.
Например -У нас один файл. И у нас есть два потока: один для чтения этого файла, а второй — для записи файла. Итак, в этом случае, если мы хотим прочитать все, что должно быть написано потоком записи, тогда поток чтения будет ждать (если запись выполняется), пока запись не завершит свою задачу.
Для синхронизации потоков важно отметить, что мы можем синхронизировать только один объект.
Хотя я буду писать код в технологии Android, но в Java и Blackberry он почти такой же. Для этого мне нужно взять два потока
Thread 1
частный класс Thread2 расширяет Thread { private SynDemo d; public Thread2 (SynDemo sDemo) { d = sDemo; } @Override public void run () { супер.бегать(); d.printNumber ("Thread2"); } }
Этот поток будет печатать числа от 0 до 2000. Следующий поток также будет делать то же самое.
Thread 2
частный класс Thread1 расширяет Thread { private SynDemo d; public Thread1 (SynDemo sDemo) { d = sDemo; } @Override public void run () { super.run (); d.printNumber ("Поток1"); } }
Теперь создайте класс, в котором у нас есть метод, в котором мы должны синхронизировать как Thread.
частный класс SynDemo { synchronized void printNumber (String string) { for (int i = 0; i <2000; i ++) { Log.i ("" + строка, String.valueOf (i)); } } }
Теперь у вас есть некоторые пояснения. Оба потока содержат один конструктор. В них есть объект SynDemo для достижения. Обратите внимание, что мы можем синхронизировать только один объект.В обоих конструкторах мы передадим один и тот же объект
Теперь посмотри, как
SynDemo d = новый SynDemo (); // Создаем один объект и передаем один и тот же объект в оба // нить Thread1 th = новый Thread1 (d); Thread2 th3 = новый Thread2 (d); th.start (); th3.start ();
Окончательный код для android будет таким. Играйте с ним
пакет com.hb; import android.app.Activity; import android.os.Bundle; import android.util.Log; открытый класс SeekBarActivity расширяет Activity { / ** Вызывается при первом создании действия.* / SeekBar seek; @Override public void onCreate (Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.main); SynDemo d = новый SynDemo (); Thread1 th = новый Thread1 (d); Thread2 th3 = новый Thread2 (d); th.start (); th3.start (); } частный класс Thread1 расширяет Thread { private SynDemo d; public Thread1 (SynDemo sDemo) { d = sDemo; } @Override public void run () { супер.бегать(); d.printNumber ("Thread1"); } } частный класс Thread2 расширяет Thread { private SynDemo d; public Thread2 (SynDemo sDemo) { d = sDemo; } @Override public void run () { super.run (); d.printNumber ("Thread2"); } } частный класс SynDemo { synchronized void printNumber (String string) { for (int i = 0; i <2000; i ++) { Log.i ("" + строка, String.valueOf (i)); } } } }
Как мне получить возвращаемое значение из метода внутреннего потока Runnable: Android - Ready Android
перейти к содержанию
Меню
Страстно стремясь к совершенству
- Анимация 3D Flip в элементе списка
- Умный способ использования дооснащения
- Об авторе
- Над нижней панелью навигации Lollipop API накладывается проблема приложения
- Режимы запуска активности
- ADB команды android
- Добавить свойство выравнивания родительского дна в RelativeLayout: Android
- Добавить ссылки на приложения Android или ссылку Деппа
- Добавить пробел в имя строки в XML?
- Добавьте разделители и пробелы между элементами в RecyclerView: Android
- Добавить событие и напоминание в календарь: Android
- adjustResize windowSoftInputMode не работает в lollipop и выше
- Диалоговое окно с предупреждением
- Пример пользовательского уведомления Android для музыкального проигрывателя
- Android определяет состояние подключения к Интернету
- UDID устройства Android или уникальный идентификатор?
- Android FCM не получает уведомления при удалении приложения из фона
- Android GCM: тот же идентификатор отправителя для других приложений
- Ссылка на хорошее знание Android
- Макет Android res в подпапках
- Именование ресурсов Android
- Поддержка дизайна Android RTL: Unicode-символ «ПЕРЕМЕЩЕНИЕ СЛЕВА НАПРАВО» (U + 202D)
- Программная клавиатура Android закрывает поле EditText: перескролить на программную клавиатуру
- Android Studio build.фрагмент Gradle
- Android Studio: Важно *
- Android studio выдает сообщение «Плагин слишком старый, обновите его до более новой версии или установите переменную среды ANDROID_DAILY_OVERRIDE» ошибка
- Конструкции потоков Android: служба, поток, IntentService, AsyncTask
- Ссылка на инструменты Android
- Ссылки на хитрости Android
- Android - атрибут AutoLink TextView
- android.view.InflateException: двоичный файл XML, строка № 0: попытка вызвать виртуальный метод boolean java.lang.String.equals (java.lang.Object) ’для ссылки на нулевой объект: Android
- Android: определение местоположения по GPS и стратегии определения местоположения
- Анимация с использованием xml: Android
- Обработчики аннотаций теперь должны быть явно объявлены: Android; build.gradle
- Ant против Maven против Gradle
- Приложение не установлено; Пакет конфликтует с существующим пакетом с тем же именем: Android
- Приложение, запуск которого требует много времени: Android
- AutoCompleteTextView: Android
- Блог
- Связанный, несвязанный и намеренный сервис: Android
- Butter Knife: Android Studio
- Caching Array Список настраиваемых объектов во внутренней памяти Android
- Расчет расстояния между двумя точками LatLng с помощью Google API или математической функции: Android
- Предварительный просмотр камеры: Android
- Не удается получить разрешение WRITE_SETTINGS: Marshmallow
- Изменить цвет фона диалогового окна: Android
- Изменить путь к каталогу сборки для приложения: Android Studio
- Изменить высоту на просмотр программно; Android
- Изменить конфигурацию языкового стандарта в Android
- Изменить цвет фона элемента меню и цвет текста: Android
- Изменить цвет значка переполнения панели инструментов
- Проверить приложение Android на переднем плане или нет?
- Проверить приложение Android на переднем плане или нет?
- Проверьте, установлено приложение или нет: Android | Котлин
- Проверить, содержит ли массив заданное значение
- Дочерние фрагменты исчезают при удалении родителя: Android Fragment
- Прослушиватель кликов для адаптера RecyclerView
- Раскраска Android-приложений с помощью палитры
- Общий GIT
- Объединить два массива
- Конфликт с зависимостью com.android.support:support-annotations »в проекте«: app ». Разрешенные версии приложения (26.1.0) и тестового приложения (27.1.1) различаются.
- Конфликт с зависимостью com.android.support:support-annotations. Разрешенные версии приложения (23.1.0) и тестового приложения (23.0.1) отличаются
- Использование API-интерфейсов при модернизации
- Выбор контактов и установка контактов: Android
- Использование контекстного меню действий с элементами списка: Android
- Преобразовать массив в набор (HashSet) и наоборот
- Преобразовать массив в набор (HashSet) и наоборот
- Преобразовать массив символов в строку
- Преобразовать файл в байтовый массив и наоборот
- Преобразование json в объект Java и наоборот: Android
- Преобразовать список (ArrayList) в массив и наоборот
- Преобразовать карту (HashMap) в список
- Преобразовать строку в массив символов
- Преобразование байтового массива в целочисленный
- Преобразование миллисекунд в часы, минуты и секунды
- Копировать в буфер обмена из командной строки Windows
- Данные страны в формате JSON; API | Android
- Создайте миниатюру видео: FFMPEG
- Создание рабочего режима киоска в Android
- Создание эскиза видео: Android
- Пользовательские аналоговые часы
- Пользовательский стиль шрифта: Android
- Пользовательский шрифт для просмотра
- Кнопка пользовательского изображения
- Пользовательское всплывающее окно макета в android
- Класс пользовательского журнала: Android
- Пользовательский тост в android
- Привязка данных для представлений: Android
- Связывание данных с Kotlin; Android; Котлин
- Реализация структуры кода базы данных: android
- Формат даты и времени
- DatePicker с настройкой: Android
- Отладка баз данных Android и общих настроек в браузере
- Отладка Bluetooth с помощью приложения для Android - журнал отслеживания Bluetooth HCI
- Диалоговое окно DatePicker по умолчанию: Android
- Программная детализация устройства Android
- Стиль диалогового окна для активности: Android
- Различия между однократным и двойным касанием в любом обзоре: Android
- Отключить режим сдвига BottomNavigationView?
- Отключить смахивание в ViewPager: Android
- Рисуемая граница круга с несколькими цветами: Android xml
- Рисунки: Android
- Eclipse зависает на «Устранении маркеров ошибок» или «Загрузчике содержимого Android SDK»
- Eclipse: не удалось открыть редактор: нет дескриптора редактора для id org.eclipse.jdt.ui.CompilationUnitEditor
- EditText.setError () выдает невидимый текст ошибки
- Включить кнопку возврата панели инструментов; Android
- Включение режима оконного программного ввода в полноэкранном режиме: Android
- - выберите Android SDK; Ошибка конфигурации редактирования приложения: Android Studio
- Ошибка: слишком длинный путь к файлу в Windows, не более 240 символов
- Ошибка: используйте JsonReader.setLenient (true), чтобы принять искаженный JSON в строке 1, путь столбца 1 $
- Ошибка: не удалось выполнить задачу ‘: app: preDebugAndroidTestBuild’, конфликт с зависимостью: Android Studio
- Ошибка: не удалось выполнить задачу ‘: app: preDebugAndroidTestBuild’.Конфликт с зависимостью com.android.support:support-annotations; Android
- Ошибка: не удалось выполнить задачу ‘: app: processDebugManifest’. > Слияние манифестов завершилось неудачно с множеством ошибок, см. Логи: Android
- Ошибка: не удалось выполнить задачу ‘: app: processDebugResources’ Нет подчиненного процесса для обработки заданий; Android
- Ошибка: не удалось выполнить задачу app: transformClassesAndResourcesWithProguardForRelease
- Ошибка: java.lang.Exception: не удалось подключиться к демону kotlin; Котлин | Android
- Событие для пользователя переключения - Многопользовательская поддержка: Android
- Выйти из приложения Android при нажатии на кнопку
- Извлечение метаданных из файла мультимедиа: Android; MediaMetaDataRetriever
- Facebook работает с api графика
- Ошибка сборки при dexDebug или dexRelease: Android Studio
- Команды FFMPEG
- Отфильтровать ввод EditText: Android
- Найти наибольший элемент массива
- Исправление неподдерживаемого основного.минорная версия 52.0 Ошибка в Java
- Force: Android; Firebase
- Проблема с анимацией фрагмента транзакции: Android
- Помощник транзакции фрагмента: Android
- Полноэкранный просмотр: изображение / видео / видео Youtube; Образец Android
- : Android Studio
- Общие вопросы интервью
- Сгенерировать случайное число в диапазоне, исключая некоторые: Android
- Создание хеш-ключа для приложения Android в Android
- Сгенерировать push-уведомление для тестовых целей с помощью phpfiddle
- Генерация сигнала с помощью ffmpeg
- Обнаружение жестов на просмотрах: Android
- Запрос GET и POST с использованием HTTP-клиента по умолчанию
- Получить растровое изображение из Drawable: Android
- Получение цвета в месте изображения при касании: Android
- Получить местоположение с помощью Cell ID
- Определение местоположения без провайдера GPS и СЕТИ: Android
- Получите код / название версии сборки установленного приложения Android: Android | Котлин
- Просмотр анимации GIF: Android
- Ссылки Git: Android
- Git искал ссылки: Android
- Ошибка использования GlideAnnotation в Kotlin; Котлин | Android
- Google AdMob
- Google adview
- Google Map: отображение всех маркеров карты в рамке: Android
- Google Place автоматически заполняет намерение API
- Google Play Merchant In-app Billing Sample Code Обновление безопасности
- Google Play Store Требования к загрузке Android APK
- Google+ логин и список контактов: Android
- Команда Gradle
- Хранилище Gradle в локальной файловой системе
- Gradle занимает слишком много времени: Android Studio
- Макет верхнего и нижнего колонтитула для RecyclerView
- Шестнадцатеричный код цвета для прозрачности
- Скрытые коды для вашего Android-устройства, о которых вы, вероятно, не знали
- Скрыть / отключить программную клавиатуру при запуске действия: Android
- Программно скрыть / показать значок приложения: Android
- Дом
- Как мне проверить в SQLite, существует ли таблица?
- Как получить возвращаемое значение из метода внутреннего потока: Android
- Как добавить файл helpl в android studio
- Как добавить заголовок или другой тип представлений в RecyclerView в Android
- Как избежать одновременного нажатия нескольких кнопок в android?
- Как размыть изображения с помощью Android's RenderScript
- Как привязать все булавки на экране карты
- Как изменить учетные данные для репозитория SVN в Eclipse: Android
- Как изменить цвет строки состояния в android
- Как изменить цвет текста по умолчанию и цвет фона Webview
- Как проверить доступна ли симка в android
- Как программно закрыть панель уведомлений: Android
- Как создать копию объекта в Android
- Как создать приложение FullScreen с помощью appcompact
- Как включить службы определения местоположения в приложении: Android
- Как включить мультидекс: Android Studio
- Как включить отладку по USB на Xiaomi Mi Max / MIX (MIUI 8)
- Как заполнить круглый индикатор выполнения, например разряд батареи или зарядка
- Как заставить EditText принимать только числа?
- Как получить все действия, доступные в пакете: Android
- Как получить версию приложения, которая есть в магазине Google Play
- Как получить растровое изображение из представления в android (скриншот макета)
- Как получить список друзей Facebook в Android
- Как получить информацию обо всех беговых занятиях: Android
- Как получить сертификат отпечатка пальца SHA1 в Android Studio
- Как установить FFmpeg в Windows
- Как перебирать карту / хэш-карту: Android
- Как заблокировать файлы в eclipse с помощью svn-subversion
- Как управлять startActivityForResult на Android?
- Как вручную включить внешний пакет aar
- Как открыть и удалить установленное приложение в android
- Как воспроизвести mp4 / видео с url-адреса в android
- Как изменить размер TextView панели инструментов по умолчанию; Android
- Как вернуть клики WebView в том же веб-просмотре: Android
- Как показать пустой вид с помощью RecyclerView
- Как показать gif изображение в android
- Как остановить активный отдых при ориентации экрана?
- Как сделать снимок экрана и опубликовать его программно на Android
- Как использовать настраиваемые разрешения в Android?
- Как использовать полноразмерный ListView (ExpandableHeightListView) и GridView (ExpandableHeightGridView) в ScrollView: Android
- Как использовать библиотеку Greenrobot EventBus
- Как проверить, что APK подписан или отладить?
- Пользовательские утилиты масштабирования изображения
- Утилиты масштабирования изображений
- Важно для поддержки RTL в дизайне Android
- Увеличение и уменьшение громкости системы и отображение интерфейса управления громкостью: Android
- Неопределенный горизонтальный ProgressBar
- Обратный вызов интерфейса: Android
- Вопросы для собеседования: Android
- Введение в Model View Presenter на Android
- IOException: не удалось найти байт-код при обновлении до Android Studio 3+
- Мое приложение Android в настоящее время находится на переднем плане или в фоновом режиме?
- Статические методы Java | переменные в Kotlin
- java.
Плагины
Реализация
Ошибка
Обновление приложения
Превышен предел накладных расходов сборщика мусора
Создание