SDK

https://developer.android.com/guide/components/activities/parcelables-and-bundles
https://developer.android.com/reference/androidx/annotation/RequiresPermission
https://developer.android.com/develop/sensors-and-location/location/permissions
https://developer.android.com/guide/topics/providers/content-providers
https://developer.android.com/guide/topics/providers/content-provider-basics
https://developer.android.com/guide/topics/providers/content-provider-creating
https://developer.android.com/guide/topics/providers/document-provider
https://developer.android.com/guide/topics/providers/create-document-provider
https://developer.android.com/guide/topics/providers/cloud-media-provider
Context

Интерфейс, предоставляющий доступ к информации о текущем состоянии приложения и его среде выполнения. Это основной компонент, который позволяет объектам взаимодействовать с системными ресурсами и службами. Божественный объект.

• Доступ к ресурсам: Через Context можно получить доступ к ресурсам приложения, таким как строки, изображения и макеты, используя методы вроде getResources и getString.

• Запуск компонентов: Context позволяет запускать активити, сервисы и получать доступ к BroadcastReceiver. Например, метод startActivity используется для запуска новых Activity.

• Доступ к файловой системе: Через Context можно получить доступ к файловой системе приложения, используя методы getFilesDir, getCacheDir и другие.

• Преференции: Для работы с хранилищем предпочтений можно использовать методы getSharedPreferences.

• Системные службы: Context позволяет получить доступ к различным системным службам, например, через метод getSystemService, чтобы взаимодействовать с компонентами, такими как LocationManager, ConnectivityManager, и другими.

// Получение Application Context
val appContext = context.applicationContext

// Запуск новой активити
val intent = Intent(context, NewActivity::class.java)
context.startActivity(intent)

// Доступ к ресурсам
val myString = context.getString(R.string.my_string)

// Работа с SharedPreferences
val prefs = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
Application Context

Context, привязанный к жизненному циклу приложения. Он существует столько, сколько работает приложение, и используется для действий, не зависящих от Activity или их состояния.

Activity Context

Context, связанный с конкретной Activity. Используется для работы с ресурсами, запуском новых Activity, доступом к системным сервисам, а также взаимодействия с элементами пользовательского интерфейса.

Service Context

Позволяет сервису взаимодействовать с системными ресурсами и компонентами.

Bundle

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

• Буфер транзакций Binder имеет фиксированный размер 1 MB, который используется всеми транзакциями в процессе, если размер превышен генерируется TransactionTooLargeException

val bundle = Bundle()
bundle.putString("key", "value")
bundle.putInt("number", 42)
Application

Базовый класс в Android, который предоставляет глобальный контекст для всего приложения.

class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
    }
}
<application
    android:name=".MyApplication"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name" />
onCreate

Вызывается один раз при запуске приложения. Используется для глобальной инициализации.

registerActivityLifecycleCallbacks

Позволяет отслеживать жизненный цикл всех Activity в приложении.

class MyApplication: Application() {
    
    override fun onCreate() {
        super.onCreate()

        registerActivityLifecycleCallbacks(object: ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

            override fun onActivityStarted(activity: Activity) {}

            override fun onActivityResumed(activity: Activity) {}

            override fun onActivityPaused(activity: Activity) {}

            override fun onActivityStopped(activity: Activity) {}

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

            override fun onActivityDestroyed(activity: Activity) {}
        })
    }
}
Parcelable
https://developer.android.com/kotlin/parcelize

Позволяет эффективно сериализовать объекты для передачи между компонентами приложения, такими как Activity и Service, или для сохранения состояния.

data class User(val name: String, val age: Int): Parcelable {
    constructor(parcel: Parcel): this(
        parcel.readString() ?: "",
        parcel.readInt()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeInt(age)
    }

    override fun describeContents(): Int = 0

    companion object {
        
        @JvmField
        val CREATOR: Parcelable.Creator<User> = object: Parcelable.Creator<User> {
            override fun createFromParcel(parcel: Parcel): User {
                return User(parcel)
            }

            override fun newArray(size: Int): Array<User?> {
                return arrayOfNulls(size)
            }
        }
    }
}
Sensor
https://developer.android.com/reference/android/hardware/Sensor

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

SensorManager
https://developer.android.com/reference/android/hardware/SensorManager

Предоставляет методы регистрации активности с датчиков и их калибровки.

SensorEvent
https://developer.android.com/reference/android/hardware/SensorEvent

Предоставляет необработанные данные датчика, включая информацию о точности.

SensorEventListener
https://developer.android.com/reference/android/hardware/SensorEventListener

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

Window
https://developer.android.com/reference/android/view/Window

Это абстрактный класс, который не является наследником Activity Fragment или View. Класс Window контролирует что и как рисуется на экране. Активити имеет один инстанс Window который можно получить методом getWindow. Window в свою очередь, имеет объект Surface и единственную иерархию View. Android-приложение использует WindowManager для создания объектов типа Window и Surface на котором рисуется контент Window. Когда UI должен обновиться, на объекте Surface вызывается метод lockCanvas который возвращает объект типа Canvas. Canvas передается вниз по иерархии View ассоциированной с Window и каждая view рисует себя на канвасе.

Permissions

Runtime Permissions (Dangerous)

Опасные разрешения, предоставляющие доступ к данным с ограниченным доступом и действиям, которые существенно влияют на систему и другие приложения. Начиная с API 23 перед перед выполнением опасных действий нужно запросить у пользователя разрешение в runtime.
• Dangerous sa
mples: location contacts storage contacts phone calls camera microphone.

shouldShowRequestPermissionRationale

Метод, который используется для того, чтобы узнать, нужно ли показывать пользователю объяснение, почему приложению требуется разрешение. Возвращает true, если пользователь ранее отклонял запрос на разрешение, но не выбрал «больше не спрашивать». Этот метод помогает улучшить пользовательский опыт, предлагая объяснение перед повторным запросом разрешения.

if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
    // Показать объяснение, почему нужно разрешение на камеру
    showPermissionExplanationDialog()
} else {
    // Запросить разрешение
    requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST_CODE)
}
Notifications

Notification Channel

Начиная с Android 8.0 (API level 26) для каждого уведомления необходимо указывать канал. Пользователь может управлять визуальным и звуковым поведением, которое будет применяться ко всем уведомлениям в канале. Например, разные каналы в приложении часы для будильников, таймеров, секундомеров

LruCache

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

val maxMemory: Long = Runtime.getRuntime().maxMemory() / 1024
val cacheSize: Int = (maxMemory / 8).toInt()

val lruCache: BitmapCache = BitmapCache(cacheSize)

class BitmapCache(maxSize: Int): LruCache<String, Bitmap>(maxSize) {

    fun getBitmapFromMemory(key: String): Bitmap? {
        return this.get(key)
    }

    fun setBitmapToMemory(key: String, bitmap: Bitmap) {
        if (getBitmapFromMemory(key) == null) {
            this.put(key, bitmap)
        }
    }
}
Reflection

Механизм исследования данных о программе во время её выполнения. Рефлексия позволяет исследовать информацию о полях, методах и конструкторах классов.

class Bank {
    private val money = "$300"
}

fun main() = runBlocking {
    val money = Bank::class
        .memberProperties
        .first { it.name == "money" }
        .apply { isAccessible = true }
        .get(Bank()) as String
    println(money)
}
Annotations
@RequiresApi

Указывает, что метод, класс или поле требует определенного уровня API (или выше) для использования. Помогает предотвратить ошибки на устройствах с более старыми версиями Android, где этот API недоступен.

@RequiresApi(21)
fun useLollipopFeature() {
    // Метод доступен на Android 5.0 (API 21) и выше
}

fun main() {
		if (Build.VERSION.SDK_INT >= 21) {
		    useLollipopFeature()  // Этот метод вызовется только на Android 5.0 (API 21) и выше
		}
}
@TargetApi

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

@TargetApi(26)
fun useOreoFeature() {
    // Этот код предназначен для Android 8.0 (API 26) и выше
    val notificationChannel = NotificationChannel("channel_id", "Channel", NotificationManager.IMPORTANCE_DEFAULT)
    // ...
}
@CheckSdkIntAtLeast

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

@CheckSdkIntAtLeast(29)
fun useNewApiFeature() {
    // Код, использующий API из Android 10 (API 29)
}
@CallSuper

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

open class BaseActivity: AppCompatActivity() {

    @CallSuper
    override fun onDestroy() {
        super.onDestroy()
        // Логика, которую необходимо выполнять всегда
        println("BaseActivity onDestroy called")
    }
}

class MainActivity: BaseActivity() {

    override fun onDestroy() {
        // Если забыть вызвать super.onDestroy(), аннотация @CallSuper предупредит об этом
        super.onDestroy()
        println("MainActivity onDestroy called")
    }
}
StorageStatsManager

Позволяет получать информацию об использовании хранилища приложением.

• Появился в Android 15 (API 35).

val storageStatsManager = ContextCompat.getSystemService(this, StorageStatsManager::class.java) as StorageStatsManager

lifecycleScope.launch(Dispatchers.IO) {
    // Запрашиваем статистику хранилища для текущего приложения по UID
    val storageStats = storageStatsManager.queryStatsForUid(
        StorageManager.UUID_DEFAULT, // UUID хранилища по умолчанию
        android.os.Process.myUid()  // UID текущего приложения
    )

    // Размер APK-файлов, занимаемых приложением
    val apkBytes = storageStats?.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_APK)

    // Размер DEX-оптимизированных файлов (артефактов оптимизации)
    val doBytes = storageStats?.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_DEXOPT_ARTIFACT)

    // Размер нативных библиотек, используемых приложением
    val libBytes = storageStats?.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_LIB)
}
Вопросы на собесе (43)
  • Основные компоненты (6)
    1. Основные компоненты приложения Android?

      Activity

      Service

      BroadcastReceiver

      ContentProvider

    1. Почему основные компоненты называются основными?

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

    1. Чем основные компоненты отличаются от других компонентов Android?

      Основные компоненты нужно объявлять в манифесте. Также без одного из запущенных компонентов не будет существовать процесс приложения.

    1. Какие из основных компонентов можно не указывать в манифесте?

      BroadcastReceiver

    1. Как вызывать основные компоненты?

      Activity через startActivity(Intent) или startActivityForResult().

      Service с помощью startService(Intent) или bindService().

      BroadcastReceiver через sendBroadcast(Intent) или зарегистрировать динамически/статически.

      ContentProvider через ContentResolver для выполнения CRUD-операций.

    1. Что из перечисленного является основными компонентами Android?

      Application

      Service

      Fragment

      Activity

  • Context (4)
    1. Что такое Context?

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

    1. Разница между Application Context и Activity Context?

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

    1. Какие есть виды Context?

      Application Context доступен на уровне всего приложения.

      Activity Context связан с конкретной Activity.

      Service Context используется в cервисах.

      BroadcastReceiver Context передается в методах onReceive.

    1. Какое ключевое слово используется для доступа к ресурсам приложения (например, строкам или изображениям) в коде? (

      R

      resources

      this

      getResources()

  • Bundle (6)
    1. Какие типы данных поддерживает Bundle?

      Boolean Byte Char CharSequence Double Float Int Long Short String

      String[] CharSequence[]

      Parcelable (включая массивы Parcelable[])

      Serializable

      Bundle (вложенные Bundle)

      ArrayList<T> (где T может быть из вышеперечисленных типов или Parcelable/Serializable объектов)

    1. Какое ограничение размера у Bundle?

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

    1. Почему в Bundle введено ограничение на 1 MB?

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

    1. Почему передавать данные между компонентами рекомендуется через Bundle?

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

    1. Как сохранить в Bundle сложные объекты? (картинки)

      Сохрани изображение в файловую систему (например, во временной директории) и храните путь к файлу в Bundle с помощью putString().

      Base64: преобразуй изображение в строку Base64 и сохраните её в Bundle с помощью putString(). (Увеличит размер данных).

    1. В крэшлитике увидели TransacionTooLargeException. Что будем делать?

      TransactionTooLargeException возникает при превышении лимитов данных в Intent. Решение: уменьшить объем данных, использовать ViewModel, SharedPreferences или базу данных для хранения.

  • Parcelable (5)
    1. Почему в Android рекомендуется использовать Parcelable?

      Быстрее будет работать в транзакциях между компонентами.

    1. В каких случаях не рекомендуется использовать Parcelable?

      Когда не хотим зависеть от Android-специфичных классов.

    1. Разница между Serializable и Parcelable?

      Serializable — это стандартный интерфейс Java, который медленнее, но проще в использовании, так как требует лишь реализации интерфейса Serializable.

      Parcelable — это Android-специфический интерфейс, который более эффективен и быстрый, но требует явного определения способов сериализации и десериализации данных.

    1. Что быстрее Serializable или Parcelable по умолчанию?

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

    1. Что быстрее Serializable или Parcelable по режиме ручной настройки?

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

  • Permissions (9)
    1. Для чего нужны разрешения в Android?

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

    1. Какие виды разрешений бывают в Android?

      Обычные разрешения (Normal permissions) — не представляют серьезной угрозы для конфиденциальности или безопасности пользователя. Пример: доступ к интернету.

      Опасные разрешения (Dangerous permissions) — могут потенциально повлиять на конфиденциальность пользователя и требуют явного согласия. Примеры: доступ к камере, геолокации или контактам.

    1. В чем разница между dangerous и normal разрешениями?

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

    1. Какие есть способы запросить разрешение в Android?

      ActivityCompat.requestPermissions.

      ContextCompat.checkSelfPermission.

      launchPermissionRequest в Compose.

    1. Можно ли отозвать полученное разрешение?

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

    1. Что значит статус show rationale у разрешения?

      Статус «show rationale» означает, что приложение может объяснить пользователю, почему ему необходимо разрешение, прежде чем запрашивать его. Это полезно, если пользователь ранее отказал в доступе.

    1. Как работать с локацией в Android?

      Для работы с локацией в Android используют API Google Play Services — FusedLocationProviderClient, который предоставляет упрощенные методы для получения местоположения устройства. Важно убедиться, что разрешения на местоположение предоставлены, и корректно обрабатывать отказ в доступе. Для получения локации в фоне можно использовать Foreground Service с типом location. Это обеспечивает непрерывное отслеживание локации, даже когда приложение не активно.

    1. Как организовать получение текущей геолокации каждые 10-15 секунд?

      Использовать FusedLocationProviderClient с периодическим запросом через requestLocationUpdates и интервалом 10-15 секунд.

    1. Какой из следующих классов используется для работы с разрешениями в Android?

      PermissionsHandler

      PermissionsManager

      ActivityCompat

      PermissionsCompat

  • Sensor (1)
    1. Как получить доступ к данным сенсора в Android-приложении?

      Использовать SensorManager.

  • Drawable (1)
    1. Какие типы Drawable существуют в Android?

      Drawable BitmapDrawable ShapeDrawable LayerDrawable StateListDrawable NinePatchDrawable VectorDrawable AnimationDrawable

  • Notifications (2)
    1. Как группировать уведомления в Android?

      Группировка уведомлений осуществляется с помощью NotificationCompat.Builder и метода setGroup(), который объединяет уведомления под одной группой. Метод setGroupSummary() позволяет создать сводное уведомление для отображения общего количества уведомлений в группе.

    1. Для чего нужны каналы в уведомлениях?

      Группировать уведомления: Уведомления с одинаковым каналом можно объединять, что упрощает восприятие.

      Настраивать звуки и приоритеты: Каждый канал может иметь свои настройки, такие как звук, вибрация и важность.

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

  • Annotations (1)
    1. Разница между @RequiresApi и @TargetApi?

      @RequiresApi проверяет минимальный API для метода/класса на уровне компиляции, @TargetApi подавляет предупреждения о несовместимости API в указанном блоке.

  • Другие (8)
    1. Как научить общаться между собой 2 Android-приложения?

      Intent. Приложение A может отправить запрос приложению B с помощью неявного Intent. Если приложение B настроено для обработки определенных Intent, оно сможет принимать эти запросы.

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

      Broadcasts: Приложение A может отправлять широковещательные сообщения (Broadcast), которые приложение B будет принимать и обрабатывать с помощью BroadcastReceiver.

      Bound Services: Одно приложение может подключаться к сервису другого приложения, используя интерфейсы AIDL (Android Interface Definition Language), что позволяет взаимодействовать между приложениями через IPC (межпроцессное взаимодействие).

    1. Что бы ты улучшил в OS Android?

      FindMyDevice

      AirDrop

      Общий буфер обмена

      Нативная реализация без JVM

    1. Как перехватить все Runtime-исключения в Android-приложении?

      Установить Thread.setDefaultUncaughtExceptionHandler в Application.onCreate

    1. Может ли приложение работать в рамках нескольких процессов?

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

    1. Какие преимущества дает работа в нескольких процессах?

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

    1. Какие есть способы сохранения состояния в Android?

      onSaveInstanceState сохраняет временное состояние активити при её уничтожении.

      ViewModel сохраняет данные при изменении конфигурации, например, при повороте экрана.

      SharedPreferences хранит простые данные (ключ-значение) между сессиями.

      DataStore современный способ хранения настроек и данных в виде ключ-значение.

      Room использует SQLite для хранения структурированных данных.

      Files: сохраняет данные в виде файлов во внутреннем или внешнем хранилище.

    1. Способы безопасного хранения данных в Android?

      EncryptedSharedPreferences для безопасного хранения паролей и конфиденциальной информации.

      SQLite с шифрованием: Используйте SQLCipher для шифрования баз данных SQLite.

      Файлы с шифрованием: Шифруйте файлы перед их сохранением в хранилище устройства, используя алгоритмы шифрования, такие как AES.

      Keystore храните криптографические ключи в Android Keystore для безопасного доступа и использования.

      ContentProvider для контроля доступа к данным и обеспечения их безопасности через разрешения.

    1. Какие есть виды диплинков?

      Стандартные диплинки: используют URL-схемы для открытия определённых активити в приложении (например, myapp://path/to/resource).

      Виртуальные диплинки (App Links): используют HTTP-URL и позволяют открывать приложение, а также обрабатывать ссылки в браузере, если приложение не установлено.

    1. Какой первый класс вызовется при запуске Android-приложения?

      Application