├── .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 | 
24 |
25 | 4) Socket-NDK
26 |
27 | Все они под капотом используют Binder
28 | 
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 | 
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 | 
81 |
82 | При этом в процессе выполнения каждый процесс может создавать новые процессы (child process),
83 | по отношению к которым он будет предком-родителем, через fork/exec
84 | 
85 |
86 |
87 | Zygote - специальный системный процесс, от которого мы запускаем процессы своего прложения.
88 | Zygote уже содержит все необходимое для функционирования нашего приложения.
89 | Через fork копируем родительский процесс, с отличным UID (User identifier).
90 | 
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 | 
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 | 
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 | 
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 | 
434 |
435 | #### Content resolver, Content Provider
436 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
41 |
42 | MessageQueue — это очередь со списком задач, которые будут выполняться в определенном потоке.
43 |
44 | Handler - добавялет Message в MessageQueue
45 |
46 | Looper - С потоком может быть связан только один Looper.
47 | Присоединение другого Looper к Thread приводит к RuntimeException.
48 | Использование статического объекта ThreadLocal в классе Looper гарантирует, что к потоку будет присоединен только один Looper.
49 | Looper отвечает за сохранение поток живым.
50 |
51 | 
--------------------------------------------------------------------------------
/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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------