Irbisa · Шпаргалка

Основы Android

Activity, Fragment, жизненный цикл, Intent, Manifest, configuration changes

Junior-разработчик · 25 вопросов

  1. 01

    Какие четыре основных компонента есть у Android-приложения и почему Application к ним не относится?

    Activity, Service, BroadcastReceiver и ContentProvider.

  2. 02

    Расскажите о жизненном цикле Activity: почему setContentView() вызывают в onCreate() и когда onDestroy() приходит без onPause()/onStop()?

    onCreate → onStart → onResume выводят Activity на экран, onPause → onStop → onDestroy убирают её.

    class HomeActivity : ComponentActivity() {  override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    if (!session.isLoggedIn) {      startActivity(Intent(this, LoginActivity::class.java))      finish()   // next callback is onDestroy: no onStart, onResume, onPause or onStop…
  3. 03

    Что происходит с Activity при повороте экрана и где хранить состояние, чтобы оно пережило поворот: ViewModel, onSaveInstanceState или rememberSaveable?

    Поворот означает смену конфигурации: Activity уничтожается и создаётся заново с новыми ресурсами, поэтому её поля теряются.

    class SearchViewModel(private val state: SavedStateHandle) : ViewModel() {  // survives rotation (ViewModel) and process death (saved state)  val query: StateFlow<String> = state.getStateFlow("query", "")  fun onQueryChange(q: String) { state["query"] = q }}…
  4. 04

    Какие виды Context даёт Android и какой из них нельзя держать в долгоживущем объекте?

    Context даёт вам доступ к системе: ресурсы, настройки, запуск компонентов.

  5. 05

    Куда поместить код, который должен выполниться до появления любой Activity, и что там нельзя хранить?

    В Application.onCreate(): он выполняется один раз на процесс, на главном потоке, раньше любой Activity, Service или ресивера.

    @HiltAndroidAppclass ShopApp : Application() {  override fun onCreate() {    super.onCreate()    crashReporter.install(this)   // cheap, and useless if it starts late    ProcessLifecycleOwner.get().lifecycle.addObserver(AppForegroundObserver)…
  6. 06

    Что входит в AndroidManifest.xml и как манифесты ваших библиотек попадают в итоговый манифест приложения?

    Манифест сообщает системе, что содержит приложение, ещё до запуска его кода: компоненты, интенты, которые они принимают, нужные разрешения и железо.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">   <uses-permission android:name="android.permission.CAMERA" />  <uses-feature android:name="android.hardware.camera" android:required="false" />…
  7. 07

    За что отвечают minSdk, targetSdk и compileSdk и что на самом деле меняется, когда вы поднимаете targetSdk?

    compileSdk определяет, с какими API можно компилироваться, minSdk задаёт самую старую версию Android, на которую ставится приложение, а targetSdk определяет, какие изменения поведения платформы система к вам применяет.

    // app/build.gradle.ktsandroid {  compileSdk = 36            // APIs you can compile against  defaultConfig {    minSdk = 26              // oldest device that can install    targetSdk = 36           // behaviour changes you have tested for…
  8. 08

    Вы добавили res/values-ru/strings.xml и res/drawable-xxhdpi/. Как Android решает, что из этого получит конкретное устройство?

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

  9. 09

    Что такое Intent: явный и неявный, как система находит обработчик неявного и как получить результат обратно?

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

    // Explicit: a screen in your own appstartActivity(Intent(this, DetailActivity::class.java).putExtra("id", 42L)) // Implicit: any app that can view a web pagetry {  startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")))…
  10. 10

    Что такое Fragment, как его жизненный цикл связан с Activity-хостом и зачем ему отдельный viewLifecycleOwner?

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

    class ProfileFragment : Fragment(R.layout.fragment_profile) {  private val viewModel: ProfileViewModel by viewModels()   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {    super.onViewCreated(view, savedInstanceState)    val name = view.findViewById<TextView>(R.id.name)…
  11. 11

    Этот Fragment после поворота падает с «could not find Fragment constructor». Что не так и как правильно передавать данные во Fragment?

    class ProfileFragment(private val userId: String) : Fragment(R.layout.fragment_profile) {  private val viewModel: ProfileViewModel by viewModels()   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {    super.onViewCreated(view, savedInstanceState)    viewModel.load(userId)  }} supportFragmentManager.commit {  replace(R.id.container, ProfileFragment("42"))}

    FragmentManager сам пересоздаёт фрагменты после смены конфигурации или смерти процесса и умеет вызывать только пустой конструктор.

  12. 12

    Как работают runtime-разрешения в Android и что такое уровни защиты normal, dangerous и signature?

    У каждого разрешения есть уровень защиты.

    class ScannerActivity : ComponentActivity() {  private val cameraPermission = registerForActivityResult(    ActivityResultContracts.RequestPermission()  ) { granted -> if (granted) openCamera() else showCameraUnavailable() }   fun onScanClick() {…
  13. 13

    Какие события вызывают onPause() без onStop() и что диалог, multi-window и picture-in-picture делают с жизненным циклом Activity?

    onPause означает, что Activity потеряла фокус, но может оставаться видимой; onStop приходит, только когда она полностью скрыта.

  14. 14

    Почему объекты между экранами передают как Parcelable, а не Serializable, и почему большой Bundle может упасть с TransactionTooLargeException?

    Extras и сохранённое состояние сплющиваются в Parcel и уходят через Binder; Parcelable даёт явный и быстрый формат, Serializable опирается на рефлексию и медленнее.

    // build.gradle.kts: plugins { id("kotlin-parcelize") }@Parcelizedata class Address(val city: String, val street: String, val zip: String) : Parcelable startActivity(  Intent(this, CheckoutActivity::class.java).putExtra(EXTRA_ADDRESS, address)…
  15. 15

    Что такое смерть процесса, как её воспроизвести и какое состояние её переживает?

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

    # 1. Open the screen, fill in the form, press Homeadb shell am kill com.example.shop # 2. The process is gone (prints nothing)adb shell pidof com.example.shop…
  16. 16

    Как два Fragment обмениваются данными (Fragment Result API, общая ViewModel или интерфейс) и когда что выбирать?

    Разовый результат передают через Fragment Result API (setFragmentResult / setFragmentResultListener); общее, постоянно меняющееся состояние передают через ViewModel, привязанную к их общему владельцу.

    // Picker: sends a one-shot resultclass SortPickerFragment : DialogFragment() {  private fun onPicked(sort: String) {    setFragmentResult("sort_request", bundleOf("sort" to sort))    dismiss()  }…
  17. 17

    Чем отличаются add() и replace() в FragmentTransaction, что делает addToBackStack() и когда нужен childFragmentManager?

    replace() удаляет фрагменты, которые сейчас в контейнере, и добавляет новый; add() кладёт новый сверху, оставляя старый живым вместе с его вью.

    supportFragmentManager.commit {  setReorderingAllowed(true)  replace(R.id.container, OrderDetailFragment.newInstance(orderId))  addToBackStack("detail")      // Back returns to the list instead of closing the app}…
  18. 18

    Что делают commit(), commitNow() и commitAllowingStateLoss() и почему коммит падает с «Can not perform this action after onSaveInstanceState»?

    commit() планирует транзакцию на главном потоке, commitNow() выполняет её синхронно, а commitAllowingStateLoss() пропускает проверку сохранённого состояния.

    // wrong: the result can arrive while the app is stoppedviewModel.result.observeForever { result ->  parentFragmentManager.commit { replace(R.id.container, ResultFragment.newInstance(result)) }} // right: events are delivered only while the view is at least STARTED…
  19. 19

    Что меняют режимы запуска standard, singleTop, singleTask и singleInstance и как при стеке A-B-C-D вернуться к A?

    Режим запуска решает, создаёт ли запуск Activity новый экземпляр или переиспользует существующий.

    // A-B-C-D: return to the existing A, destroying B, C and DstartActivity(Intent(this, HomeActivity::class.java).addFlags(  Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)) class HomeActivity : ComponentActivity() {…
  20. 20

    Что такое BroadcastReceiver, чем отличаются ресиверы из манифеста и зарегистрированные в коде и что для них изменилось в Android 8 и Android 14?

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

    class BootReceiver : BroadcastReceiver() {  override fun onReceive(context: Context, intent: Intent) {    // main thread, and the process may die right after this returns    WorkManager.getInstance(context).enqueueUniqueWork(      "resync", ExistingWorkPolicy.KEEP, OneTimeWorkRequestBuilder<SyncWorker>().build()    )…
  21. 21

    Что такое ContentProvider, когда он действительно нужен и как FileProvider позволяет другому приложению открыть ваш файл?

    ContentProvider отдаёт данные через URI content:// с интерфейсом query/insert/update/delete, а система проверяет доступ к нему по разрешениям.

    // Manifest: FileProvider with authorities=${applicationId}.fileprovider, exported=false,// grantUriPermissions=true and meta-data FILE_PROVIDER_PATHS -> @xml/file_paths val uri = FileProvider.getUriForFile(  context, "${context.packageName}.fileprovider", pdfFile)…
  22. 22

    Этот код падает, когда приходит ответ. Почему и как Looper, Handler и MessageQueue возвращают работу на главный поток?

    class ProfileActivity : AppCompatActivity() {  override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_profile)    val nameView = findViewById<TextView>(R.id.name)     Thread {      val user = api.loadUserBlocking()      nameView.text = user.name    }.start()  }}

    Код трогает вью из фонового потока, а это CalledFromWrongThreadException: менять иерархию вью может только поток, который её создал, то есть главный.

  23. 23

    Найдите баг: после ухода со сплэш-экрана приложение всё равно через две секунды открывает главный экран.

    class SplashActivity : AppCompatActivity() {  private val handler = Handler(Looper.getMainLooper())   override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_splash)    handler.postDelayed({      startActivity(Intent(this, MainActivity::class.java))      finish()    }, 2_000)  }}

    Отложенный Runnable никогда не отменяется.

  24. 24

    Как на самом деле выполняется ваш Kotlin-код на Android: что такое DEX, что пришло на смену Dalvik и как JIT и AOT работают вместе в ART?

    Компилятор Kotlin выдаёт JVM-байткод, D8 (или R8 в релизных сборках) превращает его в DEX-байткод, а исполняет его Android Runtime (ART).

  25. 25

    Может ли одно приложение работать в нескольких процессах и как процессы общаются через Binder, AIDL и bound-сервисы?

    Да: android:process у компонента запускает его в отдельном процессе со своей памятью, своим экземпляром Application и своими синглтонами.

    // IOrderService.aidl:  interface IOrderService { int getCount(); } class OrderService : Service() {  private val binder = object : IOrderService.Stub() {    // runs on a Binder pool thread, so it must be thread-safe    override fun getCount() = repo.count()…