├── .gitignore ├── QuestionsPool_eng.md ├── QuestionsPool_ru.md ├── README.md ├── out └── production │ └── Android Notes │ ├── META-INF │ └── Android Notes.kotlin_module │ ├── common_android │ └── eng │ │ ├── CommonAndroid_eng.md │ │ ├── IPCmessenger.png │ │ ├── app_launch.png │ │ ├── arch_kernel_level.png │ │ ├── async_work.png │ │ ├── binder_framework.png │ │ ├── content_provider.png │ │ ├── process_fork.png │ │ ├── process_fork_2.png │ │ ├── process_lifecycle.png │ │ ├── service_lifecycle.png │ │ └── viewLifecycle.png │ ├── compose │ ├── eng │ │ ├── ComposeTheory_eng.md │ │ ├── composition.png │ │ └── state_hoisting.png │ └── ru │ │ ├── ComposeTheory_ru.md │ │ ├── composition.png │ │ ├── frame_phases.png │ │ ├── layout.png │ │ ├── measurement_order.png │ │ └── state_hoisting.png │ ├── coroutines │ ├── eng │ │ ├── CoroutinesPracticeAnswers_eng.md │ │ ├── CoroutinesTheory_eng.md │ │ └── cancellation.png │ └── ru │ │ ├── CoroutinesPracticeAnswers_ru.md │ │ └── CoroutinesTheory_ru.md │ ├── di │ └── ru │ │ └── DI_ru.md │ ├── gradle │ └── ru │ │ ├── GradleAnswer_ru.md │ │ └── Gradle_ru.md │ ├── ktl │ └── KotlinTheory_ru.md │ ├── multithreading │ └── ru │ │ ├── Multithreading_ru.md │ │ ├── handler.png │ │ └── handler_android.png │ └── oop │ └── ru │ ├── OOPPracticeAnswers_ru.md │ └── OOPTheory_ru.md └── src ├── common_android ├── CommonAndroid_eng.md ├── CommonAndroid_ru.md ├── draft │ └── eng │ │ ├── CommonAndroid_eng.md │ │ ├── IPCmessenger.png │ │ ├── app_launch.png │ │ ├── arch_kernel_level.png │ │ ├── async_work.png │ │ ├── binder_framework.png │ │ ├── content_provider.png │ │ ├── doze_mode_maintaince_windows.png │ │ ├── process_fork.png │ │ ├── process_fork_2.png │ │ ├── process_lifecycle.png │ │ ├── service_lifecycle.png │ │ └── viewLifecycle.png └── img │ ├── AndroidSoftwareStack.png │ ├── cold_app_launch.png │ ├── control_group.png │ ├── data_api_decision_tree.jpeg │ ├── handler.png │ ├── handler_android.png │ ├── jvm_vs_dvm.png │ ├── process_fork_2.png │ ├── process_vs_thread.png │ ├── process_vs_thread_2.png │ ├── sandbox.png │ ├── startup_types.png │ └── thread_perfomance.png ├── compose ├── eng │ ├── ComposeTheory_eng.md │ ├── composition.png │ └── state_hoisting.png └── ru │ ├── ComposeTheory_ru.md │ ├── composition.png │ ├── frame_phases.png │ ├── layout.png │ ├── measurement_order.png │ └── state_hoisting.png ├── coroutines ├── CoroutinesPracticeQuestions.kt ├── eng │ ├── CoroutinesPracticeAnswers_eng.md │ ├── CoroutinesTheory_eng.md │ └── cancellation.png └── ru │ ├── CoroutinesPracticeAnswers_ru.md │ └── CoroutinesTheory_ru.md ├── di └── ru │ └── DI_ru.md ├── gradle └── ru │ ├── GradleAnswer_ru.md │ └── Gradle_ru.md ├── ktl ├── KotlinPractice.kt ├── KotlinTheory_eng.md └── KotlinTheory_ru.md ├── multithreading └── ru │ └── MultithreadingPractice.kt ├── oop └── ru │ ├── OOPPracticeAnswers_ru.md │ ├── OOPPracticeQuestions.java │ └── OOPTheory_ru.md └── utils └── utils.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Local configuration file (sdk path, etc) 26 | local.properties 27 | 28 | # IntellijJ IDEA 29 | *.iml 30 | *.ipr 31 | *.iws 32 | .idea/** 33 | classes/** 34 | target/** 35 | 36 | # External native build folder generated in Android Studio 2.2 and later 37 | .externalNativeBuild 38 | 39 | # Mac OS 40 | .DS_Store 41 | 42 | # Gradle 43 | gradle-app.setting 44 | .gradletasknamecache 45 | .gradle/** 46 | build/** 47 | /captures 48 | 49 | # С++ files 50 | .cxx 51 | /cert/release.keystore 52 | -------------------------------------------------------------------------------- /QuestionsPool_eng.md: -------------------------------------------------------------------------------- 1 | # Android-Notes 2 | 3 | ### [Common](src/common_android/CommonAndroid_eng.md) 4 | 5 | 1) What is Android Software Stack. What are the major components of the Android platform? 6 | 2) Why Android OS uses DVM instead of JVM? Why Android depreciated DVM and started to use ART? 7 | 3) What is App Sandbox? 8 | 4) Android build process 9 | 5) Inter process communication (IPC) 10 | 6) How is the application launched? 11 | 7) What is process ranking? 12 | 8) How does a garbage collector work? Which garbage collector used in Android? 13 | 9) Does increasing threads increase performance? 14 | 10) What is the difference between _process_ and _thread_? 15 | 11) What does thread scheduling depend on? 16 | 12) What are Handler, Looper, MessageQueue for? 17 | 18 | 19 | ### [Coroutines Theory](src/coroutines/eng/CoroutinesTheory_eng.md) 20 | 21 | 1) Difference between a **coroutine** and a **thread**? 22 | 2) What are the main components of coroutines? Describe their role 23 | 3) How the main components of coroutines depend on each other 24 | 4) Coroutine exceptions handling 25 | 5) Why is it forbidden to catch *CancellationException*? 26 | 6) What is suspension point? How the coroutines state machine divides code? 27 | 7) How continuation.resume() set parameters to next continuation? TODO 28 | 8) Difference between **async** and **launch** 29 | 9) Job types 30 | 10) *Join*, *JoinAll*, *Await*, *AwaitAll* 31 | 11) What is **CoroutineStart**? Which types do you know? 32 | 12) How to cancel coroutine? What is ensureActive? 33 | 13) How to put custom data to *CoroutineContext*? 34 | ##### [CoroutinesPracticeQuestions](src/coroutines/CoroutinesPracticeQuestions.kt) 35 | ##### [CoroutinesPracticeAnswers](src/coroutines/eng/CoroutinesPracticeAnswers_eng.md) 36 | 37 | 38 | ### [Compose](src/compose/ru/ComposeTheory_ru.md) 39 | 1) What is recomposition? 40 | 2) *remember*, *remember(key)*, *rememberSaveable*, *remember{derivedStateOf()}* difference 41 | 3) What is **SideEffect** for? 42 | 4) Effect types 43 | 5) State types 44 | 6) What is the **@Stable** annotation for? 45 | 46 | ### [Kotlin](src/compose/ru/ComposeTheory_ru.md) 47 | 1) Is it possible to inherit from data class? Why? 48 | 2) What is *inline/noinline/crossinline*? What are the benefits of using? Why aren't they used all the time? 49 | 3) When can't we use *inline*? What is a non-local return? 50 | 4) What is *reified*? What is the advantage of using with inline? 51 | 5) How many parameters in a constructor can an *inline class* have? Why? 52 | 6) Contravariance, covariance, invariance 53 | 7) Difference between Nothing, Unit and Any 54 | 8) What are *delegates*? 55 | 9) What does _typealias_ compile to? 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /QuestionsPool_ru.md: -------------------------------------------------------------------------------- 1 | # Android-Notes (in progress) 2 | 3 | 4 | ### Android 5 | 1) Что такое программный стек Android (Android Software Stack). Каковы основные компоненты архитектуры платформы Android? 6 | 2) Почему создали Dalvik взамен JVM? Почему отказались от DVM в пользу ART? 7 | 3) Что такое песочница (App Sandbox)? 8 | 4) Процесс сборки приложения 9 | 5) Межпроцессонрная коммуникация (Inter Process Communication - IPC) 10 | 6) Как происходит процесс запуска приложения? 11 | 7) Что такое приоритеты процессов (process ranking)? 12 | 8) Как работает Garbage collector? Какой GC использует Android? 13 | 9) Увеличивает ли количество потоков производительность? 14 | 10) Разница между процессом и потоком 15 | 11) От чего зависит планирование потока планировщиком? 16 | 12) Для чего нужны Handler, Looper, MessageQueue? 17 | 18 | [//]: # (1) Лимит на размер bundle?) 19 | 20 | [//]: # (2) Что такое Binder транзакция?) 21 | 22 | [//]: # (3) Как данные могут передаваться в обход Binder) 23 | 24 | [//]: # (4) Какой процессор использует Android?) 25 | 26 | [//]: # (5) Зачем нужен Dalvik/Art вместо JVM?) 27 | 28 | [//]: # (6) Разница между Dalvik и Art) 29 | 30 | [//]: # (7) Самая ранняя точка входа в приложение?) 31 | 32 | [//]: # (8) Отличия контекстов) 33 | 34 | [//]: # (9) Приоритеты процессов) 35 | 36 | [//]: # (10) Мы обновили приложение, хранили Serializable и Parcelable. Добавили новое поле, как поддержать изменение?) 37 | 38 | [//]: # (11) Жизненный цикл view. Когда при invalidate() не вызовется onDraw(). Всегда ли отработает requestLayout()?) 39 | 40 | [//]: # (12) Когда луче использовать svg, png, webp) 41 | 42 | [//]: # (13) Различия в работе glide, picasso, koil) 43 | 44 | [//]: # (14) Отличие LongPolling от WebSocket) 45 | 46 | [//]: # (15) Как андроид под капотом отрисовывает интерфейс?) 47 | 48 | [//]: # (16) Как запретить активити уничтожаться при повороте экрана?) 49 | 50 | [//]: # (17) Разница между low memory killer и out of memory killer?) 51 | 52 | [//]: # (18) Расскажите про версии garbage collector в Android) 53 | 54 | [//]: # (19) Как происходит запуск приложения) 55 | 56 | [//]: # (20) Что такое Zygote?) 57 | 58 | [//]: # (21) Разница между targetSDK и compileSdk) 59 | 60 | [//]: # (22) Как происходит компиляция приложения) 61 | 62 | [//]: # (23) Что такое процесс в Android) 63 | 64 | [//]: # (25) Может ли BroadcastReceiver быть запущен без объявления в манифесте?) 65 | 66 | [//]: # (26) Виды сервисов) 67 | 68 | [//]: # (27) Отличие IntentService, Service, JobIntentService, JobService) 69 | 70 | [//]: # (28) За что отвечают Content resolver и Content Provider) 71 | 72 | [//]: # (29) Что такое PendingIntent?) 73 | 74 | [//]: # (30) Если создать два Pending Intent отличные только по данным помещенным в data, с какой ошибкой можно столкнуться?) 75 | 76 | [//]: # (31) Когда можно сохранять state чтобы гарантированно восстановить его даже в случае если андроид убьёт приложение?) 77 | 78 | [//]: # (32) Какие launch mode существуют?) 79 | 80 | ##### [Ответы Android](src/common_android/CommonAndroid_ru.md) 81 | 82 | 83 | ### Kotlin 84 | 1) Разница между *class* и *data class*, *data object* и *object* 85 | 2) Способы реализовать функциональный тип 86 | 3) Разница между *0 until 10*, *0..10* and *0..<10* 87 | 4) Что такое inline/noinline/crossinline? Какие плюсы от использования? Почему не использовать их постоянно? Когда мы не можем использовать inline? Что такое non-local-return? 88 | 5) Что такое reified? В чем плюс использования с inline? 89 | 6) Сколько параметров в конструкторе может иметь inline class? Почему? 90 | 7) Контравариантность, ковариантность, инвариантность 91 | 8) Разница между Nothing, Unit и Any 92 | 9) Можно ли наследоваться от data class? Почему? 93 | ##### [Ответы по теории Kotlin](src/ktl/KotlinTheory_ru.md) 94 | 95 | 96 | ### Coroutines 97 | 98 | 1) Разница между корутинами тредами 99 | 2) Какие основные составляющие компоненты корутин вы знаете. Опишите их роль 100 | 3) Как основные компоненты корутин зависят друг от друга 101 | 4) Расскажите про обработку исключений 102 | 5) Что такое suspension points? 103 | 6) Разница между async и launch 104 | 7) Виды Job 105 | 8) Join, JoinAll, Await, AwaitAll 106 | 9) В чем разница между deferreds.map { it.await() } and deferreds.awaitAll() 107 | 10) Что такое CoroutineStart? Какие типы бывают? 108 | 11) Что такое ensureActive? 109 | 12) Как поместить дополнительные данные в CoroutineContext? 110 | 13) Напишите код который приведет к deadlock 111 | 14) Как отменяются скоупы при выбросе ошибки в дочернем скоупе? 112 | 15) Что такое Flow? Когда мы должны его использовать? 113 | 16) Что такое CoroutineDispatcher? В каких случаях какой использовать? 114 | 115 | ##### [Ответы по теории Coroutines](src/coroutines/ru/CoroutinesTheory_ru.md) 116 | ##### [Вопросы по практике Coroutines](src/coroutines/CoroutinesPracticeQuestions.kt) 117 | ##### [Ответы по практике Coroutines](src/coroutines/eng/CoroutinesPracticeAnswers_eng.md) 118 | 119 | ### Compose 120 | 1) Что такое рекомпозиция 121 | 2) remember, remember(key), rememberSaveable, remember { derivedStateOf() } различия 122 | 3) Что такое Side-Effect? 123 | 4) Какие виды Side-Effect бывают? 124 | 5) Типы state 125 | 6) Что такое Snapshot Policy? 126 | 7) Переиспользует ли LazyColumn элементы по аналогии с RecyclerView? 127 | 8) Сохранит ли _by remember {}_ свое значение при повороте экрана? 128 | 9) Что значит поднятие состояния (state hoisting)? 129 | 10) Способы сохранения состояния при смене конфигурации 130 | 11) Жизненный цикл composable 131 | 12) Можно ли передавать viewModel в дочерние composable функции? 132 | 13) Как избежать вызова рекомпозиции всех элементов списка при добавлении одного нового элемента? 133 | 14) За что отвечает аннотация @Stable 134 | 15) Как добавить отступы между элементами списка? 135 | 16) Когда мы работаем с ViewGroup, большая вложенность элементов приводит к частому измерению размеров, и уменьшает производительность. Сохраняется ли такая проблема в Compose? 136 | 17) Можно ли изменить количество измерений размеров для компоуз элементов? 137 | 18) Как создать свой Layout (ViewGroup)? 138 | 19) Что такое CompositionLocal? 139 | 20) compositionLocalOf vs staticCompositionLocalOf 140 | 21) Три фазы создания Composable UI 141 | ##### [Ответы по теории Compose](src/compose/ru/ComposeTheory_ru.md) 142 | 143 | 144 | ### DI 145 | 1) Dagger/Hilt vs Koin 146 | 2) ServiceLocator vs DI 147 | 3) Основные компоненты в DI 148 | ###### Dagger 149 | 1) Аннотации в Dagger 150 | 2) Как работает создаение Scope компонента под капотом? 151 | 3) Почему Hilt не стоит использовать для многомодульности 152 | 4) Lazy vs Scope? 153 | 5) В чем минус Subcomponent? Как разделить логику компонента без использования subcomponent? 154 | 155 | ##### [Ответы DI](src/di/ru/DI_ru.md) 156 | 157 | *** 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-Notes (in progress) 2 | 3 | [ENG](#eng) - [RU](#ru) 4 | 5 | ## ENG 6 | 7 | Full questions list for Coroutines, Compose, Common Android topics 8 | 9 | #### [Full questions Pool](QuestionsPool_eng.md) 10 | 11 | Android 12 | - [Android theory](src/common_android/CommonAndroid_eng.md) 13 | 14 | Coroutines 15 | - [Coroutines theory](src/coroutines/eng/CoroutinesTheory_eng.md) 16 | - [Coroutines practice questions](src/coroutines/CoroutinesPracticeQuestions.kt) 17 | - [Coroutines practice answers](src/coroutines/eng/CoroutinesPracticeAnswers_eng.md) 18 | 19 | Compose 20 | - [Compose theory](src/compose/ru/ComposeTheory_ru.md) 21 | 22 | Kotlin 23 | - [Kotlin theory](src/ktl/KotlinTheory_eng.md) 24 | 25 | 26 | 27 | ## RU 28 | Список вопросов по темам о разработке на андроид, корутинам и compose. 29 | 30 | #### [Список вопросов](QuestionsPool_ru.md) 31 | 32 | Android 33 | - [Android theory](src/common_android/CommonAndroid_ru.md) 34 | 35 | 36 | Coroutines 37 | - [Coroutines theory](src/coroutines/ru/CoroutinesTheory_ru.md) 38 | - [Coroutines practice questions](src/coroutines/CoroutinesPracticeQuestions.kt) 39 | - [Coroutines practice answers](src/coroutines/ru/CoroutinesPracticeAnswers_ru.md) 40 | 41 | 42 | Compose 43 | - [Compose theory](src/compose/ru/ComposeTheory_ru.md) 44 | 45 | Kotlin 46 | - [Kotlin theory](src/ktl/KotlinTheory_ru.md) 47 | 48 | 49 | 50 | **** 51 | -------------------------------------------------------------------------------- /out/production/Android Notes/META-INF/Android Notes.kotlin_module: -------------------------------------------------------------------------------- 1 |  2 | + 3 | 4 | coroutinesCoroutinesPracticeQuestionsKt 5 |  6 | ktlKotlinPracticeKt 7 | - 8 | multithreading.ruMultithreadingPracticeKt"* -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/CommonAndroid_eng.md: -------------------------------------------------------------------------------- 1 | #### Is there a limit on the size of the bundle? 2 | 3 | When used to pass information between Android components the bundle is serialized into a binder transaction. The Binder 4 | transaction buffer has a limited fixed size, currently 1MB, which is shared by all transactions in progress for the 5 | process. Since this limit is at the process level rather than at the per activity level, these transactions include all 6 | binder transactions in the app such as onSaveInstanceState, startActivity and any interaction with the system. When the 7 | size limit is exceeded, a TransactionTooLargeException is thrown. 8 | 9 | For the specific case of savedInstanceState, the amount of data should be kept small because the system process needs to 10 | hold on to the provided data for as long as the user can ever navigate back to that activity 11 | (even if the activity's process is killed). Recommended size less than 50k. 12 | 13 | *** 14 | 15 | #### Способы общения между процессами 16 | 17 | 1) Intent + Bundle 18 | 2) ContentProvider 19 | 3) Messenger 20 | 21 | Messenger - клиент серверное взаимодействие. Процесс-клиент посылает данные на процесс-сервер. 22 | Работает с bindServices которые живут в другом процессе. 23 | ![img.png](IPCmessenger.png) 24 | 25 | 4) Socket-NDK 26 | 27 | Все они под капотом используют Binder 28 | ![img.png](arch_kernel_level.png) 29 | *** 30 | 31 | 32 | #### What is the Binder transaction? 33 | 34 | Binder is the main IPC/RPC (Inter-Process Communication) system in Android. It allows applications to communicate with 35 | each other, and it is the base of several important mechanisms in the Android environment. For instance, Android 36 | services are built on top of Binder. Message exchanged with Binder are called binder transactions, they can transport 37 | simple data such as integers but also process more complex structures like file descriptors, memory buffers or 38 | weak/strong references on objects. 39 | ![img.png](binder_framework.png) 40 | [Link](https://boosty.to/androidbroadcast/posts/904c5065-6c7e-4f75-b8e6-20cb8a607170) 41 | 42 | #### What processor does Android use? 43 | 44 | [Link](https://habr.com/ru/post/140459/) 45 | 46 | *** 47 | 48 | #### Why we don't use JVM on Android? 49 | 50 | [Link](https://towardsdatascience.com/jvm-vs-dvm-b257229d18a2) 51 | [Link2](https://www.youtube.com/watch?v=duO5qgn2DO8) 52 | 53 | 54 | *** 55 | 56 | #### Как вырезать фигуру из вьюшки 57 | 58 | *** 59 | 60 | #### What is *Zygote* ? 61 | 62 | [//]: # (Чтобы разместить все необходимое в оперативной памяти, Android пытается разделить страницы оперативной памяти между ) 63 | 64 | [//]: # (процессами. Это можно сделать следующими способами:) 65 | 66 | [//]: # () 67 | [//]: # ([TODO](https://developer.android.com/topic/performance/memory-overview)) 68 | 69 | Каждое приложение запускается в отдельном процессе. Это сделано для того, чтобы процессы не могли иметь доступ к памяти друг друга, и следовательно, 70 | никто не сможет получить доступ к нашим данным. 71 | 72 | На самом раннем этапе загрузки ОС Linux, а именно, в момент загрузки ядра создается самый первый процесс — 73 | swapper или sched (процесс имеющий Process ID = 0). 74 | 75 | Сами процессы в ходе своей жизни могут пребывать в следующих состояниях: 76 | 1) состояние выполнения (running) 77 | 2) состояние сна (uninterruptible/interruptible sleep) 78 | 3) состояние остановки выполнения (stopped) 79 | 4) zombie-состояние 80 | ![img.png](process_lifecycle.png) 81 | 82 | При этом в процессе выполнения каждый процесс может создавать новые процессы (child process), 83 | по отношению к которым он будет предком-родителем, через fork/exec 84 | ![img.png](process_fork_2.png) 85 | 86 | 87 | Zygote - специальный системный процесс, от которого мы запускаем процессы своего прложения. 88 | Zygote уже содержит все необходимое для функционирования нашего приложения. 89 | Через fork копируем родительский процесс, с отличным UID (User identifier). 90 | ![img.png](process_fork.png) 91 | 92 | [Link](https://www.okbsapr.ru/library/publications/kanner_2015_3/) 93 | 94 | *** 95 | 96 | #### Difference between *Dalvik* and *ART* ? What is Profile-Guided Compilation? 97 | 98 | Dalvik - JIT 99 | 100 | ART- AOT 101 | 102 | JIT - takes less RAM, but runtime is much slower 103 | 104 | AOT - takes a lot of RAM, but runtime works is 20 time more efficient 105 | 106 | Profile-Guided Compilation - JIT, but if application is frequently uses AOT 107 | 108 | 109 | [Link](https://www.youtube.com/watch?v=0J1bm585UCc) 110 | 111 | 112 | *** 113 | 114 | #### Самая ранняя точка входа в приложение? 115 | 116 | Content provider ?? 117 | 118 | *** 119 | 120 | #### Что такое Context? Чем они отличаются друг от друга? 121 | [Link](https://www.fandroid.info/context-kontekst-v-android-chto-eto-kak-poluchit-i-zachem-ispolzovat/) 122 | 123 | *** 124 | 125 | #### Приоритеты процессов 126 | 127 | -Foreground process 128 | Visible Process 129 | Service Process 130 | Background process 131 | Empty Process 132 | 133 | 1.Процесс с которым взаимодействует пользователь(Foreground process) 134 | К таким процессам относится например: активити с которым взаимодействует пользовать; 135 | сервис(экземпляр Service), с которым взаимодействует пользователь; сервис запущенный методом startForeground(); 136 | сервис, который выполняет один из методов своего жизненного цикла; 137 | BroadcastReceiver который выполняет метод onReceive(). 138 | 139 | 2.Видимый процесс 140 | Процесс, в котором не выполнены условия из пункта №1, но который влияет на то, что пользователь видит на экране. 141 | К примеру, вызван метод onPause() активити. 142 | 143 | 3.Сервисный процесс 144 | Служба запущенная методом startService() 145 | 146 | 4.Фоновый процесс 147 | Процесс выполняемый в фоновом режиме, который не виден пользователю. 148 | 149 | 5.Пустой процесс 150 | 151 | *** 152 | 153 | #### Мы обновили приложение, хранили Serializable и Parcelable. Добавили новое поле, как поддержать изменение? 154 | 155 | Parcelable: переопределить writeToParcel 156 | 157 | Serializable: переопределить serialVersionUID -> Позволит выбросить ошибку 158 | 159 | Можно сделать ручную сериализацию: 160 | Для Serializable : writeObject - readObject. Можно использовать Externalizable - принцип тот же что и у Parcelable, 161 | работает значительно быстрее чем Serializable, но медленее чем Parcelable. 162 | 163 | [Link](https://www.youtube.com/watch?v=tko54cjc79U) 164 | 165 | *** 166 | 167 | #### Жизненный цикл view. Когда при invalidate() не вызовется onDraw(). Всегда ли отработает requestLayout()? 168 | 169 | ![img.png](viewLifecycle.png) 170 | // todo 171 | 172 | *** 173 | 174 | #### Разница между commit и commitAllowStateLoss 175 | 176 | *** 177 | 178 | #### Как реализовать viewModel с нуля? Как не создать ее дважды? Когда создавать, когда уничтожать? 179 | 180 | *** 181 | 182 | #### Как реализовать mvi? 183 | 184 | *** 185 | 186 | #### Когда луче использовать svg, png, webp 187 | 188 | если больше 200*200 - svg проседает в отрисовке раз в 10. при конвентрировани png в webp мы почти не теряем в качестве, 189 | но размер сильно уменьшается 190 | 191 | *** 192 | 193 | #### Различия в работе glide, picasso, koil 194 | 195 | *** 196 | 197 | #### Для чего нужны PrecomputedTextView, SpannableTextView 198 | 199 | *** 200 | 201 | #### Для чего нужен recycler view pool 202 | 203 | RecycledViewPool lets you share Views between multiple RecyclerViews. If you want to recycle views across RecyclerViews, 204 | create an instance of RecycledViewPool and use setRecycledViewPool. RecyclerView automatically creates a pool for itself 205 | if you don't provide one. 206 | 207 | *** 208 | 209 | #### Отличие LongPolling от WebSocket 210 | 211 | [Link](https://ably.com/blog/websockets-vs-long-polling#:~:text=Long%20polling%20is%20more%20resource,hops%20between%20servers%20and%20devices.) 212 | 213 | 214 | *** 215 | 216 | #### Как реализовать кэширование? 217 | 218 | *** 219 | 220 | #### Как андроид под капотом отрисовывает интерфейс? 221 | 222 | *** 223 | 224 | #### Как запретить активити уничтожаться при повороте экрана? 225 | 226 | configChanges 227 | 228 | ``` 229 | 233 | ``` 234 | 235 | Этот флаг сообщает платформе Android, что вы собираетесь вручную обрабатывать изменения ориентации, размера экрана и 236 | внешнего вида/исчезновения клавиатуры для этой активити. Таким образом, вместо того, чтобы уничтожать и воссоздавать 237 | вашу активити, Android просто повернет экран и вызовет один из методов жизненного цикла: onConfigurationChanged. Если у 238 | вас есть фрагмент, прикрепленный к этой активити, он также получит вызов своего метода onConfigurationChanged. Это 239 | означает, что будут использоваться одни и те же экземпляры ваших активити и фрагментов, а ваши переменные останутся 240 | нетронутыми. 241 | 242 | *** 243 | 244 | #### Разница между targetSDK и compileSdk 245 | 246 | *compileSdk* - версия API, для которой скомпилировано приложение. Eсли вы попытаетесь использовать функции API 16, но 247 | установите значение 15, вы получите ошибку компиляции. Если вы установите API 16, вы все равно сможете запускать 248 | приложение на устройстве с API 15, если пути выполнения вашего приложения не пытаются вызывать какие-либо API, 249 | специфичные для API 16. 250 | 251 | *targetSDK* - предполагается, что вы протестировали свое приложение до указанной вами версии. Если ваш targetSDK меньше 252 | чем SDK используемый в системе, то будет включена обратная совместимость. 253 | 254 | *** 255 | 256 | #### Что такое zRam? 257 | 258 | *** 259 | 260 | #### What is the difference between low memory killer and out of memory killer? 261 | 262 | Main difference is how LMK and OOM chooses a process to kill in low memory conditions. 263 | 264 | Main reason for introduction of LMK in android was OOM killer sometimes kill high priority process (Like foreground 265 | applications) in low memory conditions, on the other hand LMK has interface (oom score value) with activity manager ( 266 | framework part) which knows priority of processes this results LMK always kill hidden and empty applications before 267 | killing foreground and active applications, apart from this some differences are below 268 | 269 | *** 270 | 271 | #### Garbage Collector 272 | 273 | ###### Расскажите про версии garbage collector в Android 274 | 275 | 1) Dalvik GC: the first GC implementation. It was a conservative, “stop the world” GC. It stops all the threads in the 276 | VM and does its work. 277 | 2) ART GC (Lollipop & Marshmallow): the major and biggest change. The ART/Dalvik Android Team rewrites the entire GC. 278 | This GC was called the “Generational GC” because the objects now have “generations” based on the time they live. 279 | Several other big improvements were introduced here, including the way that GC allocates objects. 280 | 3) ART GC (Nougat): the ART/Dalvik Android Team rewrites the entire allocation process in assembly code. 281 | 4) ART GC (Oreo): an improvement of the ART GC v1. This time, the ART/Dalvik Android Team improves the way that GC does 282 | its work by making it concurrent. This was called the “Concurrent Copying Garbage Collector” among a lot of other 283 | improvements. 284 | [Link](https://www.youtube.com/watch?v=Cficzcp0ynU) 285 | [Link2](https://proandroiddev.com/collecting-the-garbage-a-brief-history-of-gc-over-android-versions-f7f5583e433c) 286 | 287 | *** 288 | 289 | ###### Расскажите подробнее про Dalvik GC 290 | Освобождение памяти проходит в 4 этапа: 291 | 292 | 1) Сборщик мусора приостанавливает все потоки в системе, чтобы найти все объекты доступные от root. Это требует времени, 293 | и за это время ваше приложение ничего не может сделать. 294 | 2) Следующее действие происходит параллельно. GC помечает все найденные объекты. Это означает, что ваше приложение снова 295 | работает, но параллелизм приводит к проблеме. Поскольку ваше приложение снова запущено, оно может выделять объекты. 296 | 3) Когда происходит новое выделение, GC снова приостанавливает все потоки, чтобы повторить пункт 1. 297 | 4) Этап сборки объектов, не помеченных как активных. Происходит параллельно 298 | 299 | Dalvik позволил процессу увеличиваться только до 36 МБ в памяти (в зависимости от конкретной конфигурации устройства) 300 | 301 | *** 302 | 303 | ###### Расскажите подробнее про ART GC (5 Lollipop & 6 Marshmallow) 304 | 305 | 1) Больше нет ограничения по памяти для процесса, ART не имеет ограничений на объем памяти, который может запросить 306 | процесс. 307 | 2) Алгоритм выделения и освобождения памяти по-прежнему CMS (Concurrent Mark-and-Sweep), но с улучшениями. Одним из 308 | самых больших улучшений в способе размещения объектов сборщиком мусора является замена алгоритма dlmalloc алгоритмом 309 | RosAlloc. Эти алгоритмы используются, когда вы создаете новый объект. Отличительной особенностью RosAlloc является 310 | то, что он поддерживает потоки, а это означает, что он может выполнять распределения, специфичные для конкретного 311 | потока. 312 | 3) Добавлены *fine-grained* блокировки, благодаря которым GC блокирует гораздо меньше кода. А также делает 1-ю фазу ( 313 | поиск доступных от корня объектов) алгоритма пометки и очистки параллельной, что сокращает время, необходимое для 314 | запустите алгоритм маркировки и очистки от ~ 10 мс до ~ 3 мс. 315 | 4) Добавлены *GC generations*. *Young*, *Old*, *Permanent*. Первоначально все объекты помещаются в *Young*, но с 316 | течением времени, если прожили достаточно долго переходят в более старые. Если мы хотим создать объект, а памяти не 317 | хватает, GC в первую очередь освободит память в *Young*, и только если там нечего освобождать пойдет в *Old*. 318 | 319 | *** 320 | 321 | ###### Расскажите подробнее про ART GC (7 Nougat) 322 | 323 | 1) Полностью переписали выделение памяти сборщиком мусора в ассемблерном коде, что позволило сделать выделение памяти в 324 | 10 раз быстрее, 325 | 326 | *** 327 | 328 | ###### Расскажите подробнее про ART GC (8 Oreo) 329 | 330 | 1) Опять полностью переписали GC и назвали его *Concurrent Heap Compaction Collector*. 331 | 1) Оптимизировали дефрагментацию, что позволило оптимизировать работу с памятью на 30% 332 | 2) В этой версии сборщик мусора делит кучу на «сегменты». Когда потоку требуется память, сборщик мусора 333 | предоставляет этому потоку «сегмент» для локального выделения объектов. Таким образом, поток может локально 334 | выделять или собирать объекты в этом сегменте, избегая блокировки всей системы для выполнения этой работы. 335 | 3) Взаимовытекающее из предыдущего: при дефрагментации сегмента, если он используется менее 70% или 75%, этот " 336 | сегемент" будет собран, а находящиеся там объекты будут перемещены в другой сегмент. 337 | 338 | За счет этого, время выделения в 18 раз быстрее, чем у Dalvik, и на 70% быстрее, чем у Nougat. 339 | 340 | 2) Убрали Generation GC :) 341 | 342 | *** 343 | 344 | ###### Расскажите подробнее про GC Андроид 10 (Q) 345 | 346 | 1) Вернули Generation GC :) 347 | 348 | *** 349 | 350 | #### Как происходит запуск приложения 351 | 352 | // todo 353 | ![Image](app_launch.png) 354 | 355 | *** 356 | 357 | #### Как происходит компиляция приложения 358 | 359 | [Link](https://www.youtube.com/watch?v=Qp-5stxpTz4) 360 | 361 | *** 362 | 363 | #### Что такое процесс в Android 364 | [Link](https://habr.com/ru/post/124484/) TODO 365 | 366 | *** 367 | 368 | #### Что такое App Sandbox 369 | 370 | Each Android app lives in its own security sandbox, protected by the following Android security features: 371 | 372 | The Android operating system is a multi-user Linux system in which each app is a different user. By default, the system 373 | assigns each app a unique Linux user ID (the ID is used only by the system and is unknown to the app). The system sets 374 | permissions for all the files in an app so that only the user ID assigned to that app can access them. Each process has 375 | its own virtual machine (VM), so an app's code runs in isolation from other apps. By default, every app runs in its own 376 | Linux process. The Android system starts the process when any of the app's components need to be executed, and then 377 | shuts down the process when it's no longer needed or when the system must recover memory for other apps. The Android 378 | system implements the principle of least privilege. That is, each app, by default, has access only to the components 379 | that it requires to do its work and no more. This creates a very secure environment in which an app cannot access parts 380 | of the system for which it is not given permission. However, there are ways for an app to share data with other apps and 381 | for an app to access system services: 382 | 383 | It's possible to arrange for two apps to share the same Linux user ID, in which case they are able to access each 384 | other's files. To conserve system resources, apps with the same user ID can also arrange to run in the same Linux 385 | process and share the same VM. The apps must also be signed with the same certificate. An app can request permission to 386 | access device data such as the device's location, camera, and Bluetooth connection. The user has to explicitly grant 387 | these permissions. For more information, see Working with System Permissions 388 | 389 | *** 390 | 391 | #### Может ли BroadcastReceiver быть запущен без объявления в манифесте? 392 | 393 | BroadcastReceiver могжет быть либо объявлен в манифесте, либо создан динамически в коде, и зарегистрирован в системе с 394 | помощью вызова. 395 | 396 | ```Kotlin 397 | registerReceiver() 398 | ``` 399 | 400 | *** 401 | 402 | #### Виды сервисов 403 | 404 | *Background Service* - service that runs only when the app is running so it’ll get terminated when the app is 405 | terminated. 406 | 407 | *Foreground Service* - service that stays alive even when the app is terminated. 408 | 409 | *Bound Service* - service that runs only if the component it is bound to is still active. 410 | 411 | #### Жизненный цикл сервисов 412 | ![img.png](service_lifecycle.png) 413 | 414 | #### Отличие IntentService, Service, JobIntentService, JobService 415 | 416 | *Service* — это компонент приложения, представляющий либо желание приложения выполнять длительную операцию, не 417 | взаимодействуя с пользователем, либо предоставлять функциональные возможности для использования другими приложениями. 418 | 419 | *Job Service* — Это базовый класс, который обрабатывает асинхронные запросы, запланированные ранее через *JobScheduler*. 420 | Вы несете ответственность за переопределение *onStartJob* и за выведение выполнения задачи из основного потока. 421 | 422 | 423 | *IntentService* — это базовый класс для сервисов, которые обрабатывают асинхронные запросы. Принимает запросы через 424 | startService(Intent); служба запускается по мере необходимости, обрабатывает каждую задачу по очереди, используя рабочий 425 | поток, и останавливается, когда заканчивается работа. Как только приложение будет уничтожено, работа внутри * 426 | onHandleIntent* тоже остановится. 427 | 428 | *JobIntentService* — В отличии от IntentService является частью Androidx и обязует переопределить не 429 | *onHandleIntent*, а *onHandleWork*. Запустить можно через *enqueue work* передав *jobId*. После того, как приложение было 430 | уничтожено, через несколько секунд цикл внутри *onHandleWork* начинает выполняться заново. 431 | 432 | // TODO 433 | ![img.png](async_work.png) 434 | 435 | #### Content resolver, Content Provider 436 | ![img.png](content_provider.png) 437 | 438 | ContentResolver --> ContentProvider --> SQLiteDatabase 439 | 440 | *ContentProvider* предоставляет личные данные вашего приложения внешнему приложению, в то время как 441 | *ContentResolver* предоставляет правильный *ContentProvider* среди всех *ContentProviders*, используя URI. 442 | 443 | 444 | *ContentProvider* и *ContentResolver* являются частью **android.content** пакета. Эти два класса работают вместе, 445 | чтобы обеспечить надежную и безопасную модель обмена данными между приложениями. 446 | 447 | *ContentProvider* предоставляет данные, хранящиеся в базе данных SQLite, другому приложению, не сообщая им о 448 | базовой реализации вашей базы данных. Таким образом, он абстрагирует SQliteDatabase. 449 | 450 | Но внешнее приложение не может напрямую обращаться к *ContentProvider*. Для этого мы взаимодействуем с 451 | *ContentResolver*. Существует только один его экземпляр, и все ContentProviders вашего устройства 452 | зарегистрированы с помощью простого URI пространства имен. Если вы хотите связаться с конкретным ContentProvider, вам просто нужно знать его URI. 453 | Передайте его ContentResolver, и он найдет провайдера, используя URI. 454 | 455 | ``` 456 | content://com.android.contacts/contacts/3 457 | ``` 458 | 459 | *content://* - называется схемой и указывает, что это ContentUri. 460 | 461 | *com.android.contacts* - называется *Content authority*, и ContentResolver использует его для разрешения уникального поставщика (в данном случае ContactProvider). 462 | 463 | *contacts* — это путь , который идентифицирует некоторое подмножество данных провайдера (например, имя таблицы). 464 | 465 | *3* — это идентификатор , используемый для уникальной идентификации строки в подмножестве данных. 466 | 467 | Вы не можете создать свой собственный класс ContentResolver, но вы всегда можете создать свой собственный класс ContentProvider. 468 | 469 | #### Что такое PendingIntent? Если создать два Pending Intent отличные только по данным помещенным в data, с какой ошибкой можно столкнуться? 470 | Позволяет сторонним приложениям запускать компоненты приложения, которое предоставило PendingIntent. 471 | Предоставляя PendingIntent другому приложению, вы предоставляете ему право выполнять указанную вами операцию, как если бы это другое приложение было вами (с теми же разрешениями и идентификатором). 472 | 473 | Сам PendingIntent — это просто ссылка на токен, поддерживаемый системой, описывающий исходные данные, 474 | используемые для его извлечения. Это означает, что даже если процесс приложения-владельца будет уничтожен, 475 | сам PendingIntent останется доступным для использования другими процессами, которые ему передали. 476 | Если создающее приложение позже повторно извлечет PendingIntent того же типа (та же операция, тот же Intent, данные, категории и компоненты и те же флаги), 477 | оно получит PendingIntent, представляющий тот же токен, если он все еще действителен. 478 | 479 | Из-за такого поведения важно знать, когда два Intent считаются одинаковыми для получения PendingIntent. 480 | Распространенной ошибкой является создание нескольких объектов PendingIntent с Intent, которые различаются только своим "дополнительным» содержимым" (data), 481 | ожидая каждый раз получать разные PendingIntent. Этого не происходит. Части Intent, которые используются для сопоставления, — это те же части, которые определены в Intent.filterEquals. 482 | Если вы используете два объекта Intent, которые эквивалентны согласно Intent.filterEquals, вы получите один и тот же PendingIntent для обоих из них. 483 | 484 | Способы справиться с этим нужно использовать флаги *FLAG_CANCEL_CURRENT* или *FLAG_UPDATE_CURRENT* 485 | 486 | При создании изменяемого PendingIntent ВСЕГДА явно указывайте компонент, который будет запускаться в файле Intent. 487 | Это можно сделать явно указав точный класс, который его получит, но это также можно сделать, вызвав Intent.setComponent(). 488 | 489 | *FLAG_IMMUTABLE* - указывает, что намерение внутри PendingIntent не может быть изменено другими приложениями, 490 | которые передают PendingIntent.send(). Приложение всегда может использовать FLAG_UPDATE_CURRENT для изменения своих собственных PendingIntents. 491 | 492 | *FLAG_MUTABLE* - указывает, что Intent внутри Pending Intent должен позволять приложению обновлять его содержимое путем слияния значений из параметра Intent в PendingIntent.send(). 493 | 494 | В версиях Android до Android 6 (API 23) PendingIntents всегда изменяемы. 495 | 496 | *FLAG_ONE_SHOT* - позволяет отправлять PendingIntent только один раз. Это нужно чтобы приложение не выполняло какое-либо действие несколько раз. 497 | 498 | *FLAG_CANCEL_CURRENT* - отменяет существующий PendingIntent, если он существует, перед регистрацией нового. 499 | Это может быть важно, если конкретный объект PendingIntent был отправлен в одно приложение, а вы хотите отправить его в другое приложение, 500 | потенциально обновив данные. Тогда первое приложение больше не сможет использовать предыдущий PendingIntent, но второе приложение сможет. 501 | 502 | #### Когда можно сохранять state чтобы гарантированно восстановить его даже в случае если андроид убьёт приложение? 503 | [Link](https://developer.android.com/reference/android/app/Activity.html?hl=de#onDestroy%28%29) 504 | Хорошая практика - сохранять данные на onPause(), поскольку не гарантируется, что onDestroy() будет всегда вызван, 505 | а onPause() вызовется, когда приложение потеряет фокус. 506 | 507 | Не стоит использовать *onDestroy*. Он может быть вызван либо из-за того, что activity завершается (вызов finish()), либо из-за того, что система временно 508 | уничтожает этот экземпляр действия для экономии места. Можно различить эти два сценария с помощью isFinishing() метода. 509 | 510 | Можно также использовать onSaveInstanceState и onRestoreInstanceState, но они вызываются только при уничтожении конкретной Activity из-за нехватки памяти. 511 | 512 | 513 | #### Launch mode 514 | [Link](https://medium.com/android-news/android-activity-launch-mode-e0df1aa72242) 515 | 516 | #### История версий 517 | [Link](https://habr.com/ru/company/tinkoff/blog/686614/) 518 | 519 | 520 | -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/IPCmessenger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/IPCmessenger.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/app_launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/app_launch.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/arch_kernel_level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/arch_kernel_level.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/async_work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/async_work.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/binder_framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/binder_framework.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/content_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/content_provider.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/process_fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/process_fork.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/process_fork_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/process_fork_2.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/process_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/process_lifecycle.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/service_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/service_lifecycle.png -------------------------------------------------------------------------------- /out/production/Android Notes/common_android/eng/viewLifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/common_android/eng/viewLifecycle.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/eng/ComposeTheory_eng.md: -------------------------------------------------------------------------------- 1 | #### 1) What is recomposition? 2 | 3 | Recomposition is the process of calling your composable functions again when inputs change. When Compose recomposes 4 | based on new inputs, it only calls the functions or lambdas that might have changed, and skips the rest. By skipping all 5 | functions or lambdas that don't have changed parameters, Compose can recompose efficiently. 6 | 7 | *** 8 | 9 | #### 2) remember, remember(key), rememberSaveable, remember { derivedStateOf() } difference 10 | 11 | *remember* - allows you to remember state from previous recompose invocation 12 | 13 | *remember(key)* - will be recomposed only when key will change 14 | 15 | *rememberSaveable* - similarly to *remember*, but the stored value will survive the activity or process recreation using 16 | the saved instance state mechanism (for example it happens when the screen is rotated in the Android application). 17 | 18 | *remember { derivedStateOf(data) }* - similarly to *remember(key)*, but will recompose only if the result inside lambda 19 | changes 20 | 21 | *** 22 | 23 | #### 3) What is SideEffect for? 24 | 25 | A side-effect is a change to the state of the app that happens outside the scope of a composable function. Composables 26 | should ideally be side-effect free. Sometimes side-effects are necessary, for example, to trigger a one-off event such 27 | as showing a snackbar or navigate to another screen given a certain state condition. These actions should be called from 28 | a controlled environment that is aware of the lifecycle of the composable. 29 | 30 | *** 31 | 32 | #### 4) Effect types 33 | 34 | *LaunchedEffect* - run suspend functions in the scope of a composable. Use to call suspend functions safely from inside 35 | a composable. 36 | 37 | *DisposableEffect* - effects that require cleanup. For side effects that need to be cleaned up after the keys change or 38 | if the composable leaves the Composition. Allow register and unregister observers when needed. 39 | 40 | *SideEffect* - publish Compose state to non-compose code 41 | 42 | *** 43 | 44 | #### 4) State types 45 | 46 | [//]: # TODO(https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate) 47 | 48 | *rememberUpdatedState* - ? 49 | 50 | *produceState* - 51 | 52 | *derivedStateOf* - 53 | 54 | *snapshotFlow* - ? 55 | 56 | #### 5) Что такое Snapshot Policy? 57 | 58 | #### 7) Переиспользует ли LazyColumn элементы по аналогии с RecyclerView? 59 | 60 | _LazyColumn_ не переиспользует своих дочерних элементов, как _RecyclerView_. Он создает новые _Composables_ по мере 61 | того, как вы прокручиваете список, и по-прежнему работает быстро, поскольку создание новых _Composables_ относительно 62 | дешево, по сравнению с созданием экземпляров Android Views. 63 | 64 | #### 8) Сохранит ли _by remember {}_ свое значение при повороте экрана? 65 | 66 | Функция _remember_ работает только до тех пор, пока компонуемый объект хранится в Composition. При повороте вся activity 67 | перезапускается, поэтому все состояние теряется. Это также происходит при любом изменении конфигурации и при смерти 68 | процесса. 69 | 70 | Хотя remember помогает сохранять состояние при рекомпозиции, состояние не сохраняется при изменении конфигурации. Для 71 | этого вы должны использовать rememberSaveable. rememberSaveable автоматически сохраняет любое значение, которое можно 72 | сохранить в файле Bundle. Это сохранит каждое сохранившееся состояние после изменений конфигурации (таких как повороты) 73 | и смерть процесса. 74 | 75 | #### 9) Что значит поднятие состояния (state hoisting)? 76 | 77 | Поднятие состояния в Compose — это шаблон перемещения состояния вверх по дереву, чтобы избавить составной объект от 78 | хранения состояния. 79 | 80 | Пример: извлекаем name и onValueChange и перемещаем их вверх по дереву в HelloScreen (компоуз функция, которая вызывает 81 | HelloContent) 82 | 83 | ```Kotlin 84 | @Composable 85 | fun HelloScreen() { 86 | var name by rememberSaveable { mutableStateOf("") } 87 | 88 | HelloContent(name = name, onNameChange = { name = it }) 89 | } 90 | ``` 91 | 92 | Подняв состояние из HelloContent, мы делаем функцию более независимой, переиспользуемой и готовой к тестированию. 93 | HelloContent не зависит от того, как хранится его состояние. Разделение означает, что если вы изменяете или заменяете 94 | HelloScreen, вам не нужно менять способ HelloContent реализации. 95 | 96 | ![img.png](state_hoisting.png) 97 | 98 | Паттерн, в котором состояние снижается, а события возрастают, называется однонаправленным потоком данных (_ 99 | unidirectional data flow_). 100 | 101 | #### 10) Способы сохранения состояния при рекомпозиции 102 | 103 | Используйте _rememberSaveable_ для восстановления состояния пользовательского интерфейса после повторного создания 104 | активити или процесса. 105 | _rememberSaveable_ сохраняет состояние при рекомпозиции. Кроме того, _rememberSaveable_ также сохраняет состояние во 106 | время действия и воссоздания процесса. 107 | 108 | Все типы данных, которые добавляются в Bundle файл, сохраняются автоматически. Если вы хотите сохранить что-то, что 109 | нельзя добавить в файл Bundle, есть несколько вариантов. 110 | 111 | - Parcelize 112 | 113 | ```Kotlin 114 | var someObject = rememberSaveable { mutableStateOf(ParcelizableCustomObject()) } 115 | ``` 116 | 117 | - MapSaver 118 | Для определения собственного правила преобразования объекта в набор значений, которые система может сохранить в формате Bundle. 119 | ```Kotlin 120 | val SomeSaver = run { 121 | val nameKey = "Name" 122 | val anotherFieldKey = "AnotherField" 123 | mapSaver( 124 | save = { mapOf(nameKey to it.name, anotherFieldKey to it.anotherField) }, 125 | restore = { CustomObject(it[nameKey] as String, it[anotherFieldKey] as String) } 126 | ) 127 | } 128 | 129 | var selectedCity = rememberSaveable(stateSaver = SomeSaver) { 130 | mutableStateOf(CustomObject("Name", "Field")) 131 | } 132 | ``` 133 | 134 | - ListSaver 135 | Чтобы избежать необходимости определять ключи для map, вы также можете использовать listSaver и использовать индексы в качестве ключей 136 | 137 | #### 11) Жизненный цикл composable 138 | [Link](https://developer.android.com/jetpack/compose/lifecycle) 139 | 140 | ![img.png](composition.png) 141 | 142 | // TODO 143 | 144 | #### 12) Почему добавление viewModel в дочерние composable функции может привести к утечке? 145 | 146 | #### 13) Как избежать вызова рекомпозиции всех элементов списка при добавлении одного нового элемента? 147 | 148 | #### 14) За что отвечает аннотация @Stable 149 | В случае если composable уже включен в композицию (был вызван, инициализирован, отрисован), он может пропустить рекомпозицию, 150 | если все входные данные стабильны и не изменились. 151 | 152 | Что значит стабилен? 153 | - Результат _equals_ для двух экземпляров всегда будет одинаковым для одних и тех же двух экземпляров. 154 | - Если _публичное_ свойство изменится, Composition будет уведомлено. 155 | - Все публичные свойства также стабильны. 156 | 157 | Есть несколько типов, которые компилятор Compose считает по дефолту стабильными, даже не смотря на то, 158 | что они не помечены как @Stable: 159 | - Все типы примитивных значений: Boolean, Int, Long, Float, Char 160 | - Strings 161 | - Все типы функций (лямбды) 162 | - MutableState 163 | 164 | Compose пропускает рекомпозицию компонуемого, если все входные данные стабильны и не изменились. Сравнение использует _equals_ метод. 165 | 166 | Compose считает тип стабильным только в том случае, если он может это доказать. Например, интерфейс обычно считается нестабильным, 167 | и типы с изменяемыми общедоступными свойствами, реализация которых может быть неизменной, также не являются стабильными. 168 | 169 | Если Compose не может определить, что тип является стабильным, но вы хотите, чтобы Compose считал его стабильным, 170 | пометьте его @Stable аннотацией. 171 | 172 | 173 | -------------------------------------------------------------------------------- /out/production/Android Notes/compose/eng/composition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/eng/composition.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/eng/state_hoisting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/eng/state_hoisting.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/ComposeTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) What is recomposition? 2 | 3 | Recomposition is the process of calling your composable functions again when inputs change. When Compose recomposes 4 | based on new inputs, it only calls the functions or lambdas that might have changed, and skips the rest. By skipping all 5 | functions or lambdas that don't have changed parameters, Compose can recompose efficiently. 6 | 7 | *** 8 | 9 | #### 2) remember, remember(key), rememberSaveable, remember { derivedStateOf() } difference 10 | 11 | *remember* - allows you to remember state from previous recompose invocation 12 | 13 | *remember(key)* - will be recomposed only when key will change 14 | 15 | *rememberSaveable* - similarly to *remember*, but the stored value will survive the activity or process recreation using 16 | the saved instance state mechanism (for example it happens when the screen is rotated in the Android application). 17 | 18 | *remember { derivedStateOf(data) }* - similarly to *remember(key)*, but will recompose only if the result inside lambda 19 | changes // проверить 20 | 21 | *** 22 | 23 | #### 3) What is SideEffect for? 24 | 25 | Побочный эффект — это изменение состояния приложения, которое происходит вне области действия компонуемой функции. 26 | Иногда побочные эффекты необходимы, и чтобы избежать ошибок это должно происходить в контролируемой среде, которая знает о жизненном 27 | цикле составного объекта. 28 | 29 | Эффект — это компонуемая функция, которая не создает пользовательский интерфейс и вызывает запуск побочных 30 | эффектов после завершения композиции. 31 | 32 | *** 33 | 34 | #### 4) Effect types 35 | 36 | *LaunchedEffect* - run suspend functions in the scope of a composable. Use to call suspend functions safely from inside 37 | a composable. 38 | 39 | *DisposableEffect* - effects that require cleanup. For side effects that need to be cleaned up after the keys change or 40 | if the composable leaves the Composition. Allow register and unregister observers when needed. 41 | 42 | *SideEffect* - publish Compose state to non-compose code 43 | 44 | *** 45 | 46 | #### 4) State types 47 | 48 | [//]: # TODO(https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate) 49 | 50 | *rememberUpdatedState* - ? 51 | 52 | *produceState* - 53 | 54 | *derivedStateOf* - 55 | 56 | *snapshotFlow* - ? 57 | 58 | #### 5) Что такое Snapshot Policy? 59 | 60 | #### 7) Переиспользует ли LazyColumn элементы по аналогии с RecyclerView? 61 | 62 | _LazyColumn_ не переиспользует своих дочерних элементов, как _RecyclerView_. Он создает новые _Composables_ по мере 63 | того, как вы прокручиваете список, и по-прежнему работает быстро, поскольку создание новых _Composables_ относительно 64 | дешево, по сравнению с созданием экземпляров Android Views. 65 | 66 | #### 8) Сохранит ли _by remember {}_ свое значение при повороте экрана? 67 | 68 | Функция _remember_ работает только до тех пор, пока компонуемый объект хранится в Composition. При повороте вся activity 69 | перезапускается, поэтому все состояние теряется. Это также происходит при любом изменении конфигурации и при смерти 70 | процесса. 71 | 72 | Хотя remember помогает сохранять состояние при рекомпозиции, состояние не сохраняется при изменении конфигурации. Для 73 | этого вы должны использовать rememberSaveable. rememberSaveable автоматически сохраняет любое значение, которое можно 74 | сохранить в файле Bundle. Это сохранит каждое сохранившееся состояние после изменений конфигурации (таких как повороты) 75 | и смерть процесса. 76 | 77 | #### 9) Что значит поднятие состояния (state hoisting)? 78 | 79 | Поднятие состояния в Compose — это шаблон перемещения состояния вверх по дереву, чтобы избавить составной объект от 80 | хранения состояния. 81 | 82 | Пример: извлекаем name и onValueChange и перемещаем их вверх по дереву в HelloScreen (компоуз функция, которая вызывает 83 | HelloContent) 84 | 85 | ```Kotlin 86 | @Composable 87 | fun HelloScreen() { 88 | var name by rememberSaveable { mutableStateOf("") } 89 | 90 | HelloContent(name = name, onNameChange = { name = it }) 91 | } 92 | ``` 93 | 94 | Подняв состояние из HelloContent, мы делаем функцию более независимой, переиспользуемой и готовой к тестированию. 95 | HelloContent не зависит от того, как хранится его состояние. Разделение означает, что если вы изменяете или заменяете 96 | HelloScreen, вам не нужно менять способ HelloContent реализации. 97 | 98 | ![img.png](state_hoisting.png) 99 | 100 | Паттерн, в котором состояние снижается, а события возрастают, называется однонаправленным потоком данных (_ 101 | unidirectional data flow_). 102 | 103 | #### 10) Способы сохранения состояния при смене конфигурации 104 | 105 | Используйте _rememberSaveable_ для восстановления состояния пользовательского интерфейса после повторного создания 106 | активити или процесса. 107 | _rememberSaveable_ сохраняет состояние при рекомпозиции. Кроме того, _rememberSaveable_ также сохраняет состояние во 108 | время действия и воссоздания процесса. 109 | 110 | Все типы данных, которые добавляются в Bundle файл, сохраняются автоматически. Если вы хотите сохранить что-то, что 111 | нельзя добавить в файл Bundle, есть несколько вариантов. 112 | 113 | - Parcelize 114 | 115 | ```Kotlin 116 | var someObject = rememberSaveable { mutableStateOf(ParcelizableCustomObject()) } 117 | ``` 118 | 119 | - MapSaver Для определения собственного правила преобразования объекта в набор значений, которые система может сохранить 120 | в формате Bundle. 121 | 122 | ```Kotlin 123 | val SomeSaver = run { 124 | val nameKey = "Name" 125 | val anotherFieldKey = "AnotherField" 126 | mapSaver( 127 | save = { mapOf(nameKey to it.name, anotherFieldKey to it.anotherField) }, 128 | restore = { CustomObject(it[nameKey] as String, it[anotherFieldKey] as String) } 129 | ) 130 | } 131 | 132 | var selectedCity = rememberSaveable(stateSaver = SomeSaver) { 133 | mutableStateOf(CustomObject("Name", "Field")) 134 | } 135 | ``` 136 | 137 | - ListSaver Чтобы избежать необходимости определять ключи для map, вы также можете использовать listSaver и использовать 138 | индексы в качестве ключей 139 | 140 | #### 11) Жизненный цикл composable 141 | 142 | [Link](https://developer.android.com/jetpack/compose/lifecycle) 143 | 144 | ![img.png](composition.png) 145 | 146 | // TODO 147 | 148 | #### 12) Можно ли передавать viewModel в дочерние composable функции? 149 | 150 | [Link1 - Business logic and its state holder](https://developer.android.com/topic/architecture/ui-layer/stateholders#business-logic) 151 | 152 | [Link2 - ViewModels as source of truth](https://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth) 153 | 154 | Согласно документации этого делать нельзя. 155 | 156 | 1) Нарушение паттерна _UDF_ (однонаправленного потока данных), который рекомендован для удобной, гибкой, адаптируемой и 157 | тестируемой работы с композабл функциями. 158 | 2) Нарушение принципа single source of truth (SSOT). Передача ViewModel вниз позволяет нескольким составным объектам 159 | вызывать функции ViewModel и изменять их состояние, что усложняет отладку ошибок. 160 | 3) Жизненный цикл композабл намного меньше чем viewModel. А так как рекомпозиция может вызываться довольно часто, то 161 | новая композиция создастся, а старая не сможет очиститься из-за ссылки на viewModel. Это может привести к утечкам. 162 | Тогда должен возникнуть логичный вопрос почему утечка не возникает всегда, а только при передаче в дочерние 163 | composable. Согласно документации: 164 | 165 | _ViewModels have a longer lifetime than the Composition because they survive configuration changes. They can follow the 166 | lifecycle of the host of Compose content–that is, activities or fragments–or the lifecycle of a destination or the 167 | Navigation graph if you're using the Navigation library. Because of their longer lifetime, ViewModels should not hold 168 | long-lived references to state bound to the lifetime of the Composition. If they do, it could cause memory leaks._ 169 | 170 | ViewModel может следить за жизненным циклом _host of Compose content_, к чему относятся активити, фрагменты или 171 | **_место назначения навигации_**. 172 | 173 | #### 13) Как избежать вызова рекомпозиции всех элементов списка при добавлении одного нового элемента? 174 | 175 | [Link1 - Add extra information to help smart recompositions](https://developer.android.com/jetpack/compose/lifecycle#add-info-smart-recomposition) 176 | 177 | Если не добавить key для каждого элемента списка, при добавлении нового элемента компилятор не будет знать, где какой 178 | элемент, и ему придется сделать полную рекомпозицию списка. 179 | 180 | Обертка блока кода в `key(id) {}`, позволит идентифицировать этот экземпляр в композиции. Значение ключа не обязательно 181 | должно быть глобально уникальным, оно должно быть уникальным только среди экземпляров этого блока кода. 182 | 183 | #### 14) За что отвечает аннотация @Stable 184 | 185 | В случае если composable уже включен в композицию (был вызван, инициализирован, отрисован), он может пропустить 186 | рекомпозицию, если все входные данные стабильны и не изменились. 187 | 188 | Что значит стабилен? 189 | 190 | - Результат _equals_ для двух экземпляров всегда будет одинаковым для одних и тех же двух экземпляров. 191 | - Если _публичное_ свойство изменится, Composition будет уведомлено. 192 | - Все публичные свойства также стабильны. 193 | 194 | Есть несколько типов, которые компилятор Compose считает по дефолту стабильными, даже не смотря на то, что они не 195 | помечены как @Stable: 196 | 197 | - Все типы примитивных значений: Boolean, Int, Long, Float, Char 198 | - Strings 199 | - Все типы функций (лямбды) 200 | 201 | Compose пропускает рекомпозицию компонуемого, если все входные данные стабильны и не изменились. Сравнение использует _ 202 | equals_ метод. 203 | 204 | Compose считает тип стабильным только в том случае, если он может это доказать. Например, интерфейс обычно считается 205 | нестабильным, и типы с изменяемыми общедоступными свойствами, реализация которых может быть неизменной, также не 206 | являются стабильными. 207 | 208 | Если Compose не может определить, что тип является стабильным, но вы хотите, чтобы Compose считал его стабильным, 209 | пометьте его _@Stable_ аннотацией. 210 | 211 | #### 15) Как добавить отступы между элементами списка? 212 | 213 | 1) Arrangement.spacedBy 214 | 215 | ```Kotlin 216 | LazyRow( 217 | horizontalArrangement = Arrangement.spacedBy(8.dp), 218 | contentPadding = PaddingValues(horizontal = 16.dp), 219 | modifier = modifier 220 | ) { 221 | items(alignYourBodyData) { item -> 222 | AlignYourBodyElement(item.drawable, item.text) 223 | } 224 | } 225 | ``` 226 | 227 | 2) Добавить к каждому элементу Space() 228 | 3) Modifier.padding() 229 | 230 | #### 16) Когда мы работаем с ViewGroup, большая вложенность элементов приводит к частому измерению размеров, и уменьшает производительность. Сохраняется ли такая проблема в Compose? 231 | 232 | Compose позволяет избежать множественных измерений, вы можете вкладывать их сколь угодно глубоко, не влияя на 233 | производительность. Compose эффективно обрабатывает вложенные макеты, что делает их отличным способом разработки 234 | сложного пользовательского интерфейса. Это улучшение по сравнению с Android Views, где вам нужно избегать вложенных 235 | макетов из соображений производительности. 236 | 237 | При компоновке дерево пользовательского интерфейса компонуется за один проход. Каждому компоненту сначала предлагается 238 | измерить себя, а затем рекурсивно измерить любые дочерние компоненты, передавая ограничения размера вниз по дереву 239 | дочерним элементам. Затем размеры и размещение компонентов определяются, а разрешенные размеры и инструкции по 240 | размещению передаются обратно вверх по дереву. 241 | 242 | ![img.png](measurement_order.png) 243 | 244 | #### 17) Можно ли изменить количество измерений размеров для компоуз элементов? 245 | 246 | Да 247 | 248 | [Link 1](https://developer.android.com/jetpack/compose/layouts/intrinsic-measurements) 249 | 250 | [Link 2](https://youtu.be/zMKMwh9gZuI?t=1047) 251 | 252 | #### 18) Как создать свой Layout (ViewGroup)? 253 | 254 | Используем composable элемент _Layout()_. Нужно передать MeasurePolicy, в которой прописаны правила расстановки 255 | элементов на экране. 256 | 257 | ```Kotlin 258 | Layout( 259 | modifier = modifier, 260 | content = content, 261 | measurePolicy = { measurables, constraints -> 262 | // проводим измерение для каждого элемента, который мы передали в content 263 | val placements = measurables.map { it.measure(constraints) } 264 | 265 | // создаем layout с максимальными из предоставленных границ 266 | layout(constraints.maxWidth, constraints.maxHeight) { 267 | var y = 0 268 | 269 | // последовательно помещаем элементы в списке (как в Column) 270 | placements.forEach { 271 | it.placeRelative(0, y) 272 | y += it.height 273 | } 274 | } 275 | }) 276 | ``` 277 | 278 | ![img.png](layout.png) 279 | 280 | #### 19) Что такое CompositionLocal? 281 | 282 | _CompositionLocal_ — это инструмент для неявной передачи данных через композицию. 283 | 284 | Чтобы избежать необходимости передавать цвета в качестве параметра для большинства функций, Compose предлагает 285 | возможность создавать именованные объекты в области дерева, которые можно использовать в качестве неявного способа 286 | передачи данных через дерево пользовательского интерфейса. 287 | 288 | Material Theme использует CompositionLocal под капотом. MaterialTheme — это объект, предоставляющий три CompositionLocal 289 | экземпляра — цвета, типографику и формы — что позволяет вам получить их позже в любой дочерней части композиции. 290 | 291 | Задать новое значение какого то ключа можно через CompositionLocalProvider и provides 292 | 293 | ``` 294 | CompositionLocalProvider(SomeKey provides SomeValue) 295 | ``` 296 | 297 | а получить через 298 | 299 | ```Kotlin 300 | val resources = LocalContext.current.resources 301 | ``` 302 | 303 | CompositionLocal создает неявные зависимости, для объектов которые их используют, и может привести к неожиданному поведению. 304 | Он не соответвует паттерну SSOT(single source of truth), поскольку может видоизменяться в любой части композиции. 305 | Таким образом, отладка приложения при возникновении проблемы может быть более сложной задачей, 306 | поскольку вам нужно перейти вверх по композиции, чтобы увидеть, где было предоставлено новое значение. 307 | Поэтому не рекомендуется чрезмерное использование CompositionLocal. 308 | Для дебага подобных кейсов можно использовать [Compose Layout Inspector](https://developer.android.com/jetpack/compose/tooling#layout-inspector) 309 | 310 | 311 | #### 20) compositionLocalOf vs staticCompositionLocalOf 312 | 313 | Оба являются API для создания CompositionLocal. 314 | Если маловероятно, что значение, CompositionLocal изменится, используйте staticCompositionLocalOf для получения преимуществ в производительности. 315 | 316 | _compositionLocalOf_ - изменение значения, предоставленного во время перекомпоновки, делает недействительным только то содержимое, которое считывает значение CompositionLocal. 317 | 318 | _staticCompositionLocalOf_ - staticCompositionLocalOf не следит кто из компонентов считывает его значение. Изменение значения приводит к перекомпоновке 319 | всего контента, в котором CompositionLocal предоставляется. 320 | 321 | 322 | #### 21) The three phases of a frame 323 | [Link](https://developer.android.com/jetpack/compose/phases) 324 | 325 | ![img.png](frame_phases.png) 326 | 327 | _Composition_ - _что_ показать. Запускает компонуемые функции и создает описание пользовательского интерфейса. 328 | 329 | _Layout_ - _где_ показать. Состоит из двух шагов - измерение и размещения (measurement and placement). 330 | Элементы измеряют и размещают себя и любые дочерние элементы в 2D-координатах для каждого узла в дереве компоновки. 331 | 332 | _Drawing_ - _как_ отрисовать. Сама отрисовка. 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/composition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/ru/composition.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/frame_phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/ru/frame_phases.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/ru/layout.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/measurement_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/ru/measurement_order.png -------------------------------------------------------------------------------- /out/production/Android Notes/compose/ru/state_hoisting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/compose/ru/state_hoisting.png -------------------------------------------------------------------------------- /out/production/Android Notes/coroutines/eng/CoroutinesPracticeAnswers_eng.md: -------------------------------------------------------------------------------- 1 | ## Answers for [coroutines practice](https://github.com/VeraUvads/Android-Notes/blob/398ce6ef0354a023ea39919c581cb9502ea630e0/src/coroutines/eng/CoroutinesTheory_eng.md) 2 | 3 | #### 1) If we run Dispatcher1 on a quad-core processor, what should we see? 4 | 5 | *Dispatchers.Default* - By default, the maximum number of threads used by this dispatcher is equal to the number of CPU 6 | cores. Coroutines 0,1,2,3 will start immediately. All threads are busy. Coroutines 4,5 coroutines will be launched when 7 | two the previous one finish their work and release threads; 8 | 9 | *Dispatchers.IO* - every Job starts immediately; 10 | 11 | *newSingleThreadExecutor* - 6 coroutines will come to the dispatcher, which has only one thread. Coroutines had to line 12 | up in a queue and be executed sequentially. 13 | 14 | #### 2) What is wrong with Deferred1? 15 | 16 | *deferredList.map { it.await() }* not the same with *deferredList.awaitAll()* 17 | 18 | The code with *map* will suspend the coroutine on each call and execute all the functions in sequence. 19 | *awaitAll* will do all Deferreds independently. 20 | 21 | #### 3) Fix the Lifecycle1. 22 | 23 | There are two mistakes. 24 | 25 | 1) When the activity is destroyed (as example, when we rotate the screen), lifecycle scope will be cancelled. We will 26 | lose result of our API call. To resolve first problem, we should call our api from *viewModelScope*. It will survive 27 | after rotation, and we will receive our data. 28 | 2) We shouldn't handle network calls on Dispatchers.Default. We have Dispatchers.IO for that goals. Use withContext( 29 | Dispatchers.IO) for API call. 30 | 31 | ```Kotlin 32 | class Lifecycle1 { 33 | internal class MainActivity { 34 | 35 | private fun onCreate() { 36 | onClick { 37 | viewModel.callApi() 38 | } 39 | } 40 | } 41 | 42 | 43 | internal class ViewModel { 44 | fun callApi() { 45 | viewModelScope.launch { 46 | withContext(Dispatchers.IO) { 47 | println("Job started") 48 | delay(2000) 49 | println("Job finished") 50 | } 51 | } 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | #### 4) Deadlock with coroutine 58 | 59 | Many ways, two of them: 60 | 61 | ```Kotlin 62 | 63 | fun coroutinesDeadlock1() { 64 | var first: Job = Job() 65 | var second: Job = Job() 66 | runBlocking() { 67 | first = launch(start = CoroutineStart.LAZY) { 68 | println("first before join") 69 | second.join() 70 | println("first after join") 71 | } 72 | second = launch(start = CoroutineStart.LAZY) { 73 | println("second before join") 74 | first.join() 75 | println("second after join") 76 | } 77 | joinAll(first, second) 78 | } 79 | } 80 | 81 | 82 | fun coroutinesDeadlock2() { 83 | var third: Job = Job() 84 | runBlocking() { 85 | third = launch() { 86 | println("third before join") 87 | third.join() 88 | println("third after join") 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | 5) What will be printed? 95 | 1. Child 1 96 | 2. Error stacktrace 97 | 98 | "Child 2" will never be printed, because SupervisorJob works only for child coroutines; Is we want not to cancel our 99 | second Job we should use the supervisor job as shared; 100 | 101 | ```Kotlin 102 | val sharedJob = SupervisorJob() 103 | launch { 104 | val child1 = launch(sharedJob) { 105 | delay(1000L) 106 | println("child 1") 107 | throw RuntimeException() 108 | } 109 | val child2 = launch(sharedJob) { 110 | delay(2000L) 111 | println("child 2") 112 | } 113 | child1.join() 114 | child2.join() 115 | }.join() 116 | ``` 117 | 118 | or 119 | 120 | ```Kotlin 121 | suspend fun exceptionHandling() = coroutineScope { 122 | supervisorScope { 123 | launch { 124 | delay(1000L) 125 | println("child 1") 126 | throw RuntimeException() 127 | } 128 | launch { 129 | delay(2000L) 130 | println("child 2") 131 | } 132 | } 133 | } 134 | ``` 135 | 136 | 6) How can we solve problem with consistent result? 137 | 138 | ```Kotlin 139 | suspend fun massiveRunAnswer() { 140 | var counter = 0 141 | val mutex = Mutex() 142 | withContext(Dispatchers.Default) { 143 | repeat(1000) { 144 | launch { 145 | mutex.withLock { 146 | counter++ 147 | } 148 | } 149 | } 150 | } 151 | println(counter) 152 | } 153 | ``` -------------------------------------------------------------------------------- /out/production/Android Notes/coroutines/eng/CoroutinesTheory_eng.md: -------------------------------------------------------------------------------- 1 | #### Difference between a "coroutine" and a "thread"? 2 | 3 | 1) Coroutines are a form of sequential processing: only one is executing at any given time. Threads are a form of 4 | concurrent processing: multiple threads may be executing at any given time. 5 | 2) The operating system switches running threads according to its scheduler, which is an algorithm in the operating 6 | system kernel. With coroutines, the programmer and programming language determine when to switch coroutines 7 | 3) Coroutines can provide a very high level of concurrency with very little overhead. Generally in a threaded 8 | environment you have at most 30-50 threads before the amount of overhead wasted actually scheduling these threads 9 | (by the system scheduler) significantly cuts into the amount of time the threads actually do useful work. 10 | 4) Coroutines run within a single thread or pool of threads. 11 | 12 | *** 13 | #### Difference between *process* and *tread* 14 | Процесс — экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: один процесс не может получить доступ к переменным и структурам данных другого. Если процесс хочет получить доступ к чужим ресурсам, необходимо использовать межпроцессное взаимодействие. Это могут быть конвейеры, файлы, каналы связи между компьютерами и многое другое. 15 | 16 | Поток использует то же самое пространства стека, что и процесс, а множество потоков совместно используют данные своих состояний. Как правило, каждый поток может работать (читать и писать) с одной и той же областью памяти, в отличие от процессов, которые не могут просто так получить доступ к памяти другого процесса. У каждого потока есть собственные регистры и собственный стек, но другие потоки могут их использовать. 17 | 18 | Поток — определенный способ выполнения процесса. Когда один поток изменяет ресурс процесса, это изменение сразу же становится видно другим потокам этого процесса. 19 | 20 | 21 | *** 22 | 23 | #### What are the main components of coroutines? Describe their role 24 | 25 | *Job, Context, Dispatcher, Continuation и CoroutineScope* 26 | 27 | *Job* - Stores the state of the coroutine: active / canceled / completed. These states change as the coroutine runs. But 28 | we also use coroutine state products on their own: to cancel the coroutine or start deferring the coroutine. Jobs can be 29 | arranged into parent-child hierarchies where cancellation of a parent leads to immediate cancellation of all its 30 | children recursively. 31 | 32 | *CoroutineContext* - collection of unique Key-Element values. Should contain an instance of a job to enforce structured 33 | concurrency. 34 | 35 | *Dispatcher* - determines what thread or threads the coroutine uses for execution. The coroutine dispatcher can confine coroutine execution to a specific thread, 36 | dispatch it to a thread pool, or let it run unconfined. 37 | 38 | [//]: # (TODO) 39 | *CoroutineScope* - 40 | 41 | *Continuation* - 42 | 43 | *** 44 | 45 | #### How the main components of coroutines depend on each other 46 | 47 | [//]: # (TODO) 48 | 49 | *CoroutineScope* store *CoroutineContext*. CoroutineContext it is a map with different objects that implement 50 | CoroutineContext.Element. As example: Job, Dispatcher, 51 | 52 | *** 53 | 54 | #### Coroutine exceptions handling 55 | 56 | a) 57 | 58 | ```Kotlin 59 | try { 60 | doSmthSuspend() 61 | } catch (exception: Exception) { 62 | if (exception is CancellationException) { 63 | throw exception 64 | } 65 | } 66 | ``` 67 | 68 | b) 69 | 70 | ```Kotlin 71 | val handler = CoroutineExceptionHandler { _, exception -> 72 | println("CoroutineExceptionHandler got $exception") 73 | } 74 | 75 | launch(handler) { 76 | throw AssertionError() 77 | } 78 | 79 | CoroutineExceptionHandler got java.lang.AssertionError 80 | ``` 81 | 82 | *** 83 | 84 | 85 | 86 | 87 | #### Why is it forbidden to catch CancellationException? 88 | 89 | [//]: # (TODO) 90 | 91 | *** 92 | 93 | #### What is suspension point? How coroutines state machine will divide this code? 94 | [//]: # (TODO) 95 | 96 | [//]: # (Suspension points are points in code which either end your program early (mostly bad paths in programs), or which start some work on the side, in another routine which is suspended, ultimately notifying you of the end result, and allowing you to continue where you left off.) 97 | 98 | ```Kotlin 99 | launch { 100 | doSmthSuspend1() 101 | toast("First task completed") 102 | doSmthSuspend2() 103 | toast("Second task completed") 104 | } 105 | 106 | 107 | ``` 108 | 109 | 110 | 111 | 1. doSmthSuspend1 and everything above 112 | 2. doSmthSuspend2 and everything between it and doSmthSuspend1 113 | 3. everything after doSmthSuspend2 114 | 115 | *** 116 | 117 | #### How continuation.resume() set parameters to next continuation? 118 | 119 | [//]: # (TODO поправить ошибку в логике) 120 | 121 | ```Kotlin 122 | launch { 123 | val param = buildParam() 124 | val firstResult = doSmthSuspend1(param) 125 | doSmthSuspend2(firstResult) 126 | } 127 | ``` 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | continuation.resume(param) 138 | 139 | *param* will be cast to required type 140 | 141 | ``` 142 | Object invokeSuspend(Object result) { 143 | switch (label) { 144 | case 0: { 145 | label = 1; 146 | result = buildParam(); 147 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 148 | } 149 | case 1: { 150 | param = (String) result; 151 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 152 | } 153 | } 154 | } 155 | ``` 156 | 157 | *** 158 | 159 | #### Difference between async and launch 160 | 161 | *Launch* - Returns a Job and does not carry any resulting value. 162 | 163 | *Async* - Creates a coroutine and returns its future result as an implementation of Deferred. It blocks the current 164 | thread at the entry point of the *await()*. 165 | 166 | *** 167 | 168 | #### Job types 169 | 170 | *Job* - Creates a job object in an active state. A failure of any child of this job immediately causes 171 | this job to fail, too, and cancels the rest of its children. 172 | 173 | *SupervisorJob* -To handle children failure independently of each other use SupervisorJob. 174 | Children of a supervisor job can fail independently of each other. If parent job is specified, 175 | then this supervisor job becomes a child job of its parent and is cancelled when its parent fails or is cancelled. 176 | All this supervisor's children are cancelled in this case, too. The invocation of cancel with exception 177 | (other than CancellationException) on this supervisor job also cancels parent. 178 | 179 | *Deferred* - it is a Job with a result. Deferred has the same state machine as the Job with additional convenience 180 | methods to retrieve the successful or failed result of the computation that was carried out. 181 | The result of the deferred is available when it is completed and can be retrieved by await method, 182 | which throws an exception if the deferred had failed. 183 | 184 | 185 | *** 186 | 187 | #### Join, JoinAll, Await, AwaitAll 188 | 189 | *Join* - Suspends current coroutine until the job completes. 190 | 191 | *JoinAll* - Suspends current coroutine until all given jobs are complete. This method is semantically equivalent to 192 | joining all given jobs one by one with 193 | 194 | ```Kotlin 195 | jobs.forEach { it.join() } 196 | ``` 197 | 198 | *Await* - Deferred extension. Work with async. Awaits for completion of the promise without blocking. Return coroutine 199 | result. 200 | 201 | *AwaitAll* - Deferred extension. Work with async. Awaits for completion of given deferred values without blocking a 202 | thread. Resumes normally with the list of values when all deferred works are completed or resumes with the first thrown 203 | exception (including cancellation). 204 | 205 | ```Kotlin 206 | val results: List = listOf>().awaitAll() 207 | ``` 208 | 209 | *** 210 | 211 | #### What is CoroutineStart? Which types do you know? 212 | 213 | ```Kotlin 214 | fun CoroutineScope.launch( 215 | context: CoroutineContext = EmptyCoroutineContext, 216 | start: CoroutineStart = CoroutineStart.DEFAULT, 217 | block: suspend CoroutineScope.() -> Unit 218 | ): Job 219 | ``` 220 | 221 | *CoroutineStart* defines start options for coroutines builders. It is used in start parameter of launch, async, and 222 | other coroutine builder functions. 223 | 224 | *DEFAULT* - immediately schedules coroutine for execution according to its context; 225 | 226 | *ATOMIC* - atomically (in a non-cancellable way) schedules coroutine for execution according to its context; 227 | 228 | *LAZY* - starts coroutine lazily, only when it is needed; 229 | 230 | *UNDISPATCHED* - immediately executes coroutine until its first suspension point in the current thread. 231 | 232 | *** 233 | 234 | #### How to cancel coroutine? What is ensureActive? 235 | 236 | We can cancel job, and have to ensure that current scope is active by using *isActive* or *ensureActive()* 237 | 238 | ensureActive() - If the job is no longer active, throws CancellationException. This method is a drop-in replacement for 239 | the following code, but with more precise exception: 240 | 241 | ```Kotlin 242 | if (!isActive) { 243 | throw CancellationException() 244 | } 245 | ``` 246 | 247 | ```Kotlin 248 | val job = CoroutineScope.launch { 249 | ensureActive() 250 | doSmth() 251 | } 252 | job.cancel() 253 | ``` 254 | 255 | *** 256 | 257 | #### How to put custom data to CoroutineContext 258 | To coroutine Context we can put CoroutineContext.Element implementation. AbstractCoroutineContextElement - base class 259 | for CoroutineContext.Element implementations. 260 | 261 | ```Kotlin 262 | data class SharedData( 263 | val sharedInfo: Long, 264 | ) : AbstractCoroutineContextElement(UserData) { 265 | companion object Key : CoroutineContext.Key 266 | } 267 | 268 | //then scope could be created as 269 | val scope = CoroutineScope(Job() + Dispatchers.Default + SharedData("I have a secret for you")) 270 | ``` 271 | 272 | *** 273 | #### What is CoroutineDispatcher? Which types do you know? 274 | 275 | The coroutine context includes a coroutine dispatcher that determines what thread or threads the coroutine uses for execution. 276 | The coroutine dispatcher can confine coroutine execution to a specific thread, 277 | dispatch it to a thread pool, or let it run unconfined. 278 | 279 | *Dispatchers.Default* - It uses a common pool of shared background threads. It is backed by a shared pool 280 | of threads on JVM. By default, the maximum number of threads used by this dispatcher is equal 281 | to the number of CPU cores, but is at least two. Using for intensive computing. 282 | 283 | *Dispatchers.IO* - uses a shared pool of on-demand created threads and is designed for blocking operations. 284 | The thread limit is 64 (or more if processor cores are more than 64). Using for write/read/network work. 285 | 286 | *Dispatchers.Unconfined* - is not confined to any specific thread. It executes the initial continuation 287 | of a coroutine in the current call-frame and lets the coroutine resume in whatever thread that is used 288 | by the corresponding suspending function, without mandating any specific threading policy. 289 | Nested coroutines launched in this dispatcher form an event-loop to avoid stack overflows; 290 | 291 | 292 | *** 293 | #### Why is Default not suitable for IO operations? 294 | 295 | 296 | 297 | *** 298 | #### How to create private thread pool? 299 | 300 | [//]: # (TODO) 301 | Private thread pools can be created with *newSingleThreadContext* and *newFixedThreadPoolContext*. 302 | 303 | *** 304 | #### What is ContinuationInterceptor? 305 | 306 | [//]: # (TODO) 307 | 308 | 309 | *** 310 | 311 | #### What is Flow? When we have to use it? 312 | 313 | [//]: # (TODO) 314 | 315 | 316 | *** 317 | 318 | #### Which coroutines will be cancelled? 319 | ![img.png](cancellation.png) 320 | 321 | 322 | -------------------------------------------------------------------------------- /out/production/Android Notes/coroutines/eng/cancellation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/coroutines/eng/cancellation.png -------------------------------------------------------------------------------- /out/production/Android Notes/coroutines/ru/CoroutinesPracticeAnswers_ru.md: -------------------------------------------------------------------------------- 1 | ##Ответы для [coroutines practice](https://github.com/VeraUvads/Android-Notes/blob/398ce6ef0354a023ea39919c581cb9502ea630e0/src/coroutines/eng/CoroutinesTheory_eng.md) 2 | 3 | Coming soon -------------------------------------------------------------------------------- /out/production/Android Notes/coroutines/ru/CoroutinesTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### Разница между корутинами тредами 2 | 3 | 1) Корутины — это форма последовательной обработки: в любой момент времени выполняется только одна. Треды представляют 4 | собой форму параллельной обработки: несколько потоков могут выполняться в любой момент времени. 5 | 2) Операционная система переключает запущенные потоки в соответствии со своим планировщиком, который управляется 6 | алгоритмом в ядре ОС. С корутинами программист и язык программирования определяют, когда переключать работу. 7 | 3) Корутины могут обеспечить очень высокий уровень параллелизма с очень небольшими накладными расходами. Как правило, в 8 | многопоточной среде у вас есть не более 30-50 потоков, прежде чем количество накладных расходов, потраченных впустую 9 | на фактическое планирование этих потоков 10 | (системным планировщиком), значительно сократит количество времени, в течение которого потоки действительно выполняют 11 | полезную работу. 12 | 4) Корутины выполняются в одном потоке или пуле потоков. 13 | 14 | *** 15 | 16 | #### Какие основные составляющие компоненты корутин вы знаете. Опишите их роль 17 | 18 | *Job, Context, Dispatcher и CoroutineScope* 19 | 20 | *Job* - Хранит состояние корутины: активно/отменено/завершено. Эти состояния меняются по мере выполнения корутины. Мы 21 | также можем менять состояния корутины самостоятельно: чтобы отменить корутину или начать создать отложенную корутины. * 22 | Job* может быть организована в иерархии родитель-потомок, где отмена родителя приводит к немедленной отмене всех его 23 | детей рекурсивно. 24 | 25 | *CoroutineContext* - коллекция уникальных значений Key-Element. По контракту должен содержать экземпляр Job для 26 | обеспечения структурированного параллелизма. 27 | 28 | [//]: # (TODO) 29 | *Dispatcher* - 30 | 31 | *CoroutineScope* - 32 | 33 | *Continuation* - 34 | 35 | *** 36 | 37 | #### Как основные компоненты корутин зависят друг от друга 38 | 39 | [//]: # (TODO) 40 | 41 | *** 42 | 43 | #### Обработка исключений корутин 44 | 45 | a) 46 | 47 | ```Kotlin 48 | try { 49 | doSmthSuspend() 50 | } catch (exception: Exception) { 51 | if (exception is CancellationException) { 52 | throw exception 53 | } 54 | } 55 | ``` 56 | 57 | b) 58 | 59 | ```Kotlin 60 | val handler = CoroutineExceptionHandler { _, exception -> 61 | println("CoroutineExceptionHandler got $exception") 62 | } 63 | 64 | launch(handler) { 65 | throw AssertionError() 66 | } 67 | 68 | CoroutineExceptionHandler got java.lang.AssertionError 69 | ``` 70 | 71 | *** 72 | 73 | #### Почему нельзя перехватывать CancellationException? 74 | 75 | [//]: # (TODO) 76 | 77 | *** 78 | 79 | #### Что такое suspension points? Как стейт машина корутин разделит этот код? 80 | 81 | [//]: # (TODO) 82 | 83 | [//]: # (Suspension points are points in code which either end your program early (mostly bad paths in programs), or which start some work on the side, in another routine which is suspended, ultimately notifying you of the end result, and allowing you to continue where you left off.) 84 | 85 | ```Kotlin 86 | launch { 87 | doSmthSuspend1() 88 | toast("First task completed") 89 | doSmthSuspend2() 90 | toast("Second task completed") 91 | } 92 | 93 | 94 | ``` 95 | 96 | 1. doSmthSuspend1() и все до 97 | 2. все после doSmthSuspend1() и до doSmthSuspend2() 98 | 3. все после doSmthSuspend2() 99 | 100 | *** 101 | 102 | #### Как continue.resume() передает параметры следующему блоку стейт машины? 103 | 104 | [//]: # (TODO поправить ошибку в логике) 105 | 106 | ```Kotlin 107 | launch { 108 | val param = buildParam() 109 | val firstResult = doSmthSuspend1(param) 110 | doSmthSuspend2(firstResult) 111 | } 112 | ``` 113 | 114 | continuation.resume(param) 115 | 116 | *param* кастуется к нужному параметру 117 | 118 | ``` 119 | Object invokeSuspend(Object result) { 120 | switch (label) { 121 | case 0: { 122 | label = 1; 123 | result = buildParam(); 124 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 125 | } 126 | case 1: { 127 | param = (String) result; 128 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | *** 135 | 136 | #### Разница между async и launch 137 | 138 | *Launch* -Создает и запукает корутину, возвращает Job и не заботится о результате. 139 | 140 | *Async* - Создает и запускает корутину, и возвращает ее будущий результат через реализацию Deferred. Результат можно 141 | получить через *Deferred.await()*. Приостанавливает нынешний тред в точке вызова *await()* и ждет завершение выполнения 142 | корутины. 143 | 144 | *** 145 | 146 | #### Виды Job 147 | 148 | [//]: # (TODO) 149 | 150 | *Job* - 151 | 152 | *Deffered* - 153 | 154 | *SupervisorJob* - 155 | 156 | *** 157 | 158 | #### Join, JoinAll, Await, AwaitAll 159 | 160 | *Join* - Приостанавливает текущую корутины до завершения Job. 161 | 162 | *JoinAll* - Приостанавливает текущую корутину до тех пор, пока все Job не будут выполнены. Этот метод семантически 163 | эквивалентен: 164 | 165 | ```Kotlin 166 | jobs.forEach { it.join() } 167 | ``` 168 | 169 | *Await* - расширение класса Deferred. Ожидает выполнения без блокировки. Возвращает результат корутины. 170 | 171 | *AwaitAll* - расширение класса Deferred. Ожидает выполнения всех Deffered. Continuation.resume вызывается либо со 172 | списком значений, когда все Deffered работы завершены, или с первым брошенным исключением (включая отмену). 173 | 174 | ```Kotlin 175 | val results: List = listOf>().awaitAll() 176 | ``` 177 | 178 | *** 179 | 180 | #### В чем разница между deferreds.map { it.await() } and deferreds.awaitAll(). 181 | 182 | *AwaitAll* это **НЕ** эквивалент для 183 | 184 | ```Kotlin 185 | deferreds.map { it.await() } // it is NOT AwaitAll 186 | ``` 187 | 188 | *awaitAll* - вернет ошибку если хоть одна из *Deffered* выбросит исключение. 189 | 190 | *этот код* - выполнит все корутины, даже если было выброшено исключение в одной из *Deffered*; 191 | 192 | *** 193 | 194 | #### Что такое CoroutineStart? Какие типы бывают? 195 | 196 | ```Kotlin 197 | fun CoroutineScope.launch( 198 | context: CoroutineContext = EmptyCoroutineContext, 199 | start: CoroutineStart = CoroutineStart.DEFAULT, 200 | block: suspend CoroutineScope.() -> Unit 201 | ): Job 202 | ``` 203 | 204 | *CoroutineStart* определяет параметры запуска для билдера корутины. 205 | 206 | *DEFAULT* - создает и запускает корутину немедленно; 207 | 208 | *ATOMIC* - атомарно (без возможности отмены) планирует сопрограмму для выполнения; 209 | 210 | *LAZY* - запускает сопрограмму, только когда к ней обратятся; 211 | 212 | *UNDISPATCHED* - немедленно выполняет сопрограмму до ее первой точки приостановки в текущем потоке. 213 | 214 | *** 215 | 216 | #### Как отменить корутину? Что такое ensureActive? 217 | 218 | Мы можем отменить job, и должны убедиться, что текущий scope активен, используя *isActive* или *ensureActive()* 219 | 220 | ensureActive() - Если job больше не активна, бросает CancellationException. Этот метод является заменой 221 | следующего кода, но с более точным исключением: 222 | 223 | ```Kotlin 224 | if (!isActive) { 225 | throw CancellationException() 226 | } 227 | ``` 228 | 229 | ```Kotlin 230 | val job = CoroutineScope.launch { 231 | ensureActive() 232 | doSmth() 233 | } 234 | job.cancel() 235 | ``` 236 | 237 | *** 238 | 239 | #### Как поместить дополнительные данные в CoroutineContext? 240 | 241 | В CoroutineContext мы можем поместить классы реализующие CoroutineContext.Element. AbstractCoroutineContextElement - абстрактный класс 242 | для реализации CoroutineContext.Element. 243 | 244 | ```Kotlin 245 | data class SharedData( 246 | val sharedInfo: Long, 247 | ) : AbstractCoroutineContextElement(UserData) { 248 | companion object Key : CoroutineContext.Key 249 | } 250 | 251 | //then scope could be created as 252 | val scope = CoroutineScope(Job() + Dispatchers.Default + SharedData("I have a secret for you")) 253 | ``` 254 | -------------------------------------------------------------------------------- /out/production/Android Notes/di/ru/DI_ru.md: -------------------------------------------------------------------------------- 1 | #### Dagger vs Koin 2 | 3 | [Link](https://proandroiddev.com/how-dagger-hilt-and-koin-differ-under-the-hood-c3be1a2959d7) 4 | 5 | *** 6 | 7 | #### ServiceLocator vs DI 8 | 9 | *** 10 | 11 | #### Основные компоненты в DI? 12 | 13 | *Module* - это классы, в которые мы выносим код создания объектов. Обычно каждый модуль включает в себя создание 14 | объектов близких по смыслу. 15 | 16 | *Component* - это посредник между Activity и модулями. Когда Activity нужен какой-либо объект, она сообщает об этом 17 | компоненту. Компонент знает, какой модуль умеет создавать такой объект, просит модуль создать объект, и передает его в 18 | Activity. При этом компонент может использовать другие модули, чтобы создать всю иерархию объектов, необходимую для 19 | создания искомого объекта. 20 | 21 | *Dependencies* - 22 | 23 | *** 24 | 25 | ### Dagger 26 | 27 | #### Аннотации в Dagger 28 | 29 | *@Module* - сообщаем даггеру, что этот класс является модулем, который вносит свой вклад в граф объектов. Модуль 30 | предоставляет зависимости. Все includes последовательно вносятся в граф объектов. Модуль содержит в себе информацию КАК 31 | создать объект. 32 | 33 | *@Component* - сообщаем даггеру, что этот класс является компонентом. Компонент — это единица, которая в основном 34 | используется для разрешения зависимостей. Посредник между классом который запрашивает и использует зависимость, и тем 35 | кто его создает. Компонент знает, какой модуль умеет создавать нужный объект, просит модуль создать объект, и передает 36 | его в Activity. При этом компонент может использовать другие модули, чтобы создать всю иерархию объектов, необходимую 37 | для создания искомого объекта. 38 | 39 | *@Subcomponent* - сообщаем даггеру, что этот класс является сабкомпонентом. Это вид компонента, производный от 40 | компонента и наследующий зависимости, которые он предоставляет. Позволяет логически разделить и обособить логику. 41 | 42 | *@Subcomponent.Builder/@Component.Builder* - помечаем как создать компонент/сабкомпонент 43 | 44 | *@Provides* - указывает, что метод является поставщиком объекта. 45 | 46 | *@Inject* - помечает, что в свойство должна доставиться зависимость. 47 | 48 | *@AssistedInject* - помечает, что не все запрашиваемые параметры есть в графе. Такие параметры мы помечаем как @Assisted 49 | 50 | *@Binds* - привязывание одного типа к другому 51 | 52 | *@BindsInstance* - помечает метод компонента как привязку экземпляра к некоторому ключу в компоненте. 53 | 54 | Example: 55 | 56 | ```Kotlin 57 | class AppComponent { 58 | @Component.Builder 59 | interface Builder { 60 | @BindsInstance 61 | fun context(context: Context): Builder 62 | } 63 | } 64 | 65 | class App : Application() { 66 | override fun onCreate() { 67 | super.onCreate() 68 | appComponent = DaggerAppComponent.builder().context(this).build() 69 | } 70 | } 71 | ``` 72 | 73 | *@Reusable* - позволяет эффективнее хранить объекты, не хранит объект все время в памяти (в отличии от *@Singleton*). 74 | Вместо этого временно сохраняет экземпляр в памяти, позволяя не создавать каждый раз новый экземпляр при обращении, но 75 | не гарантирует что объект всегда будет один. 76 | 77 | *@Scope* - 78 | 79 | *@IntoSet* - позволяет сделать инжект коллекциии Set, для всех элементов одного и того же типа 80 | 81 | Example: 82 | 83 | ```Kotlin 84 | interface AppModule { 85 | 86 | @Binds 87 | @IntoSet 88 | fun orangeToFruit(orange: Orange) : Fruit 89 | 90 | @Binds 91 | @IntoSet 92 | fun appleToFruit(apple: Apple) : Fruit 93 | 94 | } 95 | 96 | class IUseFruits @Inject constructor( 97 | private val fruits: Set<@JvmSuppressWildcards Fruits> // аннотация нужна из-за особенностей пересечения Kotlin и Java 98 | ) 99 | 100 | 101 | ``` 102 | 103 | 104 | *** 105 | 106 | #### Как работает создаение Scope компонента под капотом? 107 | 108 | *** 109 | 110 | #### Почему Dagger Hilt не стоит использовать для многомодульности 111 | Работает на subcomponents 112 | 113 | *** 114 | 115 | #### Lazy vs Scope? 116 | 117 | Lazy - создание и хранение внутри контейнера в котором вызван 118 | 119 | Scope - хранилище на уровне компонента который будет жить намного дольше 120 | 121 | *** 122 | 123 | #### В чем минус Subcomponent? Как разделить логику компонента без использования subcomponent? 124 | 125 | Subcomponent имеет очень жесткую связь с компонентом, родитель всегда четко должен знать про свои сабкомпоненты. При 126 | генерации кода он вкладывает все класы самкомпонентов внутрь класса компонента. Такой подход не подойдет для 127 | модуляризации. 128 | 129 | Для разделения логики можем использовать два component 130 | 131 | 1) Когда один модуль и мы видим appComponent из любой точки кода, можем использовать AppComponent в качестве зависимости 132 | для FeatureComponent 133 | 134 | ```Kotlin 135 | @Component 136 | interface AppComponent 137 | 138 | @Component(dependencies = [AppComponent::class]) 139 | interface FeatureComponent { 140 | 141 | @Component.Builder 142 | interface Builder { 143 | 144 | @BindsInstance 145 | fun appComponent(appComponent: AppComponent): Builder 146 | 147 | fun build(): FeatureComponent 148 | } 149 | } 150 | ``` 151 | 152 | Тогда создание FeatureComponent сводится к 153 | 154 | ```Kotlin 155 | val featureComponent = DaggerFeatureComponent.builder() 156 | .appComponent(appComponent) 157 | .build() 158 | ``` 159 | 160 | Мы должны передать ему экземпляр компонента AppComponent. Только так FeatureComponent сможет получить доступ к объектам AppComponent. 161 | Он просто будет просить их у этого компонента. 162 | 163 | И вот в этом кроется разница между отношениями компонент-сабкомпонент и компонент-компонент. 164 | Когда мы к компоненту добавляем сабкомпонент, то компонент сам создает реализацию этого сабкомпонента. 165 | В эту реализацию он передает свои модули. И сабкомпонент сам может в этих модулях найти все, что ему понадобится. 166 | Т.е. сабкомпоненту неважно, какие объекты прописаны в интерфейсе компонента. 167 | Сабкомпонент лезет напрямую в модули, минуя компонент. 168 | 169 | А вот в случае пары компонент-компонент связь не такая тесная. 170 | Компоненты создаются отдельно друг от друга. Они общаются на уровне интерфейсов и не имеют доступа к модулям. 171 | 172 | 2) Когда (к примеру) AppComponent лежит в app модуле, а FeatureComponent в feature модуле, и у нас нет доступа к 173 | AppComponent. 174 | 175 | ```Kotlin 176 | 177 | interface AppComponent : FeatureDeps { 178 | 179 | override fun application(): Application 180 | 181 | @Builder 182 | interface Builder { 183 | @BindsInstance 184 | fun application(application: Application): Builder 185 | fun build(): AppComponent 186 | } 187 | } 188 | 189 | interface FeatureDeps { 190 | fun application(): Application 191 | } 192 | 193 | @Component(dependencies = [AppComponent::class]) 194 | interface FeatureComponent { 195 | 196 | @Component.Builder 197 | interface Builder { 198 | 199 | @BindsInstance 200 | fun appComponent(appComponent: AppComponent): Builder 201 | fun build(): FeatureComponent 202 | } 203 | } 204 | ``` 205 | 206 | 207 | 208 | *** 209 | -------------------------------------------------------------------------------- /out/production/Android Notes/gradle/ru/GradleAnswer_ru.md: -------------------------------------------------------------------------------- 1 | 1) Для чего нужен градл? 2 | 3 | 2) Что такое артифакты? 4 | [Link](https://developer.android.com/reference/tools/gradle-api/8.0/com/android/build/api/artifact/Artifacts) 5 | -------------------------------------------------------------------------------- /out/production/Android Notes/gradle/ru/Gradle_ru.md: -------------------------------------------------------------------------------- 1 | 1) Для чего нужен градл? 2 | 3 | 2) Что такое артифакты? 4 | -------------------------------------------------------------------------------- /out/production/Android Notes/ktl/KotlinTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Разница между *class* и *data class*, *data object* и *object* 2 | 3 | #### 2) Способы реализовать функциональный тип? 4 | 5 | ```Kotlin 6 | val a: () -> Unit = {} 7 | 8 | val b: () -> Unit = object : () -> Unit { 9 | override fun invoke() {} 10 | } 11 | 12 | val c: () -> Unit = ::foo 13 | 14 | val d: () -> Unit = fun() {} 15 | 16 | fun foo() = Unit 17 | ``` 18 | 19 | #### 3) Разница между *0 until 10*, *0..10* and *0..<10* 20 | 21 | *0 until 10* == *0..<10* (новый синтаксис, появился в kotlin 1.8.0) 22 | 23 | *0..10* берет интервал включая правый край, *0..<10* исключая 24 | 25 | #### 4) Что такое inline/noinline/crossinline? Какие плюсы от использования? Почему не использовать их постоянно? Когда мы не можем использовать inline? Что такое non-local-return? 26 | 27 | [Link](youtube.com/watch?v=shTrR_O6TaA) 28 | 29 | #### 5) Что такое reified? В чем плюс использования с inline? 30 | 31 | Помогает избавиться от рефлексии. Inline подставляет вместо дженерика нужный нам класс 32 | 33 | #### 6) Сколько параметров в конструкторе может иметь inline class? Почему? 34 | 35 | Только один параметр. Во время компиляции на место инициализации класса заменит инициализацию этого параметра, и 36 | перенесет код из inline в тот где используется его реализация. 37 | 38 | #### 7) Контравариантность, ковариантность, инвариантность 39 | 40 | Представим что у нас есть два класса 41 | 42 | ```Kotlin 43 | open class Fruit 44 | class Orange : Fruit() 45 | 46 | fun variance() { 47 | var fruit = Fruit() 48 | var orange = Orange() 49 | 50 | fruit = orange // если мы попытаемся записать orange во fruit мы сможем это сделать 51 | // orange = fruit // fruit в orange уже нельзя (потому что orange является fruit, а fruit не является orange) 52 | 53 | // А как поведет себя список? 54 | 55 | var orangeList: ArrayList = arrayListOf() 56 | var fruitList: ArrayList = arrayListOf() 57 | // Мы не сможем присвоить переменную как список другого типа. 58 | // Это называется ИНВАРИАНТНОСТЬ 59 | // fruitList = orangeList // forbidden 60 | // orangeList = fruitList // forbidden 61 | 62 | } 63 | ``` 64 | 65 | Если мы хотим иметь список в котором можем содержать всех родителей Orange мы можем использовать *ArrayList< in Orange>* 66 | . Это называется КОНТРВАРИАНТНОСТЬ 67 | 68 | ```Kotlin 69 | doSmth(fruitList) 70 | 71 | fun doSmth(orangeList: ArrayList) { 72 | orangeList.forEach { 73 | 74 | } 75 | val orange: Any? = orangeList[0] // не даст вернуть Orange, только Any 76 | } 77 | ``` 78 | 79 | Если мы хотим иметь список в котором можем содержать всех наследлников Fruit мы можем использовать ArrayList. 80 | Это называется КОВАРИАНТНОСТЬ 81 | 82 | ```Kotlin 83 | doSmth2(orangeList) 84 | 85 | fun doSmth2(orangeList: ArrayList) { 86 | orangeList.forEach { 87 | 88 | } 89 | // orangeList.add(Orange()) не даст ничего добавить потому что тип использующий out == Nothing 90 | } 91 | 92 | ``` 93 | 94 | Так же *in* и *out* могут быть использованы для указания того, какие типы должны быть возвращающими, а какие принимающие 95 | 96 | ```Kotlin 97 | class Example { 98 | fun check(t: T) {} 99 | fun check(): V? { 100 | return null 101 | } 102 | // fun check2(v: V){ // так нельзя, тип V должен использовать для возврата значения 103 | // 104 | // } 105 | } 106 | ``` 107 | 108 | #### 8) Разница между Nothing, Unit и Any 109 | 110 | #### 9) Можно ли наследоваться от data class? Почему? 111 | 112 | #### 10) Что такое делегаты? 113 | 114 | Выражение после by является делегатом, потому что геттеры и сеттеры, делегированы его методам getValue() и setValue(). 115 | Делегаты свойств не должны реализовывать интерфейс, но они должны предоставлять getValue() функцию (и setValue() для vars). 116 | 117 | #### 11) Во что компилируется _typealias_? 118 | 119 | -------------------------------------------------------------------------------- /out/production/Android Notes/multithreading/ru/Multithreading_ru.md: -------------------------------------------------------------------------------- 1 | ### Multithreading 2 | 3 | #### В чем отличие потока от процесса 4 | Процессы и потоки связаны друг с другом, но при этом имеют существенные различия. 5 | 6 | Процесс — экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы 7 | (например, процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: 8 | один процесс не может получить доступ к переменным и структурам данных другого. 9 | Если процесс хочет получить доступ к чужим ресурсам, необходимо использовать межпроцессное взаимодействие. 10 | Это могут быть конвейеры, файлы, каналы связи между компьютерами и многое другое. 11 | 12 | Процесс может иметь дочерний процесс или родительский. У любого процесса должен быть хотя бы один поток, 13 | но поток не может существовать без процесса. 14 | У процесса есть жизненный цикл. 15 | 16 | Поток использует то же самое пространства стека, что и процесс, а множество потоков совместно 17 | используют данные своих состояний. Как правило, каждый поток может работать (читать и писать) 18 | с одной и той же областью памяти, в отличие от процессов, которые не могут просто так получить доступ 19 | к памяти другого процесса. У каждого потока есть собственные регистры и собственный стек, 20 | но другие потоки могут их использовать. 21 | 22 | Поток — определенный способ выполнения процесса. Когда один поток изменяет ресурс процесса, 23 | это изменение сразу же становится видно другим потокам этого процесса. 24 | 25 | #### Какую функцию выполняет Handler? 26 | Поскольку есть только один поток, который обновляет пользовательский интерфейс, 27 | мы используем другие потоки для выполнения нескольких задач в фоновом режиме. Но, чтобы обновить пользовательский 28 | интерфейс после выполнения, нам нужно отправить результат в основной поток или поток пользовательского интерфейса. 29 | 30 | Будет сложно управлять связью со всеми этими потоками, если вы управляете большой группой потоков. 31 | Таким образом, Android предоставил обработчики, чтобы упростить взаимодействие между процессами. 32 | 33 | Компоненты Handler: 34 | 35 | - Handler 36 | - Message 37 | - Message Queue 38 | - Looper 39 | 40 | ![img.png](handler.png) 41 | 42 | MessageQueue — это очередь со списком задач, которые будут выполняться в определенном потоке. 43 | 44 | Handler - добавялет Message в MessageQueue 45 | 46 | Looper - С потоком может быть связан только один Looper. 47 | Присоединение другого Looper к Thread приводит к RuntimeException. 48 | Использование статического объекта ThreadLocal в классе Looper гарантирует, что к потоку будет присоединен только один Looper. 49 | Looper отвечает за сохранение поток живым. 50 | 51 | ![img.png](handler_android.png) -------------------------------------------------------------------------------- /out/production/Android Notes/multithreading/ru/handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/multithreading/ru/handler.png -------------------------------------------------------------------------------- /out/production/Android Notes/multithreading/ru/handler_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/out/production/Android Notes/multithreading/ru/handler_android.png -------------------------------------------------------------------------------- /out/production/Android Notes/oop/ru/OOPPracticeAnswers_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Задача переопределить equals для дочернего класса 2 | *Ошибка N1* 3 | ``` 4 | @Override 5 | public boolean equals(Object o) { 6 | if (!(o instanceof ColorPoint)) 7 | return false; 8 | return super.equals(0) && ((ColorPoint) o).color == color; 9 | } 10 | ``` 11 | 12 | Если начать проверять на ColorPoint, то мы нарушим правило симметричности 13 | **если x.equals(y), то y.equals(x)**. 14 | 15 | *Ошибка N2* 16 | 17 | ``` 18 | @Override 19 | public boolean equals(Object o) { 20 | if (!(o instanceof Point)) 21 | return false; 22 | 23 | if (!(o instanceof ColorPoint)) 24 | return super.equals(o); 25 | return super.equals(o) && ((ColorPoint) o).color == color; 26 | } 27 | ``` 28 | 29 | Здесь мы нарушаем правило транзитивности 30 | ColorPoint p1 == new ColorPoint(1, 2, Color.RED) 31 | Point p2 == new Point(1, 2) 32 | ColorPoint p3 == new ColorPoint(1, 2, Color.BLUE) 33 | 34 | 35 | *Ошибка N3* 36 | 37 | ``` 38 | @Override public boolean equals(Object o) { 39 | if (o == null || o.getClass() != getClass()) 40 | return false; 41 | Point p = (Point) o; 42 | return p.x == x && p.y == y; 43 | } 44 | ``` 45 | 46 | Нарушение принципа Барбары Лисков 47 | -------------------------------------------------------------------------------- /out/production/Android Notes/oop/ru/OOPTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Назовите отношения эквивалентности 2 | - Рефлексивность (x.equals(x)) 3 | - Симметричность (если x.equals(y), то y.equals(x)) 4 | - Транзитивность (если x.equals(y), a y.equals(z), то x.equals(z)) 5 | - Непротиворечивость (x.equals(y)) должно возвращать всегда одинаковое значение 6 | - Для любое ненулевой ссылки x.equals(null) должно возвращать false 7 | 8 | #### 2) -------------------------------------------------------------------------------- /src/common_android/CommonAndroid_eng.md: -------------------------------------------------------------------------------- 1 | ### [1] What is Android Software Stack. What are the major components of the Android platform? 2 | 3 | Applications run on top of a software stack that is based on a Linux kernel, native C/C++ libraries, and a runtime that 4 | executes the application code. 5 | 6 | ![img.png](img/AndroidSoftwareStack.png) 7 | 8 | From the top: 9 | 10 | 1. **System Apps** - Android applications that are implemented in Java. They utilize both Java and Android framework 11 | libraries. Android comes with a set of core apps for email, SMS messaging, calendars, internet browsing, contacts, 12 | and more. If your app would like to deliver an SMS message, you don't need to build that functionality yourself—you 13 | can instead invoke whichever SMS app is already installed to deliver a message to the recipient you specify. 14 | 15 | 2. **Java API framework** - **The entire feature-set of the Android OS** is available to you **through APIs** written in 16 | the Java language. 17 | It contains Android classes that handle the window system, UI toolkit, resources, and so on—basically everything that 18 | is required to write an Android application in Java. The framework **defines and manages the lifecycles** of the 19 | Android components and their intercommunication. It defines a set of Android-specific asynchronous mechanisms that 20 | applications can utilize to simplify the thread management: HandlerThread, AsyncTask, IntentService, 21 | AsyncQueryHandler, and etc. 22 | 23 | 3. **Native C/C++ Libraries** - Many core Android system components and services, such as ART and HAL, are built from 24 | native code that require native libraries written in C and C++. Java applications normally don’t interact directly 25 | with the native libraries because the Application framework provides Java wrappers for the native code. If you are 26 | developing an app that requires C or C++ code, you can use the _Android NDK_ to access some of these native platform 27 | libraries directly from your native code. 28 | 29 | The Native Development Kit (NDK) - is a set of tools that allows you to use C and C++ code with Android. Allows you 30 | to build **performance-critical** parts of your app. hen you build your project, this code is compiled into a native 31 | library that Gradle can package with your app. 32 | 33 | 4. **Android Runtime** - Each app runs in its own process ([App sandbox question](#3-what-is-app-sandbox)) and with its 34 | own instance of the Android Runtime. Android Runtime is written to run multiple virtual machines on low-memory 35 | devices by executing DEX files. DEX files - a bytecode format designed specially for Android that's optimized for 36 | minimal memory footprint. Build tools, such as d8, compile Java sources into DEX bytecode, which can run on the 37 | Android platform. 38 | 39 | Before Android version 5.0 (API level 21) it was Dalvik, but now its 40 | ART ([JVM vs Dalvik vs ART](#2-why-android-os-uses-dvm-instead-of-jvm-why-android-depreciated-dvm-and-started-to-use-art)) 41 | . 42 | 43 | 5. **HAL (Hardware Abstraction Layer)** - provides standard interfaces that expose device hardware capabilities to the 44 | higher-level Java API Framework. The HAL consists of multiple library modules, each of which implements an interface 45 | for a specific type of hardware components, such as the camera or Bluetooth module, for example. 46 | **When a framework API makes a call** to access device hardware, the Android system **loads** the library module for 47 | that hardware component. 48 | 49 | 6. **Linux kernel** - The foundation of the Android platform. Android relies on the Linux kernel for underlying 50 | functionalities such as **threading** and **low-level memory management**. Linux kernel launch/fork a new process for 51 | every application, and every process 52 | **holds a runtime** with a running application. Also, within the process, multiple threads can execute the 53 | application code. The kernel splits the available CPU execution time for processes and their threads through _ 54 | scheduling_. 55 | 56 | ### [2] Why Android OS uses DVM instead of JVM? Why Android depreciated DVM and started to use ART? 57 | 58 | **Java Virtual Machine vs Dalvik Virtual Machine:** 59 | 60 | - JVM is stack based but DVM is register based which is designed to run on low memory. 61 | - JVM uses java byte code and runs ‘.class’ file having JIT (Just in Time) where DVM uses its own byte code and runs 62 | ‘.dex’ file. 63 | - In JVM, single instance is of JVM is shared with multiple applications where DVM has been designed so that the device 64 | can run multiple instance of VM efficiently. Applications are given their own instance. 65 | - JVM support multiple operating system where DVM supports Android operating system only. 66 | - In JVM, the executable is JAR where in DVM the executable is APK. 67 | 68 | ![img.png](img/jvm_vs_dvm.png) 69 | 70 | _Main reasons of using DVM in android is DVM takes less memory, runs and loads faster compared to JVM._ 71 | 72 | **ART vs DVM** 73 | 74 | - **DVM uses JIT (Just in Time) compilation** 75 | 76 | App is compiled every time when an app is launched. 77 | - **ART uses AOT (Ahead-of-Time) compilation.** 78 | 79 | App complies only once. During the app’s installation phase, AOT ( Ahead-of-Time) 80 | statically translates the DEX bytecode into machine code and stores in the device’s storage. This is a one-time event 81 | and happens only when the app is installed on the device. This leads to better battery life and great performance. So 82 | there’s no need for JIT compilation, and the code executes much faster. 83 | - **Garbage collector.** 84 | 85 | One of the main reasons for a poor UX, poor responsiveness, and ultimately bad reviews. In the Dalvik days, GC used to 86 | have two passes over the heap, which led to poor UXs. This situation is improved in ART, as only one pass over the 87 | heap to consolidate the memory is 88 | required. [Android GC work](#8-how-does-a-garbage-collector-work-which-garbage-collector-used-in-android) 89 | 90 | - ART _reduce startup time of applications_ and _improved battery performance_. 91 | 92 | Drawbacks: _increase app installation time_ and (as the native machine code generated on installation is stored in 93 | internal storage) _more internal storage is required_. 94 | 95 | To tackle these Drawbacks such as initial installation time and memory from Android Nougat JIT (Just In Time ) 96 | Compilation was reintroduced with code profiling along with AOT, and an interpreter in the ART thus making it hybrid. 97 | Using the Hybrid Runtime, there won’t be any compilation during install, and applications can be started right away, the 98 | bytecode is interpreted. Now with ART and the new JIT the application installation is faster. The new JIT constantly 99 | does profiling and improves applications as they run. 100 | 101 | [Link1](https://medium.com/programming-lite/android-core-jvm-dvm-art-jit-aot-855039a9a8fa) 102 | 103 | [Link2](https://source.android.com/docs/core/runtime/configure) 104 | 105 | [Video](https://www.youtube.com/watch?v=0J1bm585UCc) 106 | 107 | ### [3] What is App Sandbox? 108 | 109 | Applications execute in different processes and Virtual Machines. Each Android app lives in its own security sandbox. By 110 | default, every app runs in its own Linux process. Each process has its own virtual machine (VM). 111 | 112 | ![img.png](img/sandbox.png) 113 | 114 | Each process has its own virtual machine (VM), so an app's code runs in isolation from other apps. By default, every app 115 | runs in its own Linux process. The Android system starts the process when any of the app's components need to be 116 | executed, and then shuts down the process when it's no longer needed or when the system must recover memory for other 117 | apps. 118 | 119 | By default, the system assigns each app a unique Linux user ID (the ID is used only by the system and is unknown to the 120 | app). The system sets permissions for all the files in an app so that only the user ID assigned to that app can access 121 | them. The Android system implements the principle of least privilege. That is, each app, by default, has access only to 122 | the components that it requires to do its work and no more. This creates a very secure environment in which an app 123 | cannot access parts of the system for which it is not given permission. 124 | 125 | To share data with other apps look [Inter Process Communication](#5-inter-process-communication-ipc) 126 | 127 | It's possible to arrange for two apps to share the same Linux user ID (AndroidManifest - sharedUserID), in which case 128 | they are able to access each other's files. To conserve system resources, apps with the same user ID can also arrange to 129 | run in the same Linux process and share the same VM. The apps must also be signed with the same certificate. 130 | 131 | ### [4] Android build process 132 | 133 | [//]: # (TODO https://medium.com/androiddevnotes/the-internals-of-android-apk-build-process-article-5b68c385fb20) 134 | 135 | ### [5] Inter process communication (IPC) 136 | 137 | ### [6] How the application launch process works 138 | 139 | An application is launched when one of its components (Activity, Service, BroadcastReceiver, ContentProvider) is 140 | initiated to execute. Any component can be an entry point for an application, and as soon as the first component starts, 141 | the Linux process (if not already running) is started, which results in the following startup sequence: 142 | 143 | 1. Linux process starts 144 | 2. Android Runtime is created 145 | 3. An instance of the Application class is created 146 | 4. The application entry point component is created 147 | 148 | Setting up a new Linux process and runtime is not an instant operation. This may reduce performance and have a 149 | noticeable impact on the user experience. So the system tries reduce the startup time of Android applications by 150 | launching a special process called Zygote at system boot. 151 | 152 | What does Zygote do? To answer this question, you need to understand how processes work. 153 | 154 | At the very early stage of loading the Linux OS (at the time of loading the kernel) the very first process is created - 155 | _swapper_ or _sched_ (a process with Process ID = 0). Each process can create new processes (child process), through the 156 | fork function. Fork involves the creation of a new process that is an exact copy of the parent process. 157 | ![img.png](img/process_fork_2.png) 158 | 159 | Efficient and fast application launch is achieved by the fact that Zygote starts with a preload all classes and 160 | resources that the application may potentially need at runtime into system memory. When the application starts, it forks 161 | from the Zygote process. It is the parent for all Android applications. 162 | 163 | Zygote comes pre-installed with the entire set of core libraries. New application processes are forked from the Zygote 164 | process without copying core libraries that are common to all applications. 165 | 166 | Fork involves the creation of a new process that is an exact copy of the parent process. It doesn't actually copy 167 | anything, instead it maps the resources of the new process to those of the parent process and makes copies only when the 168 | new process changes something. 169 | 170 | [Read more: Linux process lifecycle](https://www.okbsapr.ru/library/publications/kanner_2015_3/) 171 | 172 | ### [7] What is process ranking? 173 | 174 | Android system uses Low Memory Killer Daemon (LMK) ([Link](https://source.android.com/docs/core/perf/lmkd)). 175 | 176 | Low Memory Killer Daemon - process monitors the memory state of a running Android system and reacts to high memory 177 | pressure by killing the least essential processes to keep the system performing at acceptable levels. 178 | 179 | Main reason for introduction of LMK in android was OOM (Out of Memory) killer sometimes kill high priority process 180 | (Like foreground applications) in low memory conditions, on the other hand LMK has interface (oom score value) 181 | with activity manager (framework part) which knows priority of processes this results LMK always kill hidden and empty 182 | applications before killing foreground and active applications. 183 | 184 | Process ranking: 185 | 186 | 1. **Foreground process** 187 | 188 | Application has a visible component in front, Service is bound to an Activity in front in a remote process or 189 | BroadcastReceiver is running. 190 | 2. **Visible Process** 191 | 192 | Application has a visible component but is partly obscured. 193 | 3. **Service Process** 194 | 195 | Service is executing in the background and is not tied to a visible component. 196 | 4. **Background process** 197 | 198 | A nonvisible Activity. This is the process level that contains most applications. 199 | 5. **Empty Process** 200 | 201 | A process without active components. Empty processes are kept around to improve startup times, but they are the first 202 | to be terminated when the system reclaims resources. 203 | 204 | The system can have multiple application processes running even while the user perceives them as terminated. The empty 205 | processes are lingering (if system resources permit it) to shorten the startup time on restarts. 206 | 207 | ### [8] How does a garbage collector work? Which garbage collector used in Android? 208 | 209 | ### [9] Does increasing threads increase performance? 210 | 211 | **Introduction:** 212 | 213 | On the operating system level, the thread has both an instruction and a stack pointer. The instruction pointer 214 | references the next instruction to be processed, and the stack pointer references a private memory area —_not available 215 | to other threads_— where thread-local data is stored. 216 | 217 | A CPU can process instructions from _one thread at a time_, but a system normally has multiple threads that require 218 | processing at the same time. For the user to perceive that applications can run in parallel, the CPU has to share its 219 | processing time between the application threads. The sharing of a CPU’s processing time is handled by a _scheduler_. 220 | That determines what thread the CPU should process and for how long. The scheduling strategy can be implemented in 221 | various ways, but it is mainly based on the thread priority(from 1 to 10): a high-priority thread gets the CPU 222 | allocation before a low-priority thread, which gives more execution time to high-priority threads. 223 | 224 | A thread change is known as _context switch_. A context switch starts by storing the state of the executing thread so 225 | that the execution can be resumed at a later point, whereafter that thread has to wait. The scheduler then restores 226 | another waiting thread for processing. 227 | 228 | **Answer:** 229 | 230 | Our job will take longer to finish if we generate thousands of threads since we’ll have to spend time switching between 231 | their contexts. Too many threads might have two negative effects. First, when a fixed quantity of work is divided among 232 | too many threads, each thread receives so little work that the overhead associated with initiating and stopping threads 233 | overwhelms the productive work. Second, running an excessive number of threads results in overhead due to the way they 234 | compete for limited hardware resources. 235 | 236 | ![img.png](img/thread_perfomance.png) 237 | 238 | ### [10] What is the difference between process and thread? 239 | 240 | A process is an instance of a program at runtime, an independent entity that is allocated system resources. 241 | (for example, CPU time and memory). Each process runs in a separate address space: 242 | one process cannot access the variables and data structures of another. If a process wants to access other process's 243 | resources, it must use inter-process communication. 244 | 245 | A thread uses the same stack space as a process, and multiple threads use their shared data. Generally, each thread can 246 | work (read and write) with the same area of memory, as opposed to processes that cannot simply access to the memory of 247 | another process. Each thread has its own registers and its own stack, but other threads can use them. 248 | 249 | A thread is a specific way of executing a process. When one thread modifies a process resource, this change is 250 | immediately visible to other threads in that process. 251 | 252 | ![img.png](img/process_vs_thread.png) 253 | 254 | | Basis of comparison | Process | Flow | 255 | |----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| 256 | | Definition | A process is a running program, i.e. an active program. | A thread is an entity within a process that can be scheduled to run (by scheduler). | 257 | | Context switch | Processes take longer to context switch because they are heavier and require more actions. When switching the execution of a thread of one process to a thread of another, the OS updates some processor registers that are responsible for virtual memory mechanisms, since different processes have different virtual address spaces. | Threads take less time to switch contexts because there is less information to store, clean up, and restore because threads operate in the same address space. | 258 | | Sharing memory | Each process runs in a different address space: one process cannot access the variables and data structures of another. | All threads in a process share its virtual address space and system resources. | 259 | | Communication | Communication between processes takes more time than between threads. If a process wants to access other process's resources, it must use [interprocess communication](#11-inter-process-communication). | Communication between threads takes less time than between processes. | 260 | | Time of creation and termination | Processes take longer to create and complete. When the OS starts a program, it creates a process and creates a main thread to execute the program code. Also, when creating a process, memory is allocated, resources and libraries are loaded / copied. | Thread is used in the same address space and does not require memory allocation and any loading. | 261 | 262 | ![img_1.png](img/process_vs_thread_2.png) 263 | 264 | ### [11] What does thread scheduling depend on? 265 | 266 | Linux treats threads and not processes as the fundamental unit for execution. Hence, scheduling on Android concerns 267 | threads and not processes. 268 | 269 | Scheduling allocates execution time for threads on a processor. The scheduler decides which thread should execute and 270 | for how long it should be allowed to execute before it picks a new thread to execute and a context switch occurs. 271 | 272 | In Android, the application threads are scheduled by the standard scheduler _in the Linux kernel_ and not by the virtual 273 | machine. In practice, this means that the threads in our application are competing not only directly with each other for 274 | execution time, but also against all threads in all the other applications. The threads in the Background Group can’t 275 | get more than ~5-10% execution time altogether. 276 | ![img.png](img/control_group.png) 277 | 278 | The Linux kernel scheduler is known as a _completely fair scheduler_ (CFS). It is “fair” in the sense that it tries to 279 | balance the execution of tasks not only based on the _priority_ of the thread but also by _tracking the amount of 280 | execution_ time (virtual runtime of a thread). If a thread doesn’t use the allocated time to execute, the CFS will 281 | ensure that the priority is lowered so that it will get less execution time in the future. 282 | 283 | The Linux CFS is designed to be fair to competing workloads sharing a common CPU resource. On Linux, the thread priority 284 | is called _niceness_ or n*ice value*, which basically is an indication of how nice a certain thread should behave toward 285 | other threads. The nice value ranges from -19 (least nice, or most CPU time allocated) to 20 (nicest, or least CPU time 286 | allocated). 287 | 288 | The mapping of Java priorities is an implementation detail and may vary depending on platform version. The niceness 289 | mapping values in the table are from Jelly Bean. 290 | 291 | | Thread.setPriority(int) | Linux niceness | 292 | |--------------------------|----------------| 293 | | 1 (Thread.MIN_PRIORITY) | 19 | 294 | | 2 | 16 | 295 | | 3 | 13 | 296 | | 4 | 10 | 297 | | 5 (Thread.NORM_PRIORITY) | 0 | 298 | | 6 | -2 | 299 | | 7 | -4 | 300 | | 8 | -5 | 301 | | 9 | -6 | 302 | | 10 (Thread.MAX_PRIORITY) | -8 | 303 | 304 | ### [12] What are Handler, Looper, MessageQueue for? 305 | 306 | There is only one thread that updates the UI. We use other threads to run multiple tasks in the background. To update 307 | the UI after execution, we need to send the result to the main or UI thread. 308 | 309 | It is difficult to manage communication with a large group of threads. Android provides API to make communication 310 | between threads easier. 311 | 312 | Components: 313 | 314 | - Looper 315 | - Message Queue 316 | - Handler 317 | - Message 318 | 319 | Looper is a kind of infinite loop that reads values from a queue and executes. It looks something like this: 320 | 321 | ```Kotlin 322 | val queue: Queue = ArrayDeque() 323 | while (true) { 324 | val runnable = queue.poll() 325 | runnable?.run() 326 | } 327 | ``` 328 | 329 | In fact, Looper does not work with Queue, but with MessageQueue. In short, a MessageQueue is a queue with a 330 | list of tasks that will be executed on a specific thread. It is similar to Queue but is a class that works 331 | with Message objects. 332 | 333 | Message contains the _task execution time_, a _link to the next Message_, and the _Runnable_ , that Lopper should 334 | execute. 335 | 336 | To put a task in a Looper, you need a Handler. 337 | 338 | Handler - Consumer thread message processor, and the interface for a producer thread to insert messages into the queue. 339 | A Looper can have many associated handlers, but they all insert messages into the same queue. 340 | 341 | ![img.png](img/handler.png) 342 | 343 | Only one Looper can be associated with a thread. Attaching another Looper to a Thread results in a RuntimeException. 344 | Looper is responsible for keeping the thread alive. 345 | 346 | 347 | [//]: # (### [9] Why can only the UI thread in Android update the UI?) 348 | 349 | [//]: # (https://developer.android.com/guide/components/processes-and-threads.html + Java Concurrency) 350 | 351 | [//]: # (### [9] Memory work) 352 | 353 | [//]: # (### [11] Inter Process Communication) 354 | 355 | [//]: # (Добавтить ссылку в 10 вопрос ) 356 | 357 | ### [13] Which app startup states exist? 358 | 359 | App launch can take place in one of three states: cold start, warm start, or hot start. . Each state affects how long it 360 | takes for your app to become visible to the user. Two important metrics for determining app startup are time to initial 361 | display (TTID) and time to fully drawn (TTFD). TTID is the time taken to display the first frame, and TTFD is the time 362 | taken for the app to become fully interactive. Both are equally important, as TTFD lets the user know that the app is 363 | loading, and TTFD is when the app is actually useable. 364 | 365 | ![img.png](img/startup_types.png) 366 | 367 | **Cold start** 368 | At the beginning of a cold start, the system has the three following tasks: 369 | 370 | - Load and launch the app. 371 | - Display a blank starting window for the app immediately after launch. 372 | - Create the app process. 373 | 374 | As soon as the system creates the app process, the app process is responsible for the next 375 | stages: 376 | - Create the app object. 377 | - Launch the main thread. 378 | - Create the main activity. 379 | - Inflate views. 380 | - Layout the screen. 381 | - Perform the initial draw. 382 | 383 | When the app process completes the first draw, the system process swaps out the displayed background window, replacing 384 | it with the main activity. At this point, the user can start using the app. 385 | ![img.png](img/cold_app_launch.png) 386 | 387 | **Warm start** 388 | The user backs out of your app but then re-launches it. The process might continue to run, but the app must recreate the 389 | activity from scratch using a call to onCreate(). 390 | 391 | **Hot start** 392 | If all of your app's activities are still resident in memory, then the app can avoid repeating object initialization, 393 | layout inflation, and rendering. 394 | However, if some memory is purged in response to memory trimming events, such as onTrimMemory(), then these objects need 395 | to be recreated in response to the hot start event. 396 | 397 | [Android vitals](#14-what-is-android-vitals) considers the following startup times for your app excessive: 398 | 399 | - Cold startup takes 5 seconds or longer. 400 | - Warm startup takes 2 seconds or longer. 401 | - Hot startup takes 1.5 seconds or longer. 402 | 403 | [Link](https://developer.android.com/topic/performance/vitals/launch-time) 404 | 405 | ### [14] What is Android Vitals? 406 | Core metrics that affect the visibility of your app on Google Play. 407 | 408 | Core vitals: 409 | 410 | - User-perceived ANR rate 411 | - User-perceived crash rate 412 | 413 | All other vitals: 414 | 415 | - Excessive wakeups 416 | - Stuck partial wake locks 417 | - Excessive background Wi-Fi scans 418 | - Excessive background network usage 419 | - [App startup time](#13-which-app-startup-states-exist) 420 | - Slow rendering 421 | - Frozen frames 422 | - Permission denials 423 | - High denial rates suggest that users don't think the additional exposure of their information is worth the benefits offered in return. 424 | 425 | [Link](https://developer.android.com/topic/performance/vitals) 426 | 427 | 428 | ### [15] Which API should we use if we want to save data in the application? 429 | ![img.png](img/data_api_decision_tree.jpeg) 430 | 431 | -------------------------------------------------------------------------------- /src/common_android/draft/eng/IPCmessenger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/IPCmessenger.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/app_launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/app_launch.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/arch_kernel_level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/arch_kernel_level.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/async_work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/async_work.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/binder_framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/binder_framework.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/content_provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/content_provider.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/doze_mode_maintaince_windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/doze_mode_maintaince_windows.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/process_fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/process_fork.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/process_fork_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/process_fork_2.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/process_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/process_lifecycle.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/service_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/service_lifecycle.png -------------------------------------------------------------------------------- /src/common_android/draft/eng/viewLifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/draft/eng/viewLifecycle.png -------------------------------------------------------------------------------- /src/common_android/img/AndroidSoftwareStack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/AndroidSoftwareStack.png -------------------------------------------------------------------------------- /src/common_android/img/cold_app_launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/cold_app_launch.png -------------------------------------------------------------------------------- /src/common_android/img/control_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/control_group.png -------------------------------------------------------------------------------- /src/common_android/img/data_api_decision_tree.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/data_api_decision_tree.jpeg -------------------------------------------------------------------------------- /src/common_android/img/handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/handler.png -------------------------------------------------------------------------------- /src/common_android/img/handler_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/handler_android.png -------------------------------------------------------------------------------- /src/common_android/img/jvm_vs_dvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/jvm_vs_dvm.png -------------------------------------------------------------------------------- /src/common_android/img/process_fork_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/process_fork_2.png -------------------------------------------------------------------------------- /src/common_android/img/process_vs_thread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/process_vs_thread.png -------------------------------------------------------------------------------- /src/common_android/img/process_vs_thread_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/process_vs_thread_2.png -------------------------------------------------------------------------------- /src/common_android/img/sandbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/sandbox.png -------------------------------------------------------------------------------- /src/common_android/img/startup_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/startup_types.png -------------------------------------------------------------------------------- /src/common_android/img/thread_perfomance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/common_android/img/thread_perfomance.png -------------------------------------------------------------------------------- /src/compose/eng/ComposeTheory_eng.md: -------------------------------------------------------------------------------- 1 | #### 1) What is recomposition? 2 | 3 | Recomposition is the process of calling your composable functions again when inputs change. When Compose recomposes 4 | based on new inputs, it only calls the functions or lambdas that might have changed, and skips the rest. By skipping all 5 | functions or lambdas that don't have changed parameters, Compose can recompose efficiently. 6 | 7 | *** 8 | 9 | #### 2) remember, remember(key), rememberSaveable, remember { derivedStateOf() } difference 10 | 11 | *remember* - allows you to remember state from previous recompose invocation 12 | 13 | *remember(key)* - will be recomposed only when key will change 14 | 15 | *rememberSaveable* - similarly to *remember*, but the stored value will survive the activity or process recreation using 16 | the saved instance state mechanism (for example it happens when the screen is rotated in the Android application). 17 | 18 | *remember { derivedStateOf(data) }* - similarly to *remember(key)*, but will recompose only if the result inside lambda 19 | changes 20 | 21 | *** 22 | 23 | #### 3) What is SideEffect for? 24 | 25 | A side-effect is a change to the state of the app that happens outside the scope of a composable function. Composables 26 | should ideally be side-effect free. Sometimes side-effects are necessary, for example, to trigger a one-off event such 27 | as showing a snackbar or navigate to another screen given a certain state condition. These actions should be called from 28 | a controlled environment that is aware of the lifecycle of the composable. 29 | 30 | *** 31 | 32 | #### 4) Effect types 33 | 34 | *LaunchedEffect* - run suspend functions in the scope of a composable. Use to call suspend functions safely from inside 35 | a composable. 36 | 37 | *DisposableEffect* - effects that require cleanup. For side effects that need to be cleaned up after the keys change or 38 | if the composable leaves the Composition. Allow register and unregister observers when needed. 39 | 40 | *SideEffect* - publish Compose state to non-compose code 41 | 42 | *** 43 | 44 | #### 4) State types 45 | 46 | [//]: # TODO(https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate) 47 | 48 | *rememberUpdatedState* - ? 49 | 50 | *produceState* - 51 | 52 | *derivedStateOf* - 53 | 54 | *snapshotFlow* - ? 55 | 56 | #### 5) Что такое Snapshot Policy? 57 | 58 | #### 7) Переиспользует ли LazyColumn элементы по аналогии с RecyclerView? 59 | 60 | _LazyColumn_ не переиспользует своих дочерних элементов, как _RecyclerView_. Он создает новые _Composables_ по мере 61 | того, как вы прокручиваете список, и по-прежнему работает быстро, поскольку создание новых _Composables_ относительно 62 | дешево, по сравнению с созданием экземпляров Android Views. 63 | 64 | #### 8) Сохранит ли _by remember {}_ свое значение при повороте экрана? 65 | 66 | Функция _remember_ работает только до тех пор, пока компонуемый объект хранится в Composition. При повороте вся activity 67 | перезапускается, поэтому все состояние теряется. Это также происходит при любом изменении конфигурации и при смерти 68 | процесса. 69 | 70 | Хотя remember помогает сохранять состояние при рекомпозиции, состояние не сохраняется при изменении конфигурации. Для 71 | этого вы должны использовать rememberSaveable. rememberSaveable автоматически сохраняет любое значение, которое можно 72 | сохранить в файле Bundle. Это сохранит каждое сохранившееся состояние после изменений конфигурации (таких как повороты) 73 | и смерть процесса. 74 | 75 | #### 9) Что значит поднятие состояния (state hoisting)? 76 | 77 | Поднятие состояния в Compose — это шаблон перемещения состояния вверх по дереву, чтобы избавить составной объект от 78 | хранения состояния. 79 | 80 | Пример: извлекаем name и onValueChange и перемещаем их вверх по дереву в HelloScreen (компоуз функция, которая вызывает 81 | HelloContent) 82 | 83 | ```Kotlin 84 | @Composable 85 | fun HelloScreen() { 86 | var name by rememberSaveable { mutableStateOf("") } 87 | 88 | HelloContent(name = name, onNameChange = { name = it }) 89 | } 90 | ``` 91 | 92 | Подняв состояние из HelloContent, мы делаем функцию более независимой, переиспользуемой и готовой к тестированию. 93 | HelloContent не зависит от того, как хранится его состояние. Разделение означает, что если вы изменяете или заменяете 94 | HelloScreen, вам не нужно менять способ HelloContent реализации. 95 | 96 | ![img.png](state_hoisting.png) 97 | 98 | Паттерн, в котором состояние снижается, а события возрастают, называется однонаправленным потоком данных (_ 99 | unidirectional data flow_). 100 | 101 | #### 10) Способы сохранения состояния при рекомпозиции 102 | 103 | Используйте _rememberSaveable_ для восстановления состояния пользовательского интерфейса после повторного создания 104 | активити или процесса. 105 | _rememberSaveable_ сохраняет состояние при рекомпозиции. Кроме того, _rememberSaveable_ также сохраняет состояние во 106 | время действия и воссоздания процесса. 107 | 108 | Все типы данных, которые добавляются в Bundle файл, сохраняются автоматически. Если вы хотите сохранить что-то, что 109 | нельзя добавить в файл Bundle, есть несколько вариантов. 110 | 111 | - Parcelize 112 | 113 | ```Kotlin 114 | var someObject = rememberSaveable { mutableStateOf(ParcelizableCustomObject()) } 115 | ``` 116 | 117 | - MapSaver 118 | Для определения собственного правила преобразования объекта в набор значений, которые система может сохранить в формате Bundle. 119 | ```Kotlin 120 | val SomeSaver = run { 121 | val nameKey = "Name" 122 | val anotherFieldKey = "AnotherField" 123 | mapSaver( 124 | save = { mapOf(nameKey to it.name, anotherFieldKey to it.anotherField) }, 125 | restore = { CustomObject(it[nameKey] as String, it[anotherFieldKey] as String) } 126 | ) 127 | } 128 | 129 | var selectedCity = rememberSaveable(stateSaver = SomeSaver) { 130 | mutableStateOf(CustomObject("Name", "Field")) 131 | } 132 | ``` 133 | 134 | - ListSaver 135 | Чтобы избежать необходимости определять ключи для map, вы также можете использовать listSaver и использовать индексы в качестве ключей 136 | 137 | #### 11) Жизненный цикл composable 138 | [Link](https://developer.android.com/jetpack/compose/lifecycle) 139 | 140 | ![img.png](composition.png) 141 | 142 | // TODO 143 | 144 | #### 12) Почему добавление viewModel в дочерние composable функции может привести к утечке? 145 | 146 | #### 13) Как избежать вызова рекомпозиции всех элементов списка при добавлении одного нового элемента? 147 | 148 | #### 14) За что отвечает аннотация @Stable 149 | В случае если composable уже включен в композицию (был вызван, инициализирован, отрисован), он может пропустить рекомпозицию, 150 | если все входные данные стабильны и не изменились. 151 | 152 | Что значит стабилен? 153 | - Результат _equals_ для двух экземпляров всегда будет одинаковым для одних и тех же двух экземпляров. 154 | - Если _публичное_ свойство изменится, Composition будет уведомлено. 155 | - Все публичные свойства также стабильны. 156 | 157 | Есть несколько типов, которые компилятор Compose считает по дефолту стабильными, даже не смотря на то, 158 | что они не помечены как @Stable: 159 | - Все типы примитивных значений: Boolean, Int, Long, Float, Char 160 | - Strings 161 | - Все типы функций (лямбды) 162 | - MutableState 163 | 164 | Compose пропускает рекомпозицию компонуемого, если все входные данные стабильны и не изменились. Сравнение использует _equals_ метод. 165 | 166 | Compose считает тип стабильным только в том случае, если он может это доказать. Например, интерфейс обычно считается нестабильным, 167 | и типы с изменяемыми общедоступными свойствами, реализация которых может быть неизменной, также не являются стабильными. 168 | 169 | Если Compose не может определить, что тип является стабильным, но вы хотите, чтобы Compose считал его стабильным, 170 | пометьте его @Stable аннотацией. 171 | 172 | 173 | -------------------------------------------------------------------------------- /src/compose/eng/composition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/eng/composition.png -------------------------------------------------------------------------------- /src/compose/eng/state_hoisting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/eng/state_hoisting.png -------------------------------------------------------------------------------- /src/compose/ru/ComposeTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) What is recomposition? 2 | 3 | Recomposition is the process of calling your composable functions again when inputs change. When Compose recomposes 4 | based on new inputs, it only calls the functions or lambdas that might have changed, and skips the rest. By skipping all 5 | functions or lambdas that don't have changed parameters, Compose can recompose efficiently. 6 | 7 | *** 8 | 9 | #### 2) remember, remember(key), rememberSaveable, remember { derivedStateOf() } difference 10 | 11 | *remember* - allows you to remember state from previous recompose invocation 12 | 13 | *remember(key)* - will be recomposed only when key will change 14 | 15 | *rememberSaveable* - similarly to *remember*, but the stored value will survive the activity or process recreation using 16 | the saved instance state mechanism (for example it happens when the screen is rotated in the Android application). 17 | 18 | *remember { derivedStateOf(data) }* - similarly to *remember(key)*, but will recompose only if the result inside lambda 19 | changes // проверить 20 | 21 | *** 22 | 23 | #### 3) What is SideEffect for? 24 | 25 | Побочный эффект — это изменение состояния приложения, которое происходит вне области действия компонуемой функции. 26 | Иногда побочные эффекты необходимы, и чтобы избежать ошибок это должно происходить в контролируемой среде, которая знает о жизненном 27 | цикле составного объекта. 28 | 29 | Эффект — это компонуемая функция, которая не создает пользовательский интерфейс и вызывает запуск побочных 30 | эффектов после завершения композиции. 31 | 32 | *** 33 | 34 | #### 4) Effect types 35 | 36 | *LaunchedEffect* - run suspend functions in the scope of a composable. Use to call suspend functions safely from inside 37 | a composable. 38 | 39 | *DisposableEffect* - effects that require cleanup. For side effects that need to be cleaned up after the keys change or 40 | if the composable leaves the Composition. Allow register and unregister observers when needed. 41 | 42 | *SideEffect* - publish Compose state to non-compose code 43 | 44 | *** 45 | 46 | #### 4) State types 47 | 48 | [//]: # TODO(https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate) 49 | 50 | *rememberUpdatedState* - ? 51 | 52 | *produceState* - 53 | 54 | *derivedStateOf* - 55 | 56 | *snapshotFlow* - ? 57 | 58 | #### 5) Что такое Snapshot Policy? 59 | 60 | #### 7) Переиспользует ли LazyColumn элементы по аналогии с RecyclerView? 61 | 62 | _LazyColumn_ не переиспользует своих дочерних элементов, как _RecyclerView_. Он создает новые _Composables_ по мере 63 | того, как вы прокручиваете список, и по-прежнему работает быстро, поскольку создание новых _Composables_ относительно 64 | дешево, по сравнению с созданием экземпляров Android Views. 65 | 66 | #### 8) Сохранит ли _by remember {}_ свое значение при повороте экрана? 67 | 68 | Функция _remember_ работает только до тех пор, пока компонуемый объект хранится в Composition. При повороте вся activity 69 | перезапускается, поэтому все состояние теряется. Это также происходит при любом изменении конфигурации и при смерти 70 | процесса. 71 | 72 | Хотя remember помогает сохранять состояние при рекомпозиции, состояние не сохраняется при изменении конфигурации. Для 73 | этого вы должны использовать rememberSaveable. rememberSaveable автоматически сохраняет любое значение, которое можно 74 | сохранить в файле Bundle. Это сохранит каждое сохранившееся состояние после изменений конфигурации (таких как повороты) 75 | и смерть процесса. 76 | 77 | #### 9) Что значит поднятие состояния (state hoisting)? 78 | 79 | Поднятие состояния в Compose — это шаблон перемещения состояния вверх по дереву, чтобы избавить составной объект от 80 | хранения состояния. 81 | 82 | Пример: извлекаем name и onValueChange и перемещаем их вверх по дереву в HelloScreen (компоуз функция, которая вызывает 83 | HelloContent) 84 | 85 | ```Kotlin 86 | @Composable 87 | fun HelloScreen() { 88 | var name by rememberSaveable { mutableStateOf("") } 89 | 90 | HelloContent(name = name, onNameChange = { name = it }) 91 | } 92 | ``` 93 | 94 | Подняв состояние из HelloContent, мы делаем функцию более независимой, переиспользуемой и готовой к тестированию. 95 | HelloContent не зависит от того, как хранится его состояние. Разделение означает, что если вы изменяете или заменяете 96 | HelloScreen, вам не нужно менять способ HelloContent реализации. 97 | 98 | ![img.png](state_hoisting.png) 99 | 100 | Паттерн, в котором состояние снижается, а события возрастают, называется однонаправленным потоком данных (_ 101 | unidirectional data flow_). 102 | 103 | #### 10) Способы сохранения состояния при смене конфигурации 104 | 105 | Используйте _rememberSaveable_ для восстановления состояния пользовательского интерфейса после повторного создания 106 | активити или процесса. 107 | _rememberSaveable_ сохраняет состояние при рекомпозиции. Кроме того, _rememberSaveable_ также сохраняет состояние во 108 | время действия и воссоздания процесса. 109 | 110 | Все типы данных, которые добавляются в Bundle файл, сохраняются автоматически. Если вы хотите сохранить что-то, что 111 | нельзя добавить в файл Bundle, есть несколько вариантов. 112 | 113 | - Parcelize 114 | 115 | ```Kotlin 116 | var someObject = rememberSaveable { mutableStateOf(ParcelizableCustomObject()) } 117 | ``` 118 | 119 | - MapSaver Для определения собственного правила преобразования объекта в набор значений, которые система может сохранить 120 | в формате Bundle. 121 | 122 | ```Kotlin 123 | val SomeSaver = run { 124 | val nameKey = "Name" 125 | val anotherFieldKey = "AnotherField" 126 | mapSaver( 127 | save = { mapOf(nameKey to it.name, anotherFieldKey to it.anotherField) }, 128 | restore = { CustomObject(it[nameKey] as String, it[anotherFieldKey] as String) } 129 | ) 130 | } 131 | 132 | var selectedCity = rememberSaveable(stateSaver = SomeSaver) { 133 | mutableStateOf(CustomObject("Name", "Field")) 134 | } 135 | ``` 136 | 137 | - ListSaver Чтобы избежать необходимости определять ключи для map, вы также можете использовать listSaver и использовать 138 | индексы в качестве ключей 139 | 140 | #### 11) Жизненный цикл composable 141 | 142 | [Link](https://developer.android.com/jetpack/compose/lifecycle) 143 | 144 | ![img.png](composition.png) 145 | 146 | // TODO 147 | 148 | #### 12) Можно ли передавать viewModel в дочерние composable функции? 149 | 150 | [Link1 - Business logic and its state holder](https://developer.android.com/topic/architecture/ui-layer/stateholders#business-logic) 151 | 152 | [Link2 - ViewModels as source of truth](https://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth) 153 | 154 | Согласно документации этого делать нельзя. 155 | 156 | 1) Нарушение паттерна _UDF_ (однонаправленного потока данных), который рекомендован для удобной, гибкой, адаптируемой и 157 | тестируемой работы с композабл функциями. 158 | 2) Нарушение принципа single source of truth (SSOT). Передача ViewModel вниз позволяет нескольким составным объектам 159 | вызывать функции ViewModel и изменять их состояние, что усложняет отладку ошибок. 160 | 3) Жизненный цикл композабл намного меньше чем viewModel. А так как рекомпозиция может вызываться довольно часто, то 161 | новая композиция создастся, а старая не сможет очиститься из-за ссылки на viewModel. Это может привести к утечкам. 162 | Тогда должен возникнуть логичный вопрос почему утечка не возникает всегда, а только при передаче в дочерние 163 | composable. Согласно документации: 164 | 165 | _ViewModels have a longer lifetime than the Composition because they survive configuration changes. They can follow the 166 | lifecycle of the host of Compose content–that is, activities or fragments–or the lifecycle of a destination or the 167 | Navigation graph if you're using the Navigation library. Because of their longer lifetime, ViewModels should not hold 168 | long-lived references to state bound to the lifetime of the Composition. If they do, it could cause memory leaks._ 169 | 170 | ViewModel может следить за жизненным циклом _host of Compose content_, к чему относятся активити, фрагменты или 171 | **_место назначения навигации_**. 172 | 173 | #### 13) Как избежать вызова рекомпозиции всех элементов списка при добавлении одного нового элемента? 174 | 175 | [Link1 - Add extra information to help smart recompositions](https://developer.android.com/jetpack/compose/lifecycle#add-info-smart-recomposition) 176 | 177 | Если не добавить key для каждого элемента списка, при добавлении нового элемента компилятор не будет знать, где какой 178 | элемент, и ему придется сделать полную рекомпозицию списка. 179 | 180 | Обертка блока кода в `key(id) {}`, позволит идентифицировать этот экземпляр в композиции. Значение ключа не обязательно 181 | должно быть глобально уникальным, оно должно быть уникальным только среди экземпляров этого блока кода. 182 | 183 | #### 14) За что отвечает аннотация @Stable 184 | 185 | В случае если composable уже включен в композицию (был вызван, инициализирован, отрисован), он может пропустить 186 | рекомпозицию, если все входные данные стабильны и не изменились. 187 | 188 | Что значит стабилен? 189 | 190 | - Результат _equals_ для двух экземпляров всегда будет одинаковым для одних и тех же двух экземпляров. 191 | - Если _публичное_ свойство изменится, Composition будет уведомлено. 192 | - Все публичные свойства также стабильны. 193 | 194 | Есть несколько типов, которые компилятор Compose считает по дефолту стабильными, даже не смотря на то, что они не 195 | помечены как @Stable: 196 | 197 | - Все типы примитивных значений: Boolean, Int, Long, Float, Char 198 | - Strings 199 | - Все типы функций (лямбды) 200 | 201 | Compose пропускает рекомпозицию компонуемого, если все входные данные стабильны и не изменились. Сравнение использует _ 202 | equals_ метод. 203 | 204 | Compose считает тип стабильным только в том случае, если он может это доказать. Например, интерфейс обычно считается 205 | нестабильным, и типы с изменяемыми общедоступными свойствами, реализация которых может быть неизменной, также не 206 | являются стабильными. 207 | 208 | Если Compose не может определить, что тип является стабильным, но вы хотите, чтобы Compose считал его стабильным, 209 | пометьте его _@Stable_ аннотацией. 210 | 211 | #### 15) Как добавить отступы между элементами списка? 212 | 213 | 1) Arrangement.spacedBy 214 | 215 | ```Kotlin 216 | LazyRow( 217 | horizontalArrangement = Arrangement.spacedBy(8.dp), 218 | contentPadding = PaddingValues(horizontal = 16.dp), 219 | modifier = modifier 220 | ) { 221 | items(alignYourBodyData) { item -> 222 | AlignYourBodyElement(item.drawable, item.text) 223 | } 224 | } 225 | ``` 226 | 227 | 2) Добавить к каждому элементу Space() 228 | 3) Modifier.padding() 229 | 230 | #### 16) Когда мы работаем с ViewGroup, большая вложенность элементов приводит к частому измерению размеров, и уменьшает производительность. Сохраняется ли такая проблема в Compose? 231 | 232 | Compose позволяет избежать множественных измерений, вы можете вкладывать их сколь угодно глубоко, не влияя на 233 | производительность. Compose эффективно обрабатывает вложенные макеты, что делает их отличным способом разработки 234 | сложного пользовательского интерфейса. Это улучшение по сравнению с Android Views, где вам нужно избегать вложенных 235 | макетов из соображений производительности. 236 | 237 | При компоновке дерево пользовательского интерфейса компонуется за один проход. Каждому компоненту сначала предлагается 238 | измерить себя, а затем рекурсивно измерить любые дочерние компоненты, передавая ограничения размера вниз по дереву 239 | дочерним элементам. Затем размеры и размещение компонентов определяются, а разрешенные размеры и инструкции по 240 | размещению передаются обратно вверх по дереву. 241 | 242 | ![img.png](measurement_order.png) 243 | 244 | #### 17) Можно ли изменить количество измерений размеров для компоуз элементов? 245 | 246 | Да 247 | 248 | [Link 1](https://developer.android.com/jetpack/compose/layouts/intrinsic-measurements) 249 | 250 | [Link 2](https://youtu.be/zMKMwh9gZuI?t=1047) 251 | 252 | #### 18) Как создать свой Layout (ViewGroup)? 253 | 254 | Используем composable элемент _Layout()_. Нужно передать MeasurePolicy, в которой прописаны правила расстановки 255 | элементов на экране. 256 | 257 | ```Kotlin 258 | Layout( 259 | modifier = modifier, 260 | content = content, 261 | measurePolicy = { measurables, constraints -> 262 | // проводим измерение для каждого элемента, который мы передали в content 263 | val placements = measurables.map { it.measure(constraints) } 264 | 265 | // создаем layout с максимальными из предоставленных границ 266 | layout(constraints.maxWidth, constraints.maxHeight) { 267 | var y = 0 268 | 269 | // последовательно помещаем элементы в списке (как в Column) 270 | placements.forEach { 271 | it.placeRelative(0, y) 272 | y += it.height 273 | } 274 | } 275 | }) 276 | ``` 277 | 278 | ![img.png](layout.png) 279 | 280 | #### 19) Что такое CompositionLocal? 281 | 282 | _CompositionLocal_ — это инструмент для неявной передачи данных через композицию. 283 | 284 | Чтобы избежать необходимости передавать цвета в качестве параметра для большинства функций, Compose предлагает 285 | возможность создавать именованные объекты в области дерева, которые можно использовать в качестве неявного способа 286 | передачи данных через дерево пользовательского интерфейса. 287 | 288 | Material Theme использует CompositionLocal под капотом. MaterialTheme — это объект, предоставляющий три CompositionLocal 289 | экземпляра — цвета, типографику и формы — что позволяет вам получить их позже в любой дочерней части композиции. 290 | 291 | Задать новое значение какого то ключа можно через CompositionLocalProvider и provides 292 | 293 | ``` 294 | CompositionLocalProvider(SomeKey provides SomeValue) 295 | ``` 296 | 297 | а получить через 298 | 299 | ```Kotlin 300 | val resources = LocalContext.current.resources 301 | ``` 302 | 303 | CompositionLocal создает неявные зависимости, для объектов которые их используют, и может привести к неожиданному поведению. 304 | Он не соответвует паттерну SSOT(single source of truth), поскольку может видоизменяться в любой части композиции. 305 | Таким образом, отладка приложения при возникновении проблемы может быть более сложной задачей, 306 | поскольку вам нужно перейти вверх по композиции, чтобы увидеть, где было предоставлено новое значение. 307 | Поэтому не рекомендуется чрезмерное использование CompositionLocal. 308 | Для дебага подобных кейсов можно использовать [Compose Layout Inspector](https://developer.android.com/jetpack/compose/tooling#layout-inspector) 309 | 310 | 311 | #### 20) compositionLocalOf vs staticCompositionLocalOf 312 | 313 | Оба являются API для создания CompositionLocal. 314 | Если маловероятно, что значение, CompositionLocal изменится, используйте staticCompositionLocalOf для получения преимуществ в производительности. 315 | 316 | _compositionLocalOf_ - изменение значения, предоставленного во время перекомпоновки, делает недействительным только то содержимое, которое считывает значение CompositionLocal. 317 | 318 | _staticCompositionLocalOf_ - staticCompositionLocalOf не следит кто из компонентов считывает его значение. Изменение значения приводит к перекомпоновке 319 | всего контента, в котором CompositionLocal предоставляется. 320 | 321 | 322 | #### 21) The three phases of a frame 323 | [Link](https://developer.android.com/jetpack/compose/phases) 324 | 325 | ![img.png](frame_phases.png) 326 | 327 | _Composition_ - _что_ показать. Запускает компонуемые функции и создает описание пользовательского интерфейса. 328 | 329 | _Layout_ - _где_ показать. Состоит из двух шагов - измерение и размещения (measurement and placement). 330 | Элементы измеряют и размещают себя и любые дочерние элементы в 2D-координатах для каждого узла в дереве компоновки. 331 | 332 | _Drawing_ - _как_ отрисовать. Сама отрисовка. 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /src/compose/ru/composition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/ru/composition.png -------------------------------------------------------------------------------- /src/compose/ru/frame_phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/ru/frame_phases.png -------------------------------------------------------------------------------- /src/compose/ru/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/ru/layout.png -------------------------------------------------------------------------------- /src/compose/ru/measurement_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/ru/measurement_order.png -------------------------------------------------------------------------------- /src/compose/ru/state_hoisting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/compose/ru/state_hoisting.png -------------------------------------------------------------------------------- /src/coroutines/CoroutinesPracticeQuestions.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.flow.SharingStarted 5 | import kotlinx.coroutines.flow.flow 6 | import kotlinx.coroutines.flow.shareIn 7 | import kotlinx.coroutines.sync.Mutex 8 | import kotlinx.coroutines.sync.withLock 9 | import utils.Activity 10 | import java.lang.RuntimeException 11 | import java.util.concurrent.Executors 12 | import java.util.concurrent.TimeUnit 13 | import kotlin.coroutines.CoroutineContext 14 | import kotlin.coroutines.EmptyCoroutineContext 15 | 16 | // Answers here ( https://github.com/VeraUvads/Android-Notes/blob/18127f03c878f8717f844c4809e0308fd192c7b6/src/coroutines/eng/CoroutinesPracticeAnswers_eng.md ) 17 | // Ответы по ссылке ( https://github.com/VeraUvads/Android-Notes/blob/18127f03c878f8717f844c4809e0308fd192c7b6/src/coroutines/ru/CoroutinesPracticeAnswers_ru.md ) 18 | 19 | /**1) If we run this code on a quad-core processor, what should we see? **/ 20 | /**1) Если мы запустим этот код на четырех ядерном процессоре, что интересного мы увидим? **/ 21 | 22 | class Dispatcher1 { 23 | init { 24 | start(Dispatchers.Default) 25 | start(Dispatchers.IO) 26 | EmptyCoroutineContext 27 | start(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) 28 | } 29 | 30 | private fun start(dispatcher: CoroutineDispatcher) = runBlocking { 31 | val jobList = mutableListOf() 32 | val scope = CoroutineScope( 33 | dispatcher 34 | ) 35 | 36 | repeat(6) { 37 | val job = scope.launch { 38 | println("coroutine $it, start $dispatcher") 39 | TimeUnit.MILLISECONDS.sleep(100) 40 | println("coroutine $it, end $dispatcher") 41 | } 42 | jobList.add(job) 43 | } 44 | jobList.joinAll() 45 | } 46 | } 47 | 48 | /**2) What is wrong with this code? **/ 49 | /**2) Какая ошибка допущена? **/ 50 | class Deferred1 { 51 | init { 52 | start() 53 | } 54 | 55 | private fun start() = runBlocking() { 56 | val deferredList = mutableListOf>() 57 | repeat(6) { 58 | val deffer = async(start = CoroutineStart.LAZY) { 59 | delay(2000) 60 | "String $it".also { 61 | println(it) 62 | } 63 | } 64 | deferredList.add(deffer) 65 | } 66 | deferredList.map { it.await() } 67 | } 68 | 69 | } 70 | 71 | suspend fun main() = sharedFlow() 72 | 73 | 74 | /**3) Fix the code **/ 75 | /**3) Исправьте код **/ 76 | class Lifecycle1 { 77 | init { 78 | MainActivity() 79 | } 80 | 81 | internal class MainActivity : Activity() { 82 | private val viewModel = ViewModel() 83 | 84 | override fun onCreate() { 85 | onClickToSmth { 86 | lifecycleScope.async { viewModel.callApi() } 87 | } 88 | } 89 | } 90 | 91 | internal class ViewModel { 92 | suspend fun callApi() { 93 | println("Job started") 94 | delay(2000) 95 | println("Job finished") 96 | } 97 | } 98 | } 99 | 100 | 101 | /**4) Write code with coroutines deadlock **/ 102 | /**4) Напишите код который приведет к deadlock **/ 103 | 104 | /**5) What will be printed? **/ 105 | 106 | suspend fun exceptionHandling1() = coroutineScope { 107 | val job = launch(SupervisorJob()) { 108 | val child1 = launch { 109 | delay(1000L) 110 | println("child 1") 111 | throw RuntimeException() 112 | } 113 | val child2 = launch { 114 | delay(2000L) 115 | println("child 2") 116 | } 117 | joinAll(child1, child2) 118 | } 119 | job.join() 120 | } 121 | 122 | 123 | /**6) How can we solve problem with consistent result? **/ 124 | 125 | suspend fun massiveRun() { 126 | var counter = 0 127 | withContext(Dispatchers.Default) { 128 | repeat(1000) { 129 | launch { 130 | counter++; 131 | } 132 | } 133 | } 134 | println(counter) 135 | } 136 | 137 | /**7) How can I modify the code to receive the previous 5 values after subscribing?**/ 138 | 139 | suspend fun sharedFlow(): Unit = coroutineScope { 140 | val flow = flow { 141 | repeat(10) { 142 | delay(1000L) 143 | emit(it) 144 | } 145 | } 146 | delay(6000) 147 | launch { 148 | flow.collect { 149 | println(it) 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/coroutines/eng/CoroutinesPracticeAnswers_eng.md: -------------------------------------------------------------------------------- 1 | ## Answers for [coroutines practice](https://github.com/VeraUvads/Android-Notes/blob/398ce6ef0354a023ea39919c581cb9502ea630e0/src/coroutines/eng/CoroutinesTheory_eng.md) 2 | 3 | #### 1) If we run Dispatcher1 on a quad-core processor, what should we see? 4 | 5 | *Dispatchers.Default* - By default, the maximum number of threads used by this dispatcher is equal to the number of CPU 6 | cores. Coroutines 0,1,2,3 will start immediately. All threads are busy. Coroutines 4,5 coroutines will be launched when 7 | two the previous one finish their work and release threads; 8 | 9 | *Dispatchers.IO* - every Job starts immediately; 10 | 11 | *newSingleThreadExecutor* - 6 coroutines will come to the dispatcher, which has only one thread. Coroutines had to line 12 | up in a queue and be executed sequentially. 13 | 14 | #### 2) What is wrong with Deferred1? 15 | 16 | *deferredList.map { it.await() }* not the same with *deferredList.awaitAll()* 17 | 18 | The code with *map* will suspend the coroutine on each call and execute all the functions in sequence. 19 | *awaitAll* will do all Deferreds independently. 20 | 21 | #### 3) Fix the Lifecycle1. 22 | 23 | There are two mistakes. 24 | 25 | 1) When the activity is destroyed (as example, when we rotate the screen), lifecycle scope will be cancelled. We will 26 | lose result of our API call. To resolve first problem, we should call our api from *viewModelScope*. It will survive 27 | after rotation, and we will receive our data. 28 | 2) We shouldn't handle network calls on Dispatchers.Default. We have Dispatchers.IO for that goals. Use withContext( 29 | Dispatchers.IO) for API call. 30 | 31 | ```Kotlin 32 | class Lifecycle1 { 33 | internal class MainActivity { 34 | 35 | private fun onCreate() { 36 | onClick { 37 | viewModel.callApi() 38 | } 39 | } 40 | } 41 | 42 | 43 | internal class ViewModel { 44 | fun callApi() { 45 | viewModelScope.launch { 46 | withContext(Dispatchers.IO) { 47 | println("Job started") 48 | delay(2000) 49 | println("Job finished") 50 | } 51 | } 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | #### 4) Deadlock with coroutine 58 | 59 | Many ways, two of them: 60 | 61 | ```Kotlin 62 | 63 | fun coroutinesDeadlock1() { 64 | var first: Job = Job() 65 | var second: Job = Job() 66 | runBlocking() { 67 | first = launch(start = CoroutineStart.LAZY) { 68 | println("first before join") 69 | second.join() 70 | println("first after join") 71 | } 72 | second = launch(start = CoroutineStart.LAZY) { 73 | println("second before join") 74 | first.join() 75 | println("second after join") 76 | } 77 | joinAll(first, second) 78 | } 79 | } 80 | 81 | 82 | fun coroutinesDeadlock2() { 83 | var third: Job = Job() 84 | runBlocking() { 85 | third = launch() { 86 | println("third before join") 87 | third.join() 88 | println("third after join") 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | 5) What will be printed? 95 | 1. Child 1 96 | 2. Error stacktrace 97 | 98 | "Child 2" will never be printed, because SupervisorJob works only for child coroutines; Is we want not to cancel our 99 | second Job we should use the supervisor job as shared; 100 | 101 | ```Kotlin 102 | val sharedJob = SupervisorJob() 103 | launch { 104 | val child1 = launch(sharedJob) { 105 | delay(1000L) 106 | println("child 1") 107 | throw RuntimeException() 108 | } 109 | val child2 = launch(sharedJob) { 110 | delay(2000L) 111 | println("child 2") 112 | } 113 | child1.join() 114 | child2.join() 115 | }.join() 116 | ``` 117 | 118 | or 119 | 120 | ```Kotlin 121 | suspend fun exceptionHandling() = coroutineScope { 122 | supervisorScope { 123 | launch { 124 | delay(1000L) 125 | println("child 1") 126 | throw RuntimeException() 127 | } 128 | launch { 129 | delay(2000L) 130 | println("child 2") 131 | } 132 | } 133 | } 134 | ``` 135 | 136 | 6) How can we solve problem with consistent result? 137 | 138 | ```Kotlin 139 | suspend fun massiveRunAnswer() { 140 | var counter = 0 141 | val mutex = Mutex() 142 | withContext(Dispatchers.Default) { 143 | repeat(1000) { 144 | launch { 145 | mutex.withLock { 146 | counter++ 147 | } 148 | } 149 | } 150 | } 151 | println(counter) 152 | } 153 | ``` 154 | 155 | 7) How can I modify the code to receive the previous 5 values after subscribing? 156 | 157 | Flow is a cold stream. We can convert it to a hot stream using _.stateIn_ or _.shareIn_; 158 | _stateIn_ will return a _StateFlow_ and can emit only one previous value, while _shareIn_ will return a _SharedFlow_. we 159 | can define replay policy 160 | 161 | ```Kotlin 162 | 163 | suspend fun sharedFlow(): Unit = coroutineScope { 164 | val flow = flow { 165 | repeat(10) { 166 | delay(1000L) 167 | emit(it) 168 | } 169 | }.shareIn(scope = this, replay = 5, started = SharingStarted.Eargely) 170 | delay(6000) 171 | launch { 172 | flow.collect { 173 | println(it) 174 | } 175 | } 176 | } 177 | 178 | ``` 179 | -------------------------------------------------------------------------------- /src/coroutines/eng/CoroutinesTheory_eng.md: -------------------------------------------------------------------------------- 1 | #### Difference between a "coroutine" and a "thread"? 2 | 3 | 1) Coroutines are a form of sequential processing: only one is executing at any given time. Threads are a form of 4 | concurrent processing: multiple threads may be executing at any given time. 5 | 2) The operating system switches running threads according to its scheduler, which is an algorithm in the operating 6 | system kernel. With coroutines, the programmer and programming language determine when to switch coroutines 7 | 3) Coroutines can provide a very high level of concurrency with very little overhead. Generally in a threaded 8 | environment you have at most 30-50 threads before the amount of overhead wasted actually scheduling these threads 9 | (by the system scheduler) significantly cuts into the amount of time the threads actually do useful work. 10 | 4) Coroutines run within a single thread or pool of threads. 11 | 12 | *** 13 | #### Difference between *process* and *tread* 14 | Процесс — экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: один процесс не может получить доступ к переменным и структурам данных другого. Если процесс хочет получить доступ к чужим ресурсам, необходимо использовать межпроцессное взаимодействие. Это могут быть конвейеры, файлы, каналы связи между компьютерами и многое другое. 15 | 16 | Поток использует то же самое пространства стека, что и процесс, а множество потоков совместно используют данные своих состояний. Как правило, каждый поток может работать (читать и писать) с одной и той же областью памяти, в отличие от процессов, которые не могут просто так получить доступ к памяти другого процесса. У каждого потока есть собственные регистры и собственный стек, но другие потоки могут их использовать. 17 | 18 | Поток — определенный способ выполнения процесса. Когда один поток изменяет ресурс процесса, это изменение сразу же становится видно другим потокам этого процесса. 19 | 20 | 21 | *** 22 | 23 | #### What are the main components of coroutines? Describe their role 24 | 25 | *Job, Context, Dispatcher, Continuation и CoroutineScope* 26 | 27 | *Job* - Stores the state of the coroutine: active / canceled / completed. These states change as the coroutine runs. But 28 | we also use coroutine state products on their own: to cancel the coroutine or start deferring the coroutine. Jobs can be 29 | arranged into parent-child hierarchies where cancellation of a parent leads to immediate cancellation of all its 30 | children recursively. 31 | 32 | *CoroutineContext* - collection of unique Key-Element values. Should contain an instance of a job to enforce structured 33 | concurrency. 34 | 35 | *Dispatcher* - determines what thread or threads the coroutine uses for execution. The coroutine dispatcher can confine coroutine execution to a specific thread, 36 | dispatch it to a thread pool, or let it run unconfined. 37 | 38 | [//]: # (TODO) 39 | *CoroutineScope* - 40 | 41 | *Continuation* - 42 | 43 | *** 44 | 45 | #### How the main components of coroutines depend on each other 46 | 47 | [//]: # (TODO) 48 | 49 | *CoroutineScope* store *CoroutineContext*. CoroutineContext it is a map with different objects that implement 50 | CoroutineContext.Element. As example: Job, Dispatcher, 51 | 52 | *** 53 | 54 | #### Coroutine exceptions handling 55 | 56 | a) 57 | 58 | ```Kotlin 59 | try { 60 | doSmthSuspend() 61 | } catch (exception: Exception) { 62 | if (exception is CancellationException) { 63 | throw exception 64 | } 65 | } 66 | ``` 67 | 68 | b) 69 | 70 | ```Kotlin 71 | val handler = CoroutineExceptionHandler { _, exception -> 72 | println("CoroutineExceptionHandler got $exception") 73 | } 74 | 75 | launch(handler) { 76 | throw AssertionError() 77 | } 78 | 79 | CoroutineExceptionHandler got java.lang.AssertionError 80 | ``` 81 | 82 | *** 83 | 84 | 85 | 86 | 87 | #### Why is it forbidden to catch CancellationException? 88 | 89 | [//]: # (TODO) 90 | 91 | *** 92 | 93 | #### What is suspension point? How coroutines state machine will divide this code? 94 | [//]: # (TODO) 95 | 96 | [//]: # (Suspension points are points in code which either end your program early (mostly bad paths in programs), or which start some work on the side, in another routine which is suspended, ultimately notifying you of the end result, and allowing you to continue where you left off.) 97 | 98 | ```Kotlin 99 | launch { 100 | doSmthSuspend1() 101 | toast("First task completed") 102 | doSmthSuspend2() 103 | toast("Second task completed") 104 | } 105 | 106 | 107 | ``` 108 | 109 | 110 | 111 | 1. doSmthSuspend1 and everything above 112 | 2. doSmthSuspend2 and everything between it and doSmthSuspend1 113 | 3. everything after doSmthSuspend2 114 | 115 | *** 116 | 117 | #### How continuation.resume() set parameters to next continuation? 118 | 119 | [//]: # (TODO поправить ошибку в логике) 120 | 121 | ```Kotlin 122 | launch { 123 | val param = buildParam() 124 | val firstResult = doSmthSuspend1(param) 125 | doSmthSuspend2(firstResult) 126 | } 127 | ``` 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | continuation.resume(param) 138 | 139 | *param* will be cast to required type 140 | 141 | ``` 142 | Object invokeSuspend(Object result) { 143 | switch (label) { 144 | case 0: { 145 | label = 1; 146 | result = buildParam(); 147 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 148 | } 149 | case 1: { 150 | param = (String) result; 151 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 152 | } 153 | } 154 | } 155 | ``` 156 | 157 | *** 158 | 159 | #### Difference between async and launch 160 | 161 | *Launch* - Returns a Job and does not carry any resulting value. 162 | 163 | *Async* - Creates a coroutine and returns its future result as an implementation of Deferred. It blocks the current 164 | thread at the entry point of the *await()*. 165 | 166 | *** 167 | 168 | #### Job types 169 | 170 | *Job* - Creates a job object in an active state. A failure of any child of this job immediately causes 171 | this job to fail, too, and cancels the rest of its children. 172 | 173 | *SupervisorJob* -To handle children failure independently of each other use SupervisorJob. 174 | Children of a supervisor job can fail independently of each other. If parent job is specified, 175 | then this supervisor job becomes a child job of its parent and is cancelled when its parent fails or is cancelled. 176 | All this supervisor's children are cancelled in this case, too. The invocation of cancel with exception 177 | (other than CancellationException) on this supervisor job also cancels parent. 178 | 179 | *Deferred* - it is a Job with a result. Deferred has the same state machine as the Job with additional convenience 180 | methods to retrieve the successful or failed result of the computation that was carried out. 181 | The result of the deferred is available when it is completed and can be retrieved by await method, 182 | which throws an exception if the deferred had failed. 183 | 184 | 185 | *** 186 | 187 | #### Join, JoinAll, Await, AwaitAll 188 | 189 | *Join* - Suspends current coroutine until the job completes. 190 | 191 | *JoinAll* - Suspends current coroutine until all given jobs are complete. This method is semantically equivalent to 192 | joining all given jobs one by one with 193 | 194 | ```Kotlin 195 | jobs.forEach { it.join() } 196 | ``` 197 | 198 | *Await* - Deferred extension. Work with async. Awaits for completion of the promise without blocking. Return coroutine 199 | result. 200 | 201 | *AwaitAll* - Deferred extension. Work with async. Awaits for completion of given deferred values without blocking a 202 | thread. Resumes normally with the list of values when all deferred works are completed or resumes with the first thrown 203 | exception (including cancellation). 204 | 205 | ```Kotlin 206 | val results: List = listOf>().awaitAll() 207 | ``` 208 | 209 | *** 210 | 211 | #### What is CoroutineStart? Which types do you know? 212 | 213 | ```Kotlin 214 | fun CoroutineScope.launch( 215 | context: CoroutineContext = EmptyCoroutineContext, 216 | start: CoroutineStart = CoroutineStart.DEFAULT, 217 | block: suspend CoroutineScope.() -> Unit 218 | ): Job 219 | ``` 220 | 221 | *CoroutineStart* defines start options for coroutines builders. It is used in start parameter of launch, async, and 222 | other coroutine builder functions. 223 | 224 | *DEFAULT* - immediately schedules coroutine for execution according to its context; 225 | 226 | *ATOMIC* - atomically (in a non-cancellable way) schedules coroutine for execution according to its context; 227 | 228 | *LAZY* - starts coroutine lazily, only when it is needed; 229 | 230 | *UNDISPATCHED* - immediately executes coroutine until its first suspension point in the current thread. 231 | 232 | *** 233 | 234 | #### How to cancel coroutine? What is ensureActive? 235 | 236 | We can cancel job, and have to ensure that current scope is active by using *isActive* or *ensureActive()* 237 | 238 | ensureActive() - If the job is no longer active, throws CancellationException. This method is a drop-in replacement for 239 | the following code, but with more precise exception: 240 | 241 | ```Kotlin 242 | if (!isActive) { 243 | throw CancellationException() 244 | } 245 | ``` 246 | 247 | ```Kotlin 248 | val job = CoroutineScope.launch { 249 | ensureActive() 250 | doSmth() 251 | } 252 | job.cancel() 253 | ``` 254 | 255 | *** 256 | 257 | #### How to put custom data to CoroutineContext 258 | To coroutine Context we can put CoroutineContext.Element implementation. AbstractCoroutineContextElement - base class 259 | for CoroutineContext.Element implementations. 260 | 261 | ```Kotlin 262 | data class SharedData( 263 | val sharedInfo: Long, 264 | ) : AbstractCoroutineContextElement(UserData) { 265 | companion object Key : CoroutineContext.Key 266 | } 267 | 268 | //then scope could be created as 269 | val scope = CoroutineScope(Job() + Dispatchers.Default + SharedData("I have a secret for you")) 270 | ``` 271 | 272 | *** 273 | #### What is CoroutineDispatcher? Which types do you know? 274 | 275 | The coroutine context includes a coroutine dispatcher that determines what thread or threads the coroutine uses for execution. 276 | The coroutine dispatcher can confine coroutine execution to a specific thread, 277 | dispatch it to a thread pool, or let it run unconfined. 278 | 279 | *Dispatchers.Default* - It uses a common pool of shared background threads. It is backed by a shared pool 280 | of threads on JVM. By default, the maximum number of threads used by this dispatcher is equal 281 | to the number of CPU cores, but is at least two. Using for intensive computing. 282 | 283 | *Dispatchers.IO* - uses a shared pool of on-demand created threads and is designed for blocking operations. 284 | The thread limit is 64 (or more if processor cores are more than 64). Using for write/read/network work. 285 | 286 | *Dispatchers.Unconfined* - is not confined to any specific thread. It executes the initial continuation 287 | of a coroutine in the current call-frame and lets the coroutine resume in whatever thread that is used 288 | by the corresponding suspending function, without mandating any specific threading policy. 289 | Nested coroutines launched in this dispatcher form an event-loop to avoid stack overflows; 290 | 291 | 292 | *** 293 | #### Why is Default not suitable for IO operations? 294 | 295 | 296 | 297 | *** 298 | #### How to create private thread pool? 299 | 300 | [//]: # (TODO) 301 | Private thread pools can be created with *newSingleThreadContext* and *newFixedThreadPoolContext*. 302 | 303 | *** 304 | #### What is ContinuationInterceptor? 305 | 306 | [//]: # (TODO) 307 | 308 | 309 | *** 310 | 311 | #### What is Flow? When we have to use it? 312 | 313 | [//]: # (TODO) 314 | 315 | 316 | *** 317 | 318 | #### Which coroutines will be cancelled? 319 | ![img.png](cancellation.png) 320 | 321 | 322 | -------------------------------------------------------------------------------- /src/coroutines/eng/cancellation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VeraUvads/Android-Notes/df411b624b2271e346384d47d7970d4f0fd114ee/src/coroutines/eng/cancellation.png -------------------------------------------------------------------------------- /src/coroutines/ru/CoroutinesPracticeAnswers_ru.md: -------------------------------------------------------------------------------- 1 | ##Ответы для [coroutines practice](https://github.com/VeraUvads/Android-Notes/blob/398ce6ef0354a023ea39919c581cb9502ea630e0/src/coroutines/eng/CoroutinesTheory_eng.md) 2 | 3 | Coming soon 4 | 5 | -------------------------------------------------------------------------------- /src/coroutines/ru/CoroutinesTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### Разница между корутинами тредами 2 | 3 | 1) Корутины — это форма последовательной обработки: в любой момент времени выполняется только одна. Треды представляют 4 | собой форму параллельной обработки: несколько потоков могут выполняться в любой момент времени. 5 | 2) Операционная система переключает запущенные потоки в соответствии со своим планировщиком, который управляется 6 | алгоритмом в ядре ОС. С корутинами программист и язык программирования определяют, когда переключать работу. 7 | 3) Корутины могут обеспечить очень высокий уровень параллелизма с очень небольшими накладными расходами. Как правило, в 8 | многопоточной среде у вас есть не более 30-50 потоков, прежде чем количество накладных расходов, потраченных впустую 9 | на фактическое планирование этих потоков 10 | (системным планировщиком), значительно сократит количество времени, в течение которого потоки действительно выполняют 11 | полезную работу. 12 | 4) Корутины выполняются в одном потоке или пуле потоков. 13 | 14 | *** 15 | 16 | #### Какие основные составляющие компоненты корутин вы знаете. Опишите их роль 17 | 18 | *Job, Context, Dispatcher и CoroutineScope* 19 | 20 | *Job* - Хранит состояние корутины: активно/отменено/завершено. Эти состояния меняются по мере выполнения корутины. Мы 21 | также можем менять состояния корутины самостоятельно: чтобы отменить корутину или начать создать отложенную корутины. * 22 | Job* может быть организована в иерархии родитель-потомок, где отмена родителя приводит к немедленной отмене всех его 23 | детей рекурсивно. 24 | 25 | *CoroutineContext* - коллекция уникальных значений Key-Element. По контракту должен содержать экземпляр Job для 26 | обеспечения структурированного параллелизма. 27 | 28 | [//]: # (TODO) 29 | *Dispatcher* - 30 | 31 | *CoroutineScope* - 32 | 33 | *Continuation* - 34 | 35 | *** 36 | 37 | #### Как основные компоненты корутин зависят друг от друга 38 | 39 | [//]: # (TODO) 40 | 41 | *** 42 | 43 | #### Обработка исключений корутин 44 | 45 | a) 46 | 47 | ```Kotlin 48 | try { 49 | doSmthSuspend() 50 | } catch (exception: Exception) { 51 | if (exception is CancellationException) { 52 | throw exception 53 | } 54 | } 55 | ``` 56 | 57 | b) 58 | 59 | ```Kotlin 60 | val handler = CoroutineExceptionHandler { _, exception -> 61 | println("CoroutineExceptionHandler got $exception") 62 | } 63 | 64 | launch(handler) { 65 | throw AssertionError() 66 | } 67 | 68 | CoroutineExceptionHandler got java.lang.AssertionError 69 | ``` 70 | 71 | *** 72 | 73 | #### Почему нельзя перехватывать CancellationException? 74 | 75 | [//]: # (TODO) 76 | 77 | *** 78 | 79 | #### Что такое suspension points? Как стейт машина корутин разделит этот код? 80 | 81 | [//]: # (TODO) 82 | 83 | [//]: # (Suspension points are points in code which either end your program early (mostly bad paths in programs), or which start some work on the side, in another routine which is suspended, ultimately notifying you of the end result, and allowing you to continue where you left off.) 84 | 85 | ```Kotlin 86 | launch { 87 | doSmthSuspend1() 88 | toast("First task completed") 89 | doSmthSuspend2() 90 | toast("Second task completed") 91 | } 92 | 93 | 94 | ``` 95 | 96 | 1. doSmthSuspend1() и все до 97 | 2. все после doSmthSuspend1() и до doSmthSuspend2() 98 | 3. все после doSmthSuspend2() 99 | 100 | *** 101 | 102 | #### Как continue.resume() передает параметры следующему блоку стейт машины? 103 | 104 | [//]: # (TODO поправить ошибку в логике) 105 | 106 | ```Kotlin 107 | launch { 108 | val param = buildParam() 109 | val firstResult = doSmthSuspend1(param) 110 | doSmthSuspend2(firstResult) 111 | } 112 | ``` 113 | 114 | continuation.resume(param) 115 | 116 | *param* кастуется к нужному параметру 117 | 118 | ``` 119 | Object invokeSuspend(Object result) { 120 | switch (label) { 121 | case 0: { 122 | label = 1; 123 | result = buildParam(); 124 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 125 | } 126 | case 1: { 127 | param = (String) result; 128 | if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED; 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | *** 135 | 136 | #### Разница между async и launch 137 | 138 | *Launch* -Создает и запукает корутину, возвращает Job и не заботится о результате. 139 | 140 | *Async* - Создает и запускает корутину, и возвращает ее будущий результат через реализацию Deferred. Результат можно 141 | получить через *Deferred.await()*. Приостанавливает нынешний тред в точке вызова *await()* и ждет завершение выполнения 142 | корутины. 143 | 144 | *** 145 | 146 | #### Виды Job 147 | 148 | [//]: # (TODO) 149 | 150 | *Job* - 151 | 152 | *Deffered* - 153 | 154 | *SupervisorJob* - 155 | 156 | *** 157 | 158 | #### Join, JoinAll, Await, AwaitAll 159 | 160 | *Join* - Приостанавливает текущую корутины до завершения Job. 161 | 162 | *JoinAll* - Приостанавливает текущую корутину до тех пор, пока все Job не будут выполнены. Этот метод семантически 163 | эквивалентен: 164 | 165 | ```Kotlin 166 | jobs.forEach { it.join() } 167 | ``` 168 | 169 | *Await* - расширение класса Deferred. Ожидает выполнения без блокировки. Возвращает результат корутины. 170 | 171 | *AwaitAll* - расширение класса Deferred. Ожидает выполнения всех Deffered. Continuation.resume вызывается либо со 172 | списком значений, когда все Deffered работы завершены, или с первым брошенным исключением (включая отмену). 173 | 174 | ```Kotlin 175 | val results: List = listOf>().awaitAll() 176 | ``` 177 | 178 | *** 179 | 180 | #### В чем разница между deferreds.map { it.await() } and deferreds.awaitAll(). 181 | 182 | *AwaitAll* это **НЕ** эквивалент для 183 | 184 | ```Kotlin 185 | deferreds.map { it.await() } // it is NOT AwaitAll 186 | ``` 187 | 188 | *awaitAll* - вернет ошибку если хоть одна из *Deffered* выбросит исключение. 189 | 190 | *этот код* - выполнит все корутины, даже если было выброшено исключение в одной из *Deffered*; 191 | 192 | *** 193 | 194 | #### Что такое CoroutineStart? Какие типы бывают? 195 | 196 | ```Kotlin 197 | fun CoroutineScope.launch( 198 | context: CoroutineContext = EmptyCoroutineContext, 199 | start: CoroutineStart = CoroutineStart.DEFAULT, 200 | block: suspend CoroutineScope.() -> Unit 201 | ): Job 202 | ``` 203 | 204 | *CoroutineStart* определяет параметры запуска для билдера корутины. 205 | 206 | *DEFAULT* - создает и запускает корутину немедленно; 207 | 208 | *ATOMIC* - атомарно (без возможности отмены) планирует сопрограмму для выполнения; 209 | 210 | *LAZY* - запускает сопрограмму, только когда к ней обратятся; 211 | 212 | *UNDISPATCHED* - немедленно выполняет сопрограмму до ее первой точки приостановки в текущем потоке. 213 | 214 | *** 215 | 216 | #### Как отменить корутину? Что такое ensureActive? 217 | 218 | Мы можем отменить job, и должны убедиться, что текущий scope активен, используя *isActive* или *ensureActive()* 219 | 220 | ensureActive() - Если job больше не активна, бросает CancellationException. Этот метод является заменой 221 | следующего кода, но с более точным исключением: 222 | 223 | ```Kotlin 224 | if (!isActive) { 225 | throw CancellationException() 226 | } 227 | ``` 228 | 229 | ```Kotlin 230 | val job = CoroutineScope.launch { 231 | ensureActive() 232 | doSmth() 233 | } 234 | job.cancel() 235 | ``` 236 | 237 | *** 238 | 239 | #### Как поместить дополнительные данные в CoroutineContext? 240 | 241 | В CoroutineContext мы можем поместить классы реализующие CoroutineContext.Element. AbstractCoroutineContextElement - абстрактный класс 242 | для реализации CoroutineContext.Element. 243 | 244 | ```Kotlin 245 | data class SharedData( 246 | val sharedInfo: Long, 247 | ) : AbstractCoroutineContextElement(UserData) { 248 | companion object Key : CoroutineContext.Key 249 | } 250 | 251 | //then scope could be created as 252 | val scope = CoroutineScope(Job() + Dispatchers.Default + SharedData("I have a secret for you")) 253 | ``` 254 | -------------------------------------------------------------------------------- /src/di/ru/DI_ru.md: -------------------------------------------------------------------------------- 1 | #### Dagger vs Koin 2 | 3 | [Link](https://proandroiddev.com/how-dagger-hilt-and-koin-differ-under-the-hood-c3be1a2959d7) 4 | 5 | *** 6 | 7 | #### ServiceLocator vs DI 8 | 9 | *** 10 | 11 | #### Основные компоненты в DI? 12 | 13 | *Module* - это классы, в которые мы выносим код создания объектов. Обычно каждый модуль включает в себя создание 14 | объектов близких по смыслу. 15 | 16 | *Component* - это посредник между Activity и модулями. Когда Activity нужен какой-либо объект, она сообщает об этом 17 | компоненту. Компонент знает, какой модуль умеет создавать такой объект, просит модуль создать объект, и передает его в 18 | Activity. При этом компонент может использовать другие модули, чтобы создать всю иерархию объектов, необходимую для 19 | создания искомого объекта. 20 | 21 | *Dependencies* - 22 | 23 | *** 24 | 25 | ### Dagger 26 | 27 | #### Аннотации в Dagger 28 | 29 | *@Module* - сообщаем даггеру, что этот класс является модулем, который вносит свой вклад в граф объектов. Модуль 30 | предоставляет зависимости. Все includes последовательно вносятся в граф объектов. Модуль содержит в себе информацию КАК 31 | создать объект. 32 | 33 | *@Component* - сообщаем даггеру, что этот класс является компонентом. Компонент — это единица, которая в основном 34 | используется для разрешения зависимостей. Посредник между классом который запрашивает и использует зависимость, и тем 35 | кто его создает. Компонент знает, какой модуль умеет создавать нужный объект, просит модуль создать объект, и передает 36 | его в Activity. При этом компонент может использовать другие модули, чтобы создать всю иерархию объектов, необходимую 37 | для создания искомого объекта. 38 | 39 | *@Subcomponent* - сообщаем даггеру, что этот класс является сабкомпонентом. Это вид компонента, производный от 40 | компонента и наследующий зависимости, которые он предоставляет. Позволяет логически разделить и обособить логику. 41 | 42 | *@Subcomponent.Builder/@Component.Builder* - помечаем как создать компонент/сабкомпонент 43 | 44 | *@Provides* - указывает, что метод является поставщиком объекта. 45 | 46 | *@Inject* - помечает, что в свойство должна доставиться зависимость. 47 | 48 | *@AssistedInject* - помечает, что не все запрашиваемые параметры есть в графе. Такие параметры мы помечаем как @Assisted 49 | 50 | *@Binds* - привязывание одного типа к другому 51 | 52 | *@BindsInstance* - помечает метод компонента как привязку экземпляра к некоторому ключу в компоненте. 53 | 54 | Example: 55 | 56 | ```Kotlin 57 | class AppComponent { 58 | @Component.Builder 59 | interface Builder { 60 | @BindsInstance 61 | fun context(context: Context): Builder 62 | } 63 | } 64 | 65 | class App : Application() { 66 | override fun onCreate() { 67 | super.onCreate() 68 | appComponent = DaggerAppComponent.builder().context(this).build() 69 | } 70 | } 71 | ``` 72 | 73 | *@Reusable* - позволяет эффективнее хранить объекты, не хранит объект все время в памяти (в отличии от *@Singleton*). 74 | Вместо этого временно сохраняет экземпляр в памяти, позволяя не создавать каждый раз новый экземпляр при обращении, но 75 | не гарантирует что объект всегда будет один. 76 | 77 | *@Scope* - 78 | 79 | *@IntoSet* - позволяет сделать инжект коллекциии Set, для всех элементов одного и того же типа 80 | 81 | Example: 82 | 83 | ```Kotlin 84 | interface AppModule { 85 | 86 | @Binds 87 | @IntoSet 88 | fun orangeToFruit(orange: Orange) : Fruit 89 | 90 | @Binds 91 | @IntoSet 92 | fun appleToFruit(apple: Apple) : Fruit 93 | 94 | } 95 | 96 | class IUseFruits @Inject constructor( 97 | private val fruits: Set<@JvmSuppressWildcards Fruits> // аннотация нужна из-за особенностей пересечения Kotlin и Java 98 | ) 99 | 100 | 101 | ``` 102 | 103 | 104 | *** 105 | 106 | #### Как работает создаение Scope компонента под капотом? 107 | 108 | *** 109 | 110 | #### Почему Dagger Hilt не стоит использовать для многомодульности 111 | Работает на subcomponents 112 | 113 | *** 114 | 115 | #### Lazy vs Scope? 116 | 117 | Lazy - создание и хранение внутри контейнера в котором вызван 118 | 119 | Scope - хранилище на уровне компонента который будет жить намного дольше 120 | 121 | *** 122 | 123 | #### В чем минус Subcomponent? Как разделить логику компонента без использования subcomponent? 124 | 125 | Subcomponent имеет очень жесткую связь с компонентом, родитель всегда четко должен знать про свои сабкомпоненты. При 126 | генерации кода он вкладывает все класы самкомпонентов внутрь класса компонента. Такой подход не подойдет для 127 | модуляризации. 128 | 129 | Для разделения логики можем использовать два component 130 | 131 | 1) Когда один модуль и мы видим appComponent из любой точки кода, можем использовать AppComponent в качестве зависимости 132 | для FeatureComponent 133 | 134 | ```Kotlin 135 | @Component 136 | interface AppComponent 137 | 138 | @Component(dependencies = [AppComponent::class]) 139 | interface FeatureComponent { 140 | 141 | @Component.Builder 142 | interface Builder { 143 | 144 | @BindsInstance 145 | fun appComponent(appComponent: AppComponent): Builder 146 | 147 | fun build(): FeatureComponent 148 | } 149 | } 150 | ``` 151 | 152 | Тогда создание FeatureComponent сводится к 153 | 154 | ```Kotlin 155 | val featureComponent = DaggerFeatureComponent.builder() 156 | .appComponent(appComponent) 157 | .build() 158 | ``` 159 | 160 | Мы должны передать ему экземпляр компонента AppComponent. Только так FeatureComponent сможет получить доступ к объектам AppComponent. 161 | Он просто будет просить их у этого компонента. 162 | 163 | И вот в этом кроется разница между отношениями компонент-сабкомпонент и компонент-компонент. 164 | Когда мы к компоненту добавляем сабкомпонент, то компонент сам создает реализацию этого сабкомпонента. 165 | В эту реализацию он передает свои модули. И сабкомпонент сам может в этих модулях найти все, что ему понадобится. 166 | Т.е. сабкомпоненту неважно, какие объекты прописаны в интерфейсе компонента. 167 | Сабкомпонент лезет напрямую в модули, минуя компонент. 168 | 169 | А вот в случае пары компонент-компонент связь не такая тесная. 170 | Компоненты создаются отдельно друг от друга. Они общаются на уровне интерфейсов и не имеют доступа к модулям. 171 | 172 | 2) Когда (к примеру) AppComponent лежит в app модуле, а FeatureComponent в feature модуле, и у нас нет доступа к 173 | AppComponent. 174 | 175 | ```Kotlin 176 | 177 | interface AppComponent : FeatureDeps { 178 | 179 | override fun application(): Application 180 | 181 | @Builder 182 | interface Builder { 183 | @BindsInstance 184 | fun application(application: Application): Builder 185 | fun build(): AppComponent 186 | } 187 | } 188 | 189 | interface FeatureDeps { 190 | fun application(): Application 191 | } 192 | 193 | @Component(dependencies = [AppComponent::class]) 194 | interface FeatureComponent { 195 | 196 | @Component.Builder 197 | interface Builder { 198 | 199 | @BindsInstance 200 | fun appComponent(appComponent: AppComponent): Builder 201 | fun build(): FeatureComponent 202 | } 203 | } 204 | ``` 205 | 206 | 207 | 208 | *** 209 | -------------------------------------------------------------------------------- /src/gradle/ru/GradleAnswer_ru.md: -------------------------------------------------------------------------------- 1 | 1) Для чего нужен градл? 2 | 3 | 2) Что такое артифакты? 4 | [Link](https://developer.android.com/reference/tools/gradle-api/8.0/com/android/build/api/artifact/Artifacts) 5 | -------------------------------------------------------------------------------- /src/gradle/ru/Gradle_ru.md: -------------------------------------------------------------------------------- 1 | 1) Для чего нужен градл? 2 | 3 | 2) Что такое артифакты? 4 | -------------------------------------------------------------------------------- /src/ktl/KotlinPractice.kt: -------------------------------------------------------------------------------- 1 | package ktl 2 | 3 | import java.util.concurrent.locks.ReentrantLock 4 | import kotlin.concurrent.withLock 5 | 6 | val x = 1 7 | val list = mutableListOf("dsds") 8 | fun main() { 9 | list.toList() 10 | println(list.map(::toInt)) 11 | println(list.map { toInt(it) }) 12 | println(x::dec) 13 | println(::x.isOpen) 14 | } 15 | 16 | fun toInt(s: String): Int { 17 | return s.toInt() 18 | } 19 | 20 | private fun foo() { 21 | val lock = ReentrantLock() 22 | lock.withLock { 23 | 24 | } 25 | } 26 | 27 | fun main2() { 28 | threadsDeadLock() 29 | } 30 | 31 | fun threadsDeadLock() { 32 | val firstThread = FirstThread() 33 | val secondThread = SecondThread() 34 | firstThread.start() 35 | secondThread.start() 36 | } 37 | 38 | var Lock1 = Any() 39 | var Lock2 = Any() 40 | 41 | class FirstThread : Thread() { 42 | override fun run() { 43 | synchronized(Lock1) { 44 | println("Thread 1: Holding lock 1"); 45 | sleep(10) 46 | println("Thread 1: after sleep"); 47 | synchronized(Lock2) { 48 | println("Thread 1: Holding lock 1 & 2..."); 49 | } 50 | } 51 | } 52 | } 53 | 54 | class SecondThread : Thread() { 55 | 56 | override fun run() { 57 | synchronized(Lock2) { 58 | println("Thread 2: Holding lock 2"); 59 | sleep(10) 60 | println("Thread 2: after sleep"); 61 | synchronized(Lock1) { 62 | println("Thread2: Holding lock 1 & 2..."); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/ktl/KotlinTheory_eng.md: -------------------------------------------------------------------------------- 1 | #### Is it possible to inherit from data class? Why? 2 | 3 | #### What is *inline/noinline/crossinline*? What are the benefits of using? Why aren't they used all the time? 4 | 5 | #### When can't we use *inline*? What is a non-local return? 6 | 7 | #### What is *reified*? What is the advantage of using with inline? 8 | 9 | #### How many parameters in a constructor can an *inline class* have? Why? 10 | 11 | #### Contravariance, covariance, invariance 12 | 13 | #### Why we should prefer list to array? 14 | 15 | #### Difference between Nothing, Unit and Any 16 | 17 | #### What are *delegates*? 18 | 19 | #### What does _typealias_ compile to? 20 | 21 | -------------------------------------------------------------------------------- /src/ktl/KotlinTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Разница между *class* и *data class*, *data object* и *object* 2 | 3 | #### 2) Способы реализовать функциональный тип? 4 | 5 | ```Kotlin 6 | val a: () -> Unit = {} 7 | 8 | val b: () -> Unit = object : () -> Unit { 9 | override fun invoke() {} 10 | } 11 | 12 | val c: () -> Unit = ::foo 13 | 14 | val d: () -> Unit = fun() {} 15 | 16 | fun foo() = Unit 17 | ``` 18 | 19 | #### 3) Разница между *0 until 10*, *0..10* and *0..<10* 20 | 21 | *0 until 10* == *0..<10* (новый синтаксис, появился в kotlin 1.8.0) 22 | 23 | *0..10* берет интервал включая правый край, *0..<10* исключая 24 | 25 | #### 4) Что такое inline/noinline/crossinline? Какие плюсы от использования? Почему не использовать их постоянно? Когда мы не можем использовать inline? Что такое non-local-return? 26 | 27 | [Link](youtube.com/watch?v=shTrR_O6TaA) 28 | 29 | #### 5) Что такое reified? В чем плюс использования с inline? 30 | 31 | Помогает избавиться от рефлексии. Inline подставляет вместо дженерика нужный нам класс 32 | 33 | #### 6) Сколько параметров в конструкторе может иметь inline class? Почему? 34 | 35 | Только один параметр. Во время компиляции на место инициализации класса заменит инициализацию этого параметра, и 36 | перенесет код из inline в тот где используется его реализация. 37 | 38 | #### 7) Контравариантность, ковариантность, инвариантность 39 | 40 | Представим что у нас есть два класса 41 | 42 | ```Kotlin 43 | open class Fruit 44 | class Orange : Fruit() 45 | 46 | fun variance() { 47 | var fruit = Fruit() 48 | var orange = Orange() 49 | 50 | fruit = orange // если мы попытаемся записать orange во fruit мы сможем это сделать 51 | // orange = fruit // fruit в orange уже нельзя (потому что orange является fruit, а fruit не является orange) 52 | 53 | // А как поведет себя список? 54 | 55 | var orangeList: ArrayList = arrayListOf() 56 | var fruitList: ArrayList = arrayListOf() 57 | // Мы не сможем присвоить переменную как список другого типа. 58 | // Это называется ИНВАРИАНТНОСТЬ 59 | fruitList = orangeList // forbidden 60 | orangeList = fruitList // forbidden 61 | 62 | } 63 | ``` 64 | 65 | Если мы хотим иметь список в котором можем содержать всех родителей Orange мы можем использовать *ArrayList< in Orange>* 66 | . Это называется КОНТРВАРИАНТНОСТЬ 67 | 68 | ```Kotlin 69 | doSmth(fruitList) 70 | 71 | fun doSmth(orangeList: ArrayList) { 72 | orangeList.forEach { 73 | 74 | } 75 | val orange: Any? = orangeList[0] // не даст вернуть Orange, только Any 76 | } 77 | ``` 78 | 79 | Если мы хотим иметь список в котором можем содержать всех наследлников Fruit мы можем использовать ArrayList. 80 | Это называется КОВАРИАНТНОСТЬ 81 | 82 | ```Kotlin 83 | doSmth2(orangeList) 84 | 85 | fun doSmth2(orangeList: ArrayList) { 86 | orangeList.forEach { 87 | 88 | } 89 | // orangeList.add(Orange()) не даст ничего добавить потому что тип использующий out == Nothing 90 | } 91 | 92 | ``` 93 | 94 | Так же *in* и *out* могут быть использованы для указания того, какие типы должны быть возвращающими, а какие принимающие 95 | 96 | ```Kotlin 97 | class Example { 98 | fun check(t: T) {} 99 | fun check(): V? { 100 | return null 101 | } 102 | // fun check2(v: V){ // так нельзя, тип V должен использовать для возврата значения 103 | // 104 | // } 105 | } 106 | ``` 107 | 108 | #### 8) Разница между Nothing, Unit и Any 109 | 110 | #### 9) Можно ли наследоваться от data class? Почему? 111 | 112 | #### 10) Что такое делегаты? 113 | 114 | Выражение после by является делегатом, потому что геттеры и сеттеры, делегированы его методам getValue() и setValue(). 115 | Делегаты свойств не должны реализовывать интерфейс, но они должны предоставлять getValue() функцию (и setValue() для vars). 116 | 117 | #### 11) Во что компилируется _typealias_? 118 | 119 | -------------------------------------------------------------------------------- /src/multithreading/ru/MultithreadingPractice.kt: -------------------------------------------------------------------------------- 1 | package multithreading.ru 2 | 3 | 4 | fun main() { 5 | // threadsDeadLock() 6 | } 7 | 8 | fun threadsDeadLock() { 9 | val firstThread = FirstThread() 10 | val secondThread = SecondThread() 11 | firstThread.start() 12 | secondThread.start() 13 | } 14 | 15 | var Lock1 = Any() 16 | var Lock2 = Any() 17 | 18 | class FirstThread : Thread() { 19 | override fun run() { 20 | synchronized(Lock1) { 21 | println("Thread 1: Holding lock 1"); 22 | sleep(10) 23 | println("Thread 1: after sleep"); 24 | synchronized(Lock2) { 25 | println("Thread 1: Holding lock 1 & 2..."); 26 | } 27 | } 28 | } 29 | } 30 | 31 | class SecondThread : Thread() { 32 | 33 | override fun run() { 34 | synchronized(Lock2) { 35 | println("Thread 2: Holding lock 2"); 36 | sleep(10) 37 | println("Thread 2: after sleep"); 38 | synchronized(Lock1) { 39 | println("Thread2: Holding lock 1 & 2..."); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/oop/ru/OOPPracticeAnswers_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Задача переопределить equals для дочернего класса 2 | *Ошибка N1* 3 | ``` 4 | @Override 5 | public boolean equals(Object o) { 6 | if (!(o instanceof ColorPoint)) 7 | return false; 8 | return super.equals(0) && ((ColorPoint) o).color == color; 9 | } 10 | ``` 11 | 12 | Если начать проверять на ColorPoint, то мы нарушим правило симметричности 13 | **если x.equals(y), то y.equals(x)**. 14 | 15 | *Ошибка N2* 16 | 17 | ``` 18 | @Override 19 | public boolean equals(Object o) { 20 | if (!(o instanceof Point)) 21 | return false; 22 | 23 | if (!(o instanceof ColorPoint)) 24 | return super.equals(o); 25 | return super.equals(o) && ((ColorPoint) o).color == color; 26 | } 27 | ``` 28 | 29 | Здесь мы нарушаем правило транзитивности 30 | ColorPoint p1 == new ColorPoint(1, 2, Color.RED) 31 | Point p2 == new Point(1, 2) 32 | ColorPoint p3 == new ColorPoint(1, 2, Color.BLUE) 33 | 34 | 35 | *Ошибка N3* 36 | 37 | ``` 38 | @Override public boolean equals(Object o) { 39 | if (o == null || o.getClass() != getClass()) 40 | return false; 41 | Point p = (Point) o; 42 | return p.x == x && p.y == y; 43 | } 44 | ``` 45 | 46 | Нарушение принципа Барбары Лисков 47 | -------------------------------------------------------------------------------- /src/oop/ru/OOPPracticeQuestions.java: -------------------------------------------------------------------------------- 1 | package oop.ru; 2 | 3 | import java.awt.*; 4 | 5 | import static com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util.println; 6 | 7 | 8 | public class OOPPracticeQuestions { 9 | /** 10 | * 1) Переопределите equals для ColorPoint 11 | **/ 12 | public static class Point { 13 | private final int x; 14 | private final int y; 15 | 16 | public Point(int x, int y) { 17 | this.x = x; 18 | this.y = y; 19 | } 20 | 21 | @Override 22 | public boolean equals(Object o) { 23 | if (!(o instanceof Point)) 24 | return false; 25 | Point p = (Point) o; 26 | return p.x == x && p.y == y; 27 | } 28 | 29 | // ... остальная часть кода опущена 30 | } 31 | 32 | public static class ColorPoint extends Point { 33 | private final Color color; 34 | 35 | public ColorPoint(int x, int y, Color color) { 36 | super(x, y); 37 | this.color = color; 38 | } 39 | 40 | // ... остальная часть кода опущена 41 | 42 | } 43 | 44 | 45 | public static void main(String[] args) { 46 | new TryCatchTask().start(); 47 | } 48 | 49 | 50 | /** 51 | * 2) Что выведет код? 52 | **/ 53 | public static class TryCatchTask { 54 | 55 | public void start() { 56 | try { 57 | println("try"); 58 | throw new ArithmeticException(); 59 | } catch (ArithmeticException e) { 60 | println("catch"); 61 | throw new NullPointerException(); 62 | } finally { 63 | println("finally"); 64 | throw new IndexOutOfBoundsException(); 65 | } 66 | } 67 | 68 | } 69 | 70 | 71 | 72 | /** 73 | * 3) Что выведется и в каком порядке? 74 | **/ 75 | 76 | public static class HierarchyTask { 77 | 78 | public static class Parent { 79 | public void start() { 80 | println("parent start"); 81 | } 82 | } 83 | 84 | public static class Child extends Parent { 85 | 86 | public void start() { 87 | println("child start"); 88 | } 89 | } 90 | 91 | public static class Launcher { 92 | 93 | public void launch(Parent parent) { 94 | println("launching parent"); 95 | parent.start(); 96 | } 97 | 98 | public void launch(Child child) { 99 | println("launching child"); 100 | child.start(); 101 | } 102 | } 103 | 104 | public static void main(String[] args) { 105 | Parent obj = new Child(); 106 | Launcher launcher = new Launcher(); 107 | launcher.launch(obj); 108 | 109 | 110 | 111 | Child obj2 = new Child(); 112 | launcher.launch(obj2); 113 | } 114 | 115 | } 116 | 117 | 118 | } 119 | 120 | -------------------------------------------------------------------------------- /src/oop/ru/OOPTheory_ru.md: -------------------------------------------------------------------------------- 1 | #### 1) Назовите отношения эквивалентности 2 | - Рефлексивность (x.equals(x)) 3 | - Симметричность (если x.equals(y), то y.equals(x)) 4 | - Транзитивность (если x.equals(y), a y.equals(z), то x.equals(z)) 5 | - Непротиворечивость (x.equals(y)) должно возвращать всегда одинаковое значение 6 | - Для любое ненулевой ссылки x.equals(null) должно возвращать false 7 | 8 | #### 2) -------------------------------------------------------------------------------- /src/utils/utils.kt: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.Deferred 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.runBlocking 7 | 8 | abstract class Activity{ 9 | protected val lifecycleScope = CoroutineScope(Dispatchers.Default) 10 | init { onCreate() } 11 | 12 | abstract fun onCreate() 13 | 14 | protected fun onClickToSmth(action: suspend () -> Deferred) = runBlocking { 15 | action().await() 16 | } 17 | } 18 | --------------------------------------------------------------------------------