Android сколько процессорного времени занимает поток

Обновлено: 01.07.2024

Как разогнать смартфон на Android — пошаговый гайд и опасности

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

Чем чреват разгон смартфона Android — подводные камни

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

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

Плюсы разгона смартфона — кому нужно

Главный плюс разгона — это повышение общей производительности устройства. Быстродействие после поднятия тактовой частоты процессора может вырасти от 10 % до 30 %. Нужен разгон тем, кто недоволен текущим уровнем производительности смартфона: если он не тянет какое-либо приложение, тормозит в процессе работы и откликается очень медленно, то разгон может стать решением.

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

Какой софт использовать для разгона смартфона — практические советы, примеры

Для разгона Android-смартфона можно использовать различный софт, включая специализированные приложения, например:

AnTuTu CPU Master или Kernel Adiutor. Плюсы — большинство этих приложений бесплатны и разгонять устройство можно, что называемся «в два клика».

Минусы приложений — чтобы разгонять телефон при помощи них, должны быть активированы root-права.

  1. Скачиваем apk-файл Framaroot на компьютер и переносим его в любую удобную папку, которая находится во внутреннем хранилище устройства.
  2. Устанавливаем приложение из apk-файла.
  3. Запускаем приложение.


Даем доступ к данным и устанавливаем приложение из apk-файла


Выбираем любой из доступных эксплоитов


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

Разогнать через пользовательское ядро можно и Xiaomi

Как разогнать смартфон через замену пользовательского ядра

Для разгона нам понадобятся следующие составляющие:

  • Root-права на разгоняемом устройстве;
  • номер модели смартфона;
  • пользовательское ядро;
  • кастомное рекавери — это сторонняя среда восстановления, которая используется вместо стандартной для создания/восстановления резервных копий данных.
    Первым делом необходимо узнать номер модели устройства. Для этого открываем «Настройки» и выбираем пункт «Сведения о телефоне». Нас интересует строка «Номер модели».


Сохраняем «Номер модели» телефона

Поясним, что такое пользовательское ядро. Это компонент, который управляет всем оборудованием смартфона, включая, процессор, GPS, Wi-Fi, Bluetooth.

Теперь переходим к поиску кастомного ядра — для популярных смартфонов найти его не составит труда. Для этого открываем специализированный раздел 4pda и выбираем производителя устройства. Например: вот все доступные кастомные ядра для смартфона Xiaomi Redmi 4x. Очень много прошивок есть на XDA Developers.

Кроме этого, можно просто воспользоваться поиском Google по шаблону: «кастомное ядро_производитель_смартфона».

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

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

Хороший разгонный потенциал имеют смартфоны Samsung, например — A51

Прошиваем смартфон — подготовка, загрузка, разгон тактовой частоты

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

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


Скопировать пользовательское ядро можно в любую удобную папку. Например, мы создали новую папку Custom Core

На всякий случай, если кастомное рекавери по каким-то причинам не сработает, нужно скопировать архив с прошивкой в корень внутреннего хранилища. Сделать это можно, подключив смартфон к компьютеру по USB-интерфейсу.

Отключаем смартфон — теперь нужно войти в режим Fastboot. Способ будет различаться в зависимости от производителя устройства. В нашем случае режим быстрой загрузки активируется одновременным нажатием клавиш «Питание» и «Увеличение громкости».


Для перепрошивки загружаем смартфон в режиме Fastboot

В появившемся меню необходимо выбрать режим загрузки «Recovery».


Выбираем режим загрузки Recovery

Скриншоты в режиме загрузки Fastboot сделать нельзя, поэтому просто опишу алгоритм пошагово и максимально подробно:

  1. При помощи кнопок регулировки громкости, выбираем «Custom Recovery».
  2. Выбираем пункт TWRP (для создания кастомного рекавери использовалась программа TWRP, если вы использовали другую программу, то нужно будет выбрать ее).
  3. Выбрать вариант «Установить» (или Install) и указать папку, куда мы скопировали архив пользовательского ядра, в нашем случае — это папка, куда скопирован архив ElementalX.


Для поднятия частоты процессора я использовал пользовательское ядро ElementalX

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

Айфон разогнать не получиться — iOS максимально закрыта от пользователя

Как разогнать смартфон при помощи приложения

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

Для работы всех перечисленных приложений, как уже упоминалось выше, требуются root-права на устройстве.

Для разгона CPU особенно удобно использовать приложение AnTuTu CPU Master. В нем тактовая частота регулируется при помощи ползунка. Помните, что нельзя сразу увеличивать частоту процессора до максимального уровеня. Делать это нужно постепенно, прибавляя по 5-10 %. При этом нужно внимательно следить за тем, как ведет себя устройство под нагрузкой.


Регулирование минимальной/максимальной тактовой частоты при помощи приложения AnTuTu CPU Master

Если после повышения тактовой частоты устройство работает стабильно, не начинает перегреваться и корректно реагирует на сенсор, можно постепенно добавить еще 5 %. Повышать частоту нужно до тех пор, пока смартфон работает стабильно.

Работу смартфона следует проверять исключительно под нагрузкой, так как CPU работает на максимальных частотах только в стрессовых условиях.

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

Разгон чаще всего не имеет практической пользы. На практике корректно разогнанный смартфон получает совсем небольшой прирост быстродействия — около 10 %. В исключительных случаях — до 20–30 %. Такой результат, во многих случаях, и вовсе остается незамеченным. Более заметен разгон будет на совсем устаревших смартфонах, которые не поддерживают многозадачность и были выпущены много лет назад.

Если вы новичок в общении с RxJava или пытались разобраться в этом, но не довели дело до конца, то ниже вы найдете для себя кое-что новое.

image


Оригинал статьи написан 29 ноября 2017. Перевод вольный.

Нам в GO-JEK требуется выполнять большое количество асинхронных операций в приложениях и мы не можем позволить себе идти на компромиссы в ущерб скорости работы и плавности пользовательского интерфейса.

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

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

Почему реактивное программирование?

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

Никаких больше обратных вызовов

Если вы давно разрабатываете под Android, то, должно быть, заметили, как быстро вещи становятся чересчур сложными и неподконтрольными с использованием вложенных обратных вызовов.

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

Простой контроль ошибок

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

Очень простое использование многопоточности

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

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

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

RxJava НЕ многопоточна по умолчанию

Да, вы прочли всё верно. RxJava по умолчанию не многопоточна в любом случае. Определение, данное для RxJava на официальном сайте, выглядит примерно следующим образом:
«Библиотека для составления асинхронных и основанных на событиях программ с использованием последовательностей (observable sequences) для виртуальной Java машины».

Увидев слово «асинхронных», многие люди ошибочно полагают, что RxJava многопоточна по умолчанию. Да, RxJava поддерживает многопоточность, предлагает множество мощных возможностей для легкой работы с асинхронными операциями, но это не значит что поведение RxJava по умолчанию многопоточно.

Если вы уже немного работали с RxJava, то её знаете базовые конструкции:

  • Наблюдаемый источник (source Observable), далее
  • несколько операторов (Operators), затем
  • целевой подписчик (Subscriber)

Если вы запустите данный пример кода, то ясно увидите, что все действия выполняются в основном потоке приложения (проследите за именами потоков в логе в консоли). Этот пример показывает, что по умолчанию поведение RxJava блокирующее. Всё выполняется в том же потоке, в котором вызван код.

Бонус: Интересно, что же делает doOnNext() ? Это не что иное, как side-effect оператор. Он помогает внедряться в цепочку объектов observable и выполнять грязные (impure) операции. Например, внедрять дополнительный код в цепочке вызовов для отладки. Прочитать больше можно здесь.

Простой пример

Для того, чтобы начать работать с многопоточностью с применением RxJava необходимо познакомиться с базовыми классами и методами, такими как Schedulers, observeOn/subscribeOn.

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

Здесь мы видим метод getBooks() , который осуществляет сетевой вызов и собирает список книг для нас. Сетевой вызов занимает время (несколько миллисекунд или секунд), поэтому мы используем subscribeOn() и указываем планировщик Schedulers.io() для выполнения операции в потоке ввода-вывода.

Также мы используем оператор observeOn() вместе с планировщиком AndroidSchedulers.mainThread() для того, чтобы обрабатывать результат в основном потоке и показать список книг в пользовательском интерфейсе приложения.

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

Подружимся с планировщиками (Schedulers)

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

Можете представлять планировщики как потоки или пулы потоков (коллекции потоков) для выполнения разного рода задач.

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

В RxJava доступны несколько типов планировщиков. Самая сложная часть — выбрать верный планировщик для вашей задачи. Задача никогда не будет выполняться оптимально, если вы не выберете верный планировщик. Давайте разберем каждый планировщик.

Schedulers.io()

Этот планировщик основывается на неограниченном пуле потоков и используется для интенсивной работы с вводом-выводом без использования ЦП, например, доступ к файловой системе, выполнение сетевых вызовов, доступ к базе данных и так далее. Количество потоков в этом планировщике неограничено и может расти по мере необходимости.

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

Schedulers.newThread()

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

Schedulers.single()

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

Schedulers.from(Executor executor)

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

Допустим, вы хотите ограничить число параллельных сетевых вызовов, которые делает ваше приложение. Можно создать собственный планировщик, который будет работать на базе ограниченного в размерах пула потоков ( Scheduler.from(Executors.newFixedThreadPool(n)) ) и использовать его во всех местах, связанных с сетевыми вызовами.

AndroidSchedulers.mainThread()

Это специальный планировщик, который недоступен в библиотеке RxJava. Необходимо использовать расширяющую библиотеку RxAndroid для доступа к этому планировщику. Этот планировщик полезен в Android приложениях для выполнения действий в потоке пользовательского интерфейса.
По умолчанию этот планировщик ставит задания в очередь в Looper , связанный с основным потоком, но есть возможность переопределения: AndroidSchedulers.from(Looper looper) .

Заметка: Будьте осторожны в использовании планировщиков, основанных на неограниченных пулах потоков, таких как Schedulers.io() . Всегда есть риск бесконечного роста количества потоков.

Понимание subscribeOn() и observeOn()

Теперь, когда у вас есть представление о типах планировщиков, разберем subscribeOn() и observeOn() в деталях.

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

subscribeOn()

Простыми словами, этот оператор говорит в какой поток наблюдаемый источник (source observable) будет передавать элементы. Вы должны уяснить важность слова «источник». Когда у вас цепь наблюдаемых элементов (observables), источник (source observable) — это всегда корневой элемент или верхняя часть цепи, откуда происходит создание событий.

Как вы уже видели, если не использовать subscribeOn() , то все события происходят в том потоке, в котором произошел вызов кода (в нашем случае — main поток).

В целях сокращения кода мы не будем полностью переопределять все методы DisposableSubscriber , так как нам не нужно переопределять onError() и onComplete() . Воспользуемся doOnNext() и лямбдами.


Не важно в каком месте в цепочке вызовов вы используете subscribeOn() . Он работает только с наблюдаемым источником (source observable), и контролирует в какой поток наблюдаемый источник передает события.

В нижеследующем примере после observable-источника создаются другие объекты observable (методами map() и filter() ), а оператор subscribeOn() помещен в конце цепочки вызовов. Но как только вы запустите этот код, то заметите, что все события будут возникать в потоке, указанном в subscribeOn() . Это станет более понятным при добавлении observeOn() в цепь вызовов. И даже если мы разместим subscribeOn() ниже observeOn() , то логика работы не изменится. subscribeOn() работает только с наблюдаемым источником (source observable).


Также важно понять, что нельзя использовать subscribeOn() несколько раз в одной цепочке вызовов. Можно, конечно, написать ещё раз, но никаких изменений это не повлечет. В примере ниже мы последовательно вызываем три различных планировщика, можете ли вы догадаться, какой планировщик сработает при запуске?


Если вы ответили Schedulers.io() , то вы правы! Даже если делать вызов многократно — сработает только первый subscribeOn() , вызванный после observable-источника.

Под капотом

Стоит потратить ещё немного времени на более подробное изучение рассмотренного примера. Почему срабатывает только планировщик Schedulers.io() ? Обычно все думают, что сработает Schedulers.newThread() , так как этот вызов находится в конце цепочки.


Для того, чтобы понять как всё работает — начнем разбирать всё с последней строки примера. В ней целевой подписчик (target subscriber), вызывает метод subscribe() у observable объекта o3 , который затем делает неявный вызов subscribe() у своего родительского observable объекта o2 . Реализация наблюдателя (observer), предоставляемая объектом o3 , умножает переданные числа на 10.

Процесс повторяется и o2 неявно вызывает subscribe() у объекта o1 , передавая реализацию наблюдателя, которая позволяет обрабатывать только четные числа. Теперь мы достигли корневого элемента ( o1 ), у которого нет родителя для последующего вызова subscribe() . На этом этапе завершается цепочка наблюдаемых (observable) элементов, после чего observable-источник начинает передавать (emit) элементы.

Теперь для вас должна быть понятна концепция работы подписок в RxJava. К настоящему времени у вас должно появиться понимание того, как формируются цепочки наблюдаемых (observable) объектов и как события распространяются, начиная с observable-источника.

observeOn()

Как мы уже видели, subscribeOn() указывает observable-источнику передавать элементы в определенный поток и этот поток будет отвечать за продвижение элементов вплоть до подписчика (Subscriber). Поэтому, по умолчанию, подписчик получает обработанные элементы в этом же потоке.

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

Нужно выполнить две вещи:

  • Сделать сетевой вызов в неблокирующем потоке ввода-вывода
  • Получить результат в основном потоке приложения

Теперь нам крайне необходимо переключить потоки и мы используем для этого observeOn() . Когда observeOn() встречается в цепочке вызовов, то передаваемые observable-источником элементы незамедлительно перебрасываются в поток, указанный в observeOn() .


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

Теперь рассмотрим более сложный пример, в котором observeOn() будет вызываться несколько раз для переключения потоков в процессе обработки данных.

Следующим шагом отфильтруем некоторые элементы и по какой-то причине мы хотим выполнить эту операцию в новом потоке для каждого из элементов. Используем снова observeOn() , но уже в паре с Schedulers.newThread() перед вызовом оператора filter() для передачи каждого элемента в новый поток.

В итоге мы хотим, чтобы подписчик получил результат обработки в потоке пользовательского интерфейса. Для этого используем observeOn() вместе с планировщиком AndroidSchedulers.mainThread() .

Но что произойдет, если использовать observeOn() несколько раз последовательно? В примере ниже в каком потоке подписчик получит результат?


Если запустите пример, то увидите, что подписчик получит элементы в вычислительном потоке RxComputationThreadPool-1 . Это значит, что сработал последний вызванный observeOn() . Интересно почему?

Под капотом

Резюме

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

Если понимание не пришло сразу, ничего страшного. Прочитайте статью ещё раз, поэкспериментируйте с примерами кода. Здесь достаточно много нюансов для понимания, не торопитесь.


При создании мобильного приложения чуть сложнее «Hello, world» почти наверняка требуется скачать что-то из Сети или считать файл с диска. Для стабильной работы программы в целом эти действия должны совершаться в отдельных потоках. Зачем, когда и как генерировать новые потоки в Android — об этом ты узнаешь в этой статье.

Процессы и потоки

Прежде чем разбираться с Android API, вспомним, какой структурой обладает эта ОС. В ее основе лежит Linux-ядро, в котором реализованы базовые механизмы, присущие всем *nix-системам. В ядре собраны модули, предназначенные для низкоуровневой работы: взаимодействия с железом, организации памяти, файловой системы и так далее.

В мире Linux каждая запущенная программа — это отдельный процесс. Каждый процесс обладает уникальным номером и собственной «территорией» — виртуальным адресным пространством, в рамках которого содержатся все данные процесса. Поток же — это набор инструкций внутри запущенной программы (процесса), который может быть выполнен отдельно. У потока нет своего уникального идентификатора и адресного пространства — все это он наследует от родительского процесса и делит с другими потоками.


Массовое распространение в Google Play приложений, имеющих проблемы с утечкой памяти, резонно вызовет у пользователей ощущение, что «Android тормозит»

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

Когда в компьютерах (а вслед за ними — в планшетах и телефонах) появились процессоры с несколькими ядрами, программисты внедрили в ОС планировщик задач. Такой планировщик самостоятельно распределяет нагрузку по всем ядрам процессора, исполняя блоки кода параллельно или асинхронно, и тем самым повышает производительность. Поначалу маркетологи даже продавали компьютеры с лозунгом «Два ядра — в два раза быстрее», но, к сожалению, действительности он не соответствует.

В Android программист обязан повсеместно создавать новые потоки и процессы. Все операции, которые могут продлиться более нескольких секунд, должны обязательно выполняться в отдельных потоках. Иначе начнутся задержки в отрисовке интерфейса и пользователю будет казаться, что приложение «зависает».

Вообще, суть многопоточного программирования в том, чтобы максимально задействовать все ресурсы устройства, при этом синхронизируя результаты вычислений. Это не так легко, как может показаться на первый взгляд, но создатели Android добавили в API несколько полезных классов, которые сильно упростили жизнь Java-разработчику.

Потоки в Android

Запущенное в Android приложение имеет собственный процесс и как минимум один поток — так называемый главный поток (main thread). Если в приложении есть какие-либо визуальные элементы, то в этом потоке запускается объект класса Activity, отвечающий за отрисовку на дисплее интерфейса (user interface, UI).

Thread и Runnable

Базовым классом для потоков в Android является класс Thread, в котором уже все готово для создания потока. Но для того, чтобы что-то выполнить внутри нового потока, нужно завернуть данные в объект класса Runnable. Thread, получив объект этого класса, сразу же выполнит метод run .

Но при такой организации сложно использовать всю силу дополнительных потоков — нельзя ни поменять задачу, ни поcмотреть результат вычислений. Хотя все это происходит в едином адресном пространстве, у Java-разработчика нет возможности просто так получить ресурсы соседних потоков.

Looper

Чтобы организовать очередь, нужно воспользоваться классами Handler и Looper: первый отвечает за организацию очереди, а второй в бесконечном цикле проверяет, нет ли в ней новых задач для потока.

Запуск такого потока устроен по похожей схеме — создание нового объекта и вызов метода start .

После выполнения этого метода создастся новый поток, который заживет своей жизнью. А это значит, что инициализация переменных и создание объектов будут уже идти параллельно с теми вызовами, которые забиты в следующих строчках после команды myLooper.start() . Поэтому перед обращением к очереди в новом потоке нужно немного подождать — объект handler может еще не существовать.

AsynkTask

Класс AsyncTask — это представитель Java-обобщений (generic) в мире Android. У классов-обобщений заранее не определен тип данных, с которыми им предстоит работать. Этот прием позволяет создать класс, который в последующем сможет без проблем работать с любым типом данных. В данном случае благодаря дженерику AsynkTask возможно запускать новые потоки с совершенно произвольными объектами внутри.

C помощью AsyncTask теперь можно почти не думать (о вероятных последствиях позже) о создании потока, а просто создать объект и обрабатывать результаты. К примеру, с помощью AsyncTask удобно преобразовывать файлы (например, их зашифровать), при этом сам метод шифрования modifyFile может быть объявлен в Activity главного потока.

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

Для запуска нового потока достаточно одной строчки в Activity:

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

Сложность с отменой

Для отмены вычислений существует метод cancel(boolean) , который в идеале должен остановить поток и высвободить ресурсы. Но этого не происходит. В случае если он вызван с аргументом false, вычисления будут продолжены, только их результат не будет возвращен в UI. Вызов cancel(true) лишь частично поможет решить проблему, поскольку существуют методы, которые из-за механизма синхронизации прервать нельзя, — к примеру, получение изображения с помощью BitmapFactory.decodeStream() .

Потеря результатов

Архитектура приложений построена таким образом, что главный поток может быть перезапущен в любой момент, — например, при перевороте устройства пользователем создается новый экземпляр Activity и в нем выполняется метод onCreate() . В этом случае у нового экземпляра Activity не будет связи с объектом AsyncTask, созданным и запущенным «старым» Activity. Поэтому все вычисления, которые не успели завершиться в AsyncTask до переворота устройства, будут потеряны.

Утечка памяти

А это самый неприятный недостаток AsyncTask, который напрямую следует из предыдущего пункта. После запуска нового Activity прошлый экземпляр UI должен быть выгружен из памяти сборщиком мусора. Но этого не произойдет, поскольку на «старый» UI есть ссылка в работающем потоке, созданном AsyncTask. Ничего не поделать, придется создавать еще один поток и запускать все вычисления в нем по новой. Но есть риск, что пользователь опять повернет экран! При такой организации рабочего процесса вся выделенная память потратится на содержание ненужных Activity и дополнительных потоков, и ОС принудительно завершит приложение с ошибкой OutOfMemoryException.

Что же делать?

Сделать экземпляр AsyncTask статическим и использовать слабые связи (WeakReference). При таком подходе в приложении не будут генериться лишние потоки, а слабая связность позволит сборщику мусора выгрузить ненужные Activity из памяти.

WeakReference

Немного о связях в Java. Создавая новый объект и ассоциируя его с переменной, мы создаем ссылку между ними. Привычное создание объекта сопровождается созданием сильной (strong) ссылки.

В Java нет принудительного уничтожения объектов, этим занимается сборщик мусора. Пока сильная ссылка существует, объект будет висеть в памяти. Разрушить такую ссылку можно только вручную, приравняв переменную sObj к null . Если же объект связан только слабыми ссылками (WeakReference), то сборщик мусора при первой же возможности выгрузит его из памяти.

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

Loaders

Пожалуй, основная задача 90% всех мобильных приложений вообще — это быстро и незаметно для пользователя загрузить данные из сети или файловой системы, а затем красиво отобразить их на дисплее. Для всего этого отлично подходит AsyncTask, но его проблемы не только неочевидны для неопытных разработчиков, но и плохо детектируются в процессе отладки.

Массовое распространение в Google Play приложений, имеющих проблемы с утечкой памяти, резонно вызовет у пользователей ощущение, что «Android тормозит». Компания Google решила взять ситуацию под свой контроль и добавила в API класс-загрузчик (Loader), который еще больше упрощает генерацию потоков и самостоятельно обходит слабые места AsyncTask. Создание нового потока теперь ведется через класс AsyncTaskLoader, который обязательно должен быть статическим.

Его реализация очень похожа на то, что приходится делать при использовании родительского AsyncTask, только теперь все вычисления в новом потоке находятся в методе loadInBackground .

Проблемы, которые вылезли в AsyncTask, решены путем введения промежуточного звена — менеджера загрузки. Класс LoaderManager.LoaderCallbacks управляет созданным потоком и по окончании вычислений возвращает данные в действующий Activity. Теперь достаточно быстро можно создать код, экономящий ресурсы и решающий проблему перезапуска Activity: вычисления продолжатся в самом первом потоке, а менеджер подключит новый Activity к ранее созданному потоку.

Для примера поместим на экран ListView, данные для которого поступят из сгенерированного потока. Менеджер потока тоже класс-дженерик, сегодня он будет работать со списком строк.

Теперь нужно создать менеджер и подключить к нему поток (Loader). Под управлением у менеджера может быть несколько потоков с уникальными номерами, но менеджер в Activity должен быть только один.

Данные будут обновляться после нажатия кнопки — к примеру, FloatingActionButton. Для доступа к менеджеру из обработчика setOnClickListener нужно добраться до контекста приложения и вытащить оттуда экземпляр класса LoaderManager.

Создаваться поток будет в методе onCreateLoader , который ОС вызовет и обработает самостоятельно. В качестве параметров этот метод принимает уникальный номер будущего потока (31337), а также объект класса Bundle, через который можно задавать параметры по связке «ключ — значение».

После того как AsyncTaskLoader выполнит все действия, в менеджере сработает метод onLoadFinished . Для передачи данных в UI тут нужно заново получить доступ к визуальным объектам, так как они могли быть пересозданы.

Чтобы избежать ошибок с передачей некорректных данных, нужно еще заполнить метод onLoaderReset . Он вызывается в том случае, если действия, выполняющиеся в AsyncTaskLoader, были отменены или перезапущены.

По каким-то причинам в загрузчике не был реализован аналог метода onProgressUpdate из AsyncTask. Но это возможно сделать самостоятельно, передавая данные в UI с помощью слабых ссылок.

To be continued

Сегодня мы разобрали особенности генерации потоков, которые могут быть не так очевидны, когда ты только-только начинаешь работать с Java или Android. Надеюсь, мир операционной системы от Google стал немножко понятней и у тебя появилось желание написать что-нибудь самому.

Тема потоков и процессов слишком большая, чтобы ее раскрыть в одной статье. Есть даже программисты, которых ценят именно за то, что они лучше всех умеют распараллеливать программу! Нам еще есть о чем поговорить — в стороне остались сервисы и процессы, поэтому в следующем номере мы продолжим разбираться с многопоточностью в Android. Пока почитай что-нибудь самостоятельно по теме, а если будут вопросы — пиши мне на почту. Удачи!

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

Обновите прошивку на телефоне Андроид

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


Установка кастомной прошивки на Андроид

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

Обычно в такой прошивке установлены моды созданные сообществом, что увеличивает производительность или добавляет новые функции. Еще кастомная прошивка может обновить ваше устройство до последней версии Андроид, даже если устройство не поддерживается официально.


Уберись на рабочем столе Андроид

Установка лаунчера, очень часто помогает сделать устройство более чистым. Я лично стараюсь ограничить рабочий стол своего смартфона одним рабочим столом. Это не ускорит Андроид, но ускорят мою работу со смартфоном, так как я могу быстрей перейти к нужному приложению.

Уменьшите анимацию на Андроид


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

Принудительный рендеринг GPU Андроид



Пока вы находитесь в режиме разработчика, можете попробовать включить Force 4x MSAA. Это метод сглаживания, который не увеличит FPS в игре, но может сделать их визуализацию более красивой. Но, увеличит нагрузку на батарею и может привести к определенному замедлению смартфона. Тем не менее, изменение этих настроек не принесут вреда телефону. Попробуйте включить их, сравнит, и выбрать то, что вам больше нравиться.

Очистить кеш память Андроид



Избегайте приложений для оптимизации на Андроид



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

Естественно, не забудьте проверить меню настроек самих игр! Снижение настроек графики часто может увеличить плавность игр и срок работы аккумулятора.

Удалить все начисто


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

Я, что-то пропустил?

Читайте также: