Се­год­ня в выпус­ке: security-нов­шес­тва Android 12, иден­тифика­ция Android-устрой­ств по обо­ям, советы, как избе­жать чрез­мерно­го соз­дания объ­ектов, FAQ по Jetpack Compose и самые популяр­ные воп­росы на собесе­дова­нии. А так­же: смерть ViewModel и под­борка биб­лиотек для раз­работ­чиков.
 

Почитать

 

Security-новшества Android 12

Android 12 Release Notes — офи­циаль­ный документ коман­ды раз­работ­чиков Google о нов­шес­твах плат­формы Android 12. Мы сос­редото­чим­ся исклю­читель­но на новых фун­кци­ях безопас­ности.

Ог­раниче­ния на показ овер­леев. С новой вер­сии Android боль­ше не пре­дос­тавля­ет раз­решение на показ овер­леев (окон поверх экра­на) авто­мати­чес­ки для пре­дус­танов­ленных при­ложе­ний. Исклю­чение сде­лано толь­ко для сис­темных при­ложе­ний, рас­полага­ющих­ся в катало­ге /system. ОС так­же налага­ет огра­ниче­ния на овер­леи. Теперь при­ложе­ния не могут исполь­зовать неп­розрач­ные овер­леи, которые про­пус­кают через себя нажатия (рань­ше с их помощью поль­зовате­ля вынуж­дали нажать опре­делен­ные эле­мен­ты интерфей­са, перек­рыв их безобид­ным окном). Кро­ме того, при­ложе­ния теперь могут поп­росить сис­тему не показы­вать овер­леи поверх сво­их окон с помощью раз­решения HIDE_OVERLAY_WINDOWS.

Воз­можность отклю­чить 2G. Android 12 поз­воля­ет отклю­чать под­дер­жку сотовых сетей стан­дарта 2G в нас­трой­ках смар­тфо­на, что­бы защитить­ся от атак, исполь­зующих пониже­ние до 2G.

Улуч­шения Wi-Fi. Android 12 под­держи­вает WPA3 Hash-to-Element (H2E), WPA2/WPA3-Enterprise transition mode и Transition Disable indication.

Раз­решения на исполь­зование Bluetooth. Android 12 вклю­чает нес­коль­ко новых раз­решений для работы с Bluetooth:

  • BLUETOOTH_SCAN — воз­можность поис­ка новых устрой­ств и под­клю­чения к ним;
  • BLUETOOTH_ADVERTISE — раз­решение на исполь­зование Bluetooth advertising;
  • BLUETOOTH_CONNECT — воз­можность под­клю­чения к при­вязан­ным устрой­ствам.

Эти раз­решения заменя­ют ранее при­меняв­шиеся раз­решения BLUETOOTH и BLUETOOTH_ADMIN, но толь­ко для при­ложе­ний, соб­ранных под Android 12. Ста­рые при­ложе­ния про­дол­жат работать как преж­де.

Ин­дикато­ры камеры и мик­рофона. Как и iOS, начиная с 12-й вер­сии Android будет показы­вать инди­като­ры исполь­зования мик­рофона и камеры в пра­вом вер­хнем углу экра­на.

 

Идентификация Android-устройств по обоям

How Android Wallpaper Images Can Threaten Your Privacy — занима­тель­ная статья о том, как иден­тифици­ровать любое устрой­ство с Android по уста­нов­ленным на экра­не обо­ям.

Не­боль­шое пре­дис­ловие. Еще в Android 2.0 появил­ся класс WallpaperManager, который мож­но исполь­зовать для управле­ния уста­нов­ленны­ми на экра­не обо­ями. Помимо про­чего, класс вклю­чает метод getDrawable(), который поз­воля­ет получить текущие обои в виде изоб­ражения. По фак­ту это была уяз­вимость, которую испра­вили толь­ко в Android 8.1, зас­тавив при­ложе­ния исполь­зовать раз­решение READ_EXTERNAL_STORAGE, что­бы получить обои.

В той же Android 8.1 у клас­са WallpaperManager появил­ся дру­гой метод — getWallpaperColors(), поз­воля­ющий извлечь три «глав­ных» цве­та из обо­ев (имен­но его исполь­зует новая сис­тема темин­га Android 12). Внут­ри эта фун­кция исполь­зует метод k-сред­них, инте­рес­ная осо­бен­ность которо­го в том, что фак­тичес­ки он соз­дает уни­каль­ный «цве­товой отпе­чаток» обо­ев. В ито­ге по такому отпе­чат­ку мож­но точ­но иден­тифици­ровать устрой­ство.

Для демонс­тра­ции этой тех­ники автор соз­дал при­ложе­ние и выложил его исходни­ки на GitHub.

 

Разработчику

 

Как избежать создания объектов

Effective Kotlin Item 47: Avoid unnecessary object creation — статья о том, как сде­лать при­ложе­ние чуточ­ку быс­трее, изба­вив­шись от ненуж­ного соз­дания допол­нитель­ных объ­ектов.

Ос­новная идея: объ­екты — дорогое удо­воль­ствие. Они занима­ют память, а на их соз­дание ухо­дит хоть и сов­сем нез­начитель­ное по сов­ремен­ным мер­кам, но вре­мя. Поэто­му даже вир­туаль­ная машина Java ста­рает­ся миними­зиро­вать соз­дание допол­нитель­ных объ­ектов. Нап­ример, мож­но было бы подумать, что две сле­дующие стро­ки дол­жны быть раз­ными объ­екта­ми, но это не так (опе­ратор === срав­нива­ет ссыл­ки на объ­ект):

val str1 = "Lorem ipsum dolor sit amet"
val str2 = "Lorem ipsum dolor sit amet"
print(str1 == str2) // True
print(str1 === str2) // True

Вир­туаль­ная машина, видя два оди­нако­вых объ­екта типа String, объ­еди­няет их в один объ­ект. То же самое про­исхо­дит с типами Int и Long, но толь­ко для чисел от –128 до 127:

val i1: Int? = 1
val i2: Int? = 1
print(i1 == i2) // True
print(i1 === i2) // True

Для хра­нения каж­дого объ­екта исполь­зует­ся заголо­вок из 12 байт, который на 64-бит­ных сис­темах вырав­нен по 8 бай­там. Так что в целом заголо­вок занима­ет 16 байт, плюс сама ссыл­ка на объ­ект. Это не так мно­го, но, ког­да име­ешь дело с боль­шим количес­твом оди­нако­вых неболь­ших объ­ектов, это игра­ет свою роль. Нап­ример, Array<Int> будет занимать в пять раз боль­ше мес­та, чем IntArray, прос­то потому, что в пер­вом слу­чае каж­дое чис­ло обер­нуто в объ­ект.

На соз­дание объ­ектов так­же ухо­дит вре­мя. Оно сов­сем неболь­шое при соз­дании оди­ноч­ных объ­ектов, но может иметь серь­езные негатив­ные пос­ледс­твия, ког­да объ­ектов мно­го. Нап­ример:

class A
private val a = A()
// Benchmark result: 2.698 ns/op
fun accessA(blackhole: Blackhole) {
blackhole.consume(a)
}
// Benchmark result: 3.814 ns/op
fun createA(blackhole: Blackhole) {
blackhole.consume(A())
}
// Benchmark result: 3828.540 ns/op
fun createListAccessA(blackhole: Blackhole) {
blackhole.consume(List(1000) { a })
}
// Benchmark result: 5322.857 ns/op
fun createListCreateA(blackhole: Blackhole) {
blackhole.consume(List(1000) { A() })
}

Есть нес­коль­ко спо­собов избе­жать соз­дания объ­ектов. Один из них — исполь­зовать синг­лто­ны. Нап­ример, в сле­дующей реали­зации связ­ного спис­ка объ­ект клас­са Empty соз­дает­ся при каж­дом соз­дании спис­ка, хотя он всег­да один и тот же:

sealed class LinkedList<T>
class Node<T>(
val head: T,
val tail: LinkedList<T>
) : LinkedList<T>()
class Empty<T> : LinkedList<T>()
// Usage
val list: LinkedList<Int> =
Node(1, Node(2, Node(3, Empty())))
val list2: LinkedList<String> =
Node("A", Node("B", Empty()))

Прос­то заменим его синг­лто­ном:

object Empty : LinkedList<Nothing>()

Еще один спо­соб — исполь­зовать пул объ­ектов. Так дела­ет, нап­ример, биб­лиоте­ка под­дер­жки корутин. Вмес­то соз­дания потока для каж­дой фоновой задачи она под­держи­вает пул (по сути, мас­сив) потоков и запус­кает код на уже под­готов­ленном потоке. Прос­тей­шая реали­зация пула может выг­лядеть при­мер­но так:

private val connections: MutableMap<String, Connection> =
mutableMapOf<String, Connection>()
fun getConnection(host: String) =
connections.getOrPut(host) { createConnection(host) }

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии