Потоки
Потоки позволяют выполнять несколько задач одновременно, не мешая друг другу, что даёт возможность эффективно использовать системные ресурсы. Потоки используются в тех случаях, когда одно долгоиграющее действие не должно мешать другим действиям. Например, у нас есть музыкальный проигрыватель с кнопками воспроизведения и паузы. Если вы нажимаете кнопку воспроизведения и у вас запускается музыкальный файл в отдельном потоке, то вы не можете нажать на кнопку паузы, пока файл не воспроизведётся полностью. С помощью потоков вы можете обойти данное ограничение.
Использование фоновых потоков
Чтобы быть уверенным, что ваше приложение не теряет отзывчивости, хорошим решением станет перемещение всех медленных, трудоёмких операций из главного потока приложения в дочерний.
Применение фоновых потоков — необходимое условие, если вы хотите избежать появления диалогового окна для принудительного закрытия приложения. Когда активность в Android на протяжении 5 секунд не отвечает на события пользовательского ввода (например, нажатие кнопки) или приёмник широковещательных намерений не завершает работу обработчика onReceive() в течение 10 секунд, считается, что приложение зависло. Подобные ситуации следует избегать любой ценой. Используйте фоновые потоки для всех трудоёмких операций, включая работу с файлами, сетевые запросы, транзакции в базах данных и сложные вычисления.
Android предоставляет несколько механизмов перемещения функциональности в фоновый режим.
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- Handlers
- AsyncTask
Класс AsyncTask позволяет определить операции, которые будут выполняться в фоне, вы также будете иметь доступ к обработчику событий, что позволит отслеживать прогресс выполнения задач и выводить результаты в контексте главного графического потока. Подробнее об этом классе в отдельной статье.
Хотя использование AsyncTask — хорошее решение, случается, что для работы в фоновом режиме приходится создавать собственные потоки и управлять ими.
В Java есть стандартный класс Thread, который вы можете использовать следующим образом:
Данный способ подходит только для операций, связанных с временем. Но вы не сможете обновлять графический интерфейс программы.
Если вам нужно обновлять интерфейс программы, то нужно использовать AsyncTask, о котором говорилось выше, или вы можете реализовать ваш собственный класс, наследованный от Thread, используя объект Handler из пакета android.os для синхронизации с потоком GUI перед обновлением пользовательского интерфейса.
Вы можете создавать дочерние потоки и управлять ими с помощью класса Handler, а также классов, доступных в пространстве имён java.lang.Thread. Ниже показан простой каркас для переноса операций в дочерний поток.
Плохое приложение
Напишем «плохое» приложение, неправильно использующее основной поток. Однажды мы писали программу для подсчёта ворон. На этот раз будем считать чёрных котов, которые перебегают нам дорогу. Зачем они это делают — молчит наука. Может быть собранная статистика поможет разгадать тайну. Добавим на экран активности кнопки и текстовую метку. Код для щелчка кнопки.
Для имитации тяжёлой работы программа делает паузу на двадцать секунд, а потом выводит текст с подсчётом котов. Если нажать на кнопку один раз и подождать двадцать секунд, то программа отработает как положено. Но представьте себе, что вы нажали на кнопку один раз. Программа запустила паузу. Вы, не дожидаясь окончания паузы, снова нажали на кнопку. Программа должна выполнить вашу команду, но предыдущая команда ещё не отработала и наступает конфликт. Попробуйте нажать на кнопку несколько раз с небольшими перерывами. В какой-то момент приложение зависнет и выведет системное диалоговое окно:
В реальных приложениях такое окно может разозлить пользователя и он поставит низкую оценку вашему приложению.
В данном случае ошибку вызывает не сам вывод текста в текстовой метке, который, к слову, тоже выполняется в основном потоке, а сам щелчок кнопки. Если вы закомментируете последние две строчки кода, связанные с TextView, то ошибка сохранится.
Вам необходимо перенести трудоёмкую задачу в отдельный поток. Для этого создаётся экземпляр класса Runnable, у которого есть метод run(). Далее создаётся объект Thread, в конструкторе у которого указывается созданный Runnable. После этого можно запускать новый поток с помощью метода start(). Перепишем пример.
Весь код мы перенесли в метод run(). Теперь вы можете безостановочно щёлкать по кнопке. На этот раз приложение сохранит свою работоспособность. Чтобы в этом убедиться, в код добавлено протоколирование логов Log.i(). При каждом нажатии создаётся новый поток, в котором выполняется код. Потоки друг другу не мешают и дожидаются своей очереди, когда система позволит им отработать.
Основной поток также называют UI-потоком. Имено в главном потоке можно обновить текст у текстовой метки. В создаваемых нами потоках это делать нельзя. Если вы уберёте комментарии с последнего примера и запустите проект, то получите сообщение об ошибке.
Нужен некий посредник между создаваемыми потоками и основным UI-потоком. В роли такого посредника служит класс Handler (полное название класса android.os.Handler, не перепутайте). Вам нужно создать экземпляр класса и указать код, который нужно выполнить.
После строчки кода с Log.i() добавьте вызов метода посредника.
Поток вызывает посредника, который в свою очередь обновляет интерфейс. В нашем случае посредник посылает пустое сообщение от потока.
Но бывает так, что от потока требуется получить информацию для обработки. Ниже упрощённый пример.
Запуск потока
Предположим, мы разрабатываем собственный проигрыватель. У нас есть кнопка Play, которая вызывает метод play() для воспроизведения музыки:
Теперь запустим метод в другом потоке. Сначала создаётся новый поток. Далее описывается объект Runnable в конструкторе потока. А внутри созданного потока вызываем наш метод play(). И, наконец, запускаем поток.
Усыпить поток
Иногда требуется временно приостановить поток («усыпить»):
Приоритет потоков
Для установки приоритета процесса используется метод setPriority(), который вызывается до запуска потока. Значение приоритета может варьироваться от Thread.MIN_PRIORITY (1) до Thread.MAX_PRIORITY (10):
Отмена выполнения потока
У потока есть метод stop(), но использовать его не рекомендуется, поскольку он оставляет приложение в неопределённом состоянии. Обычно используют такой подход:
Существует и другой способ, когда все запускаемые потоки объявляются демонами. В этом случае все запущенные потоки будут автоматически завершены при завершении основного потока приложения:
Источник
Как приостановить / спать поток или процесс в Android?
Я хочу сделать паузу между двумя строками кода, позвольте мне объяснить немного:
-> пользователь нажимает кнопку (фактически карту), и я показываю ее, изменяя фон этой кнопки:
-> после, скажем, 1 секунды, мне нужно вернуться к предыдущему состоянию кнопки, изменив ее фон:
-> Я пытался приостановить поток между этими двумя строками кода с помощью:
Однако это не работает. Может быть, мне нужно приостановить процесс, а не поток?
Я также пытался (но это не работает):
Как я могу приостановить / спать поток или процесс?
Одним из решений этой проблемы является использование метода Handler.postDelayed () . Некоторые учебные материалы Google предлагают такое же решение.
Однако некоторые отмечают, что приведенное выше решение вызывает утечку памяти, поскольку оно использует нестатический внутренний и анонимный класс, который неявно содержит ссылку на свой внешний класс, активность. Это проблема, когда контекст активности собирается мусором.
Более сложное решение , которое позволяет избежать утечек памяти подкласса Handler и Runnable со статическими внутренними классами внутри деятельности , так как статические внутренних классы не занимающим неявную ссылки на их внешний класс:
Обратите внимание, что Runnable используется WeakReference для Activity, что необходимо в статическом классе, которому требуется доступ к пользовательскому интерфейсу.
Источник
Sleep () в java (Android)
Я следую этому руководству, чтобы иметь экран загрузки в моей программе. В учебном пособии говорится, что моя активность должна Sleep () с помощью команды Sleep (), однако она не распознает функцию Sleep () как функцию и предоставляет мне ошибку, спрашивая, хочу ли я создать метод Sleep ().
Вот ссылка на учебник:
Вот пример кода:
Вы можете использовать один из следующих методов:
SystemClock.sleep(milliseconds) – это функция утилиты, очень похожая на Thread.sleep(milliseconds) , но игнорирует InterruptedException . Используйте эту функцию для задержек, если вы не используете Thread.interrupt() , так как она сохранит прерванное состояние потока.
Обратите внимание, однако, что вы не должны выполнять спящий режим в потоке пользовательского интерфейса.
Код, который вы опубликовали, ужасен. Не используйте это на реальном устройстве. Вы получите ошибку «Приложение не отвечает», если вы запустите что-то похожее на это.
Если вы используете Handlers, имейте в виду, что обработчик создается в потоке, где он выполняется . Поэтому вызов new Handler().post(. в потоке пользовательского интерфейса будет выполнять runnable в потоке пользовательского интерфейса, включая эту «длительную операцию». Преимущество заключается в том, что вы можете создать обработчик для потока пользовательского интерфейса, который вы можете использовать Позже, как показано ниже.
Чтобы включить длительную операцию в фоновый поток, вам нужно создать поток вокруг runnable, как показано ниже. Теперь, если вы хотите обновить пользовательский интерфейс после завершения длительной операции, вам необходимо опубликовать его в потоке пользовательского интерфейса с помощью обработчика.
Обратите внимание, что эта функциональность идеально подходит для AsyncTask что сделает этот вид намного более чистым, чем шаблон ниже. Однако я включил это, чтобы показать, как связаны обработчики, потоки и управляемые файлы.
Источник
Android Studio: запуск и остановка звука с помощью mediaPlayer
Как start и stop воспроизводить музыку, используя одну и ту же кнопку. Приведенный ниже код будет воспроизводить песню несколько раз (с перекрытием), если я нажму на нее несколько раз.
И еще одна проблема — куда вставить функции starTimer и StopTimer , чтобы starTimer был активен, если звук не воспроизводится, и stopTimer будет активен, если звук воспроизводится.
И для функции startTimer stopTimer
4 ответа
Пожалуйста, укажите ниже код в Global:
Теперь в onCreate методе:
Кнопка паузы воспроизведения:
Когда аудио готово к воспроизведению:
Аудио завершено, затем автоматическая остановка:
Не забудьте добавить эту строку:
Чтобы остановить музыку / звук с помощью медиаплеера, создайте логическое значение isPlaying . сделать это правдой, когда музыка играет
Объявите isPlaying как глобальную переменную как
Теперь вы можете создать метод, который будет проверять значение isPlaying и воспроизводить или приостанавливать музыку, вызывая этот метод onClick() кнопки
Надеюсь, это поможет!
Приведенный ниже код будет воспроизводить песню несколько раз (с перекрытием), если я нажму на нее несколько раз.
Поскольку каждый раз, когда вы нажимаете кнопку, вы создаете экземпляр объекта медиаплеера, чтобы решить эту проблему, сделайте свой объект проигрывателя medica полем класса, а не локальным полем метода, и сначала проверьте, не является ли объект нулевым, прежде чем создавать его.
И не нужно иметь переменную, чтобы проверить, воспроизводится ли медиа, так как вы можете проверить это непосредственно из объекта медиаплеера.
Так что измени свой код на
Что касается вопроса с таймером, не очень понятно, как вы используете свой таймер?
Лучше сделать одно логическое значение при нажатии кнопки, а затем легко обрабатывать
IsClick имеет значение false, означает, что если будет выполнено другое действие. В противном случае вы делаете остановку медиаплеера и функцию остановки таймера.
Источник
Полный список
— рассмотрим еще пару способов запуска Runnable в UI-потоке
Мы подробно рассмотрели Handler и увидели, что он умеет. Главное его достоинство – это умение выполнять код в UI-потоке. Существует еще пара способов выполнять Runnable в UI-потоке. Это методы:
Первые два похожи и отправляют Runnable на немедленную обработку. Я не знаю в чем их принципиальное отличие. Если у вас есть соображения на этот счет, пишите на форуме в ветке этого урока. А третий метод позволяет указать задержку выполнения Runnable.
Создадим приложение и опробуем эти методы.
Project name: P0851_RunnableUIThread
Build Target: Android 2.3.3
Application name: RunnableUIThread
Package name: ru.startandroid.develop.p0851runnableuithread
Create Activity: MainActivity
TextView, которое будем обновлять из нового потока.
В onCreate создаем новый поток. В нем мы через паузы выполняем runn1 и runn2, и планируем runn3 с задержкой в 2000 мсек, используя вышеупомянутые методы.
runn1, runn2 и runn3 – это просто Runnable, которые обновляют текст в TextView. Они должны быть выполнены в UI-потоке.
Все сохраним и запустим. Экран пустой.
Через 2 сек выполняется runn1
Еще через секунду runn2
И еще через 2 секунды срабатывает runn3, который был отложен на 2 сек.
Тем самым, если ваши алгоритмы не особо сложны, можно использовать эти методы для выполнения кода в UI-потоке. Если же нужны навороты и алгоритм достаточно сложен, то используем Handler.
На следующем уроке:
— создаем несложный пример с AsyncTask
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник