Kotlin | Вопросы собесов
2.56K subscribers
27 photos
956 links
Download Telegram
🤔 Как рисовать UI без xml?

В Android можно создавать интерфейс без XML с помощью Jetpack Compose или программного кода (View в Kotlin/Java).

🚩Jetpack Compose — современный декларативный подход

Jetpack Compose — это новый способ создания UI в Android без XML, основанный на Kotlin.
@Composable
fun GreetingScreen() {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Привет, мир!", fontSize = 24.sp)
Button(onClick = { println("Кнопка нажата") }) {
Text("Нажми меня")
}
}
}

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GreetingScreen()
}
}
}


🚩Создание UI через View в Kotlin (старый способ)

Если Jetpack Compose не подходит, можно создать интерфейс программно, без XML, используя стандартные View.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Создаём контейнер (LinearLayout)
val layout = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}

// Создаём текст
val textView = TextView(this).apply {
text = "Привет, мир!"
textSize = 24f
setTextColor(Color.BLACK)
}

// Создаём кнопку
val button = Button(this).apply {
text = "Нажми меня"
setOnClickListener {
textView.text = "Кнопка нажата!"
}
}

// Добавляем элементы в контейнер
layout.addView(textView)
layout.addView(button)

// Устанавливаем UI
setContentView(layout)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как помогает Handler выполнять работу синхронно?

Handler используется вместе с Looper, чтобы поставить задачу в очередь исполнения в определённом потоке. Если ты создаёшь Handler, привязанный к конкретному Looper, все отправленные сообщения и Runnable выполняются по очереди, синхронно внутри этого потока, но асинхронно по отношению к другим потокам. То есть Handler помогает упорядочить выполнение задач внутри одного потока.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
🤔 Что используешь, если нужно получить элемент или список элементов?

В Kotlin есть множество удобных функций для получения одного элемента или списка элементов из коллекций (List, Set, Map). Какую функцию использовать — зависит от задачи.

🚩Получение одного элемента

🟠`get(index)` и `getOrNull(index)`
получение по индексу
Если индекс может выйти за границы массива, лучше использовать getOrNull().
val list = listOf("A", "B", "C")

println(list[1]) // "B"
println(list.getOrNull(5)) // null


🟠`first()` и `last()`
первый и последний элементы
println(list.first())  // "A"
println(list.last()) // "C"

Используйте firstOrNull() или lastOrNull(), если список может быть пустым
println(emptyList<String>().firstOrNull())  // null


🟠`find {}` и `findLast {}`
поиск по условию Возвращает первый или последний элемент, удовлетворяющий условию.
val numbers = listOf(10, 20, 30, 40)

println(numbers.find { it > 15 }) // 20
println(numbers.findLast { it > 15 }) // 40
println(numbers.find { it > 50 }) // null


🟠`single()` и `singleOrNull()`
если ожидается только один элемент
val oneElementList = listOf(42)
println(oneElementList.single()) // 42
println(oneElementList.singleOrNull()) // 42

val emptyList = emptyList<Int>()
println(emptyList.singleOrNull()) // null

single() бросает исключение, если элементов нет или их больше одного.

🚩Получение нескольких элементов (списка)

filter {} – фильтрация элементов по условию
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // [2, 4]


take(n) и takeLast(n) – первые/последние n элементов
val items = listOf("a", "b", "c", "d", "e")

println(items.take(3)) // [a, b, c]
println(items.takeLast(2)) // [d, e]


slice(indices) – получение элементов по индексам
val sliced = items.slice(listOf(0, 2, 4))
println(sliced) // [a, c, e]


map {} – преобразование списка
val squared = numbers.map { it * it }
println(squared) // [1, 4, 9, 16, 25]


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как сборщик мусора понимает, что объект можно уничтожить?

GC отслеживает достижимость объектов из корневых точек (stack, static, thread references). Если объект:
- Не имеет ни одной активной ссылки
- Не достижим по цепочке ссылок
…то он считается неиспользуемым и может быть удалён.
Алгоритмы: mark-and-sweep, tracing, generational collection.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 В чем различие Default диспатчер и IO диспатчер?

В Kotlin Coroutines есть несколько диспетчеров (Dispatchers), но Default и IO используются чаще всего.

🚩Главное различие:

Dispatchers.Default — для тяжёлых вычислений (CPU-операции).
Dispatchers.IO — для операций ввода-вывода (сеть, файлы, БД).

🟠`Dispatchers.Default` – для сложных вычислений (CPU-bound)
Этот диспетчер используется, если код загружает процессор (например, сложные вычисления).
import kotlinx.coroutines.*

fun main() = runBlocking {
launch(Dispatchers.Default) {
val result = heavyComputation()
println("Результат: $result")
}
}

suspend fun heavyComputation(): Int {
delay(1000)
return (1..1_000_000).sum()
}


🟠`Dispatchers.IO` – для работы с файлами, сетью, БД (IO-bound)
Этот диспетчер оптимизирован для ввода-вывода (I/O): работа с файлами, сетью, БД.
import kotlinx.coroutines.*
import java.io.File

fun main() = runBlocking {
launch(Dispatchers.IO) {
val text = File("data.txt").readText()
println("Файл прочитан: $text")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Чем data class отличается от обычного класса?

`data class` в Kotlin автоматически генерирует полезные методы, такие как `equals()`, `hashCode()`, `toString()`, и `copy()`, что делает его идеальным для хранения данных. Обычные классы не предоставляют такую функциональность по умолчанию, и все эти методы должны быть реализованы вручную. Data-классы используются для создания объектов, которые содержат только данные, без сложной логики. Это сокращает количество шаблонного кода и улучшает читаемость.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Как запустить асинхронные операции в чистом Android?

В Android существует несколько способов запуска асинхронных операций. Наиболее распространённые из них включают использование AsyncTask (хотя он уже устарел), HandlerThread, AsyncTaskLoader, ExecutorService и современные подходы с использованием Kotlin Coroutines и библиотеки WorkManager.

🟠AsyncTask (устаревший)
AsyncTask был одним из первых инструментов для выполнения асинхронных задач, однако его использование сейчас не рекомендуется из-за проблем с управлением жизненным циклом и утечками памяти.
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
// Выполнение длительной операции
return "Result";
}

@Override
protected void onPostExecute(String result) {
// Обновление UI после завершения операции
}
}

// Запуск
new MyAsyncTask().execute();


🟠HandlerThread
HandlerThread — это удобный способ создания фонового потока с циклом обработки сообщений.
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

handler.post(new Runnable() {
@Override
public void run() {
// Выполнение длительной операции
}
});


🟠ExecutorService
ExecutorService из стандартной библиотеки Java предоставляет более гибкий способ управления потоками.
ExecutorService executor = Executors.newSingleThreadExecutor();

executor.submit(new Runnable() {
@Override
public void run() {
// Выполнение длительной операции
}
});


🟠Kotlin Coroutines
Корутины в Kotlin — это современный и мощный инструмент для асинхронного программирования. Они упрощают работу с асинхронными задачами и обеспечивают безопасность работы с UI.
import kotlinx.coroutines.*

fun performAsyncTask() {
GlobalScope.launch(Dispatchers.IO) {
// Выполнение длительной операции
val result = longRunningTask()

withContext(Dispatchers.Main) {
// Обновление UI после завершения операции
}
}
}

suspend fun longRunningTask(): String {
delay(1000) // Симуляция длительной операции
return "Result"
}


🟠WorkManager
WorkManager — это библиотека для выполнения фоновых задач, которая обеспечивает выполнение задач даже после закрытия приложения или перезагрузки устройства.
public class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}

@NonNull
@Override
public Result doWork() {
// Выполнение длительной операции
return Result.success();
}
}

// Запуск Worker
WorkManager workManager = WorkManager.getInstance(context);
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();
workManager.enqueue(request);


🟠RxJava
RxJava — это библиотека для реактивного программирования, которая также может быть использована для выполнения асинхронных задач.
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;

Observable.fromCallable(() -> {
// Выполнение длительной операции
return "Result";
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// Обновление UI после завершения операции
});


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🤔1
🤔 Где хранятся объекты в Garbage Collector?

Это область памяти, управляемая сборщиком мусора.
GC следит за тем, какие объекты ещё используются, и очищает неиспользуемые (недостижимые).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊13🔥1
🤔 Как сохранить состояние скролла при пересоздании Activity?

При пересоздании Activity (например, при повороте экрана) состояние ScrollView или RecyclerView сбрасывается. Чтобы этого избежать, можно сохранить и восстановить позицию скролла.

🟠Использование `onSaveInstanceState()`
Android позволяет сохранять данные в Bundle перед уничтожением Activity, а затем восстанавливать их.
class MainActivity : AppCompatActivity() {
private lateinit var scrollView: ScrollView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

scrollView = findViewById(R.id.scrollView)

// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollY = it.getInt("scroll_position", 0)
scrollView.post { scrollView.scrollTo(0, scrollY) }
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("scroll_position", scrollView.scrollY)
}
}


🟠Для `RecyclerView`
У RecyclerView уже есть встроенный механизм сохранения состояния через LinearLayoutManager.
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var layoutManager: LinearLayoutManager

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

layoutManager = LinearLayoutManager(this)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = MyAdapter()

// Восстанавливаем позицию скролла
savedInstanceState?.let {
val scrollPosition = it.getInt("recycler_position", 0)
recyclerView.post { layoutManager.scrollToPosition(scrollPosition) }
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("recycler_position", layoutManager.findFirstVisibleItemPosition())
}
}


🟠Использование `android:freezesText` для `EditText`
Если EditText находится внутри ScrollView, можно включить android:freezesText="true", чтобы автоматически сохранять данные.
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:freezesText="true"/>


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что случится, если долго выполнять работу на главном потоке?

Главный поток (UI thread) в Android отвечает за:
- Отрисовку интерфейса.
- Обработку касаний.
- Обновление экранов.
Если долго блокировать этот поток (например, выполнять сетевой запрос или тяжёлый цикл), приложение:
- Перестанет откликаться.
- Выдаст предупреждение ANR (Application Not Responding).
- Может быть завершено системой или вызвать раздражение у пользователя.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В какой момент генерируется код при использовании SQLite?

Код для работы с SQLite в Android не генерируется автоматически — разработчик сам пишет SQL-запросы и управляет БД.

Но если используется Room (ORM для SQLite), то код генерируется на этапе компиляции с помощью Annotation Processing (kapt) или KSP.

🚩Обычный SQLite – без генерации кода

Если используешь SQLiteOpenHelper, то код не генерируется, ты сам пишешь SQL-запросы:
class MyDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "mydb", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
}

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS users")
onCreate(db)
}
}


🚩Room – код генерируется при компиляции

Если используешь Room, код автоматически генерируется во время компиляции с помощью Annotation Processing (kapt) или KSP.
Ты пишешь аннотации (@Entity, @Dao, @Database).
Room-аннотации анализируются при компиляции (kapt / KSP).
Генерируется SQL-код и DAO-методы.
@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)

@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAllUsers(): List<User>

@Insert
fun insert(user: User)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}


🚩Когда именно генерируется код Room?

Если используешь kapt (Annotation Processor) или KSP, то код генерируется во время компиляции.
dependencies {
implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1") // Кодогенерация
}


Пример с KSP (быстрее kapt)
dependencies {
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1") // Кодогенерация
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Какие паттерны реализуют очередь и стек?

Для очереди — паттерн Producer-Consumer, особенно актуален в многопоточных сценариях. Для стека — используется паттерн Last-In-First-Out (LIFO), часто применяемый в навигации и при обратном обходе структур данных.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В каких случаях наследование полезно, а в каких нет?

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

🚩Когда наследование полезно

🟠Для повторного использования кода
Если у вас есть несколько классов, которые имеют общие свойства или методы, наследование позволяет вынести эти общие элементы в родительский класс. Это помогает избежать дублирования кода.

   open class Animal(val name: String) {
fun eat() {
println("$name ест.")
}
}

class Dog(name: String) : Animal(name) {
fun bark() {
println("$name лает.")
}
}

class Cat(name: String) : Animal(name) {
fun meow() {
println("$name мяукает.")
}
}

// Использование:
val dog = Dog("Бобик")
dog.eat() // "Бобик ест."
dog.bark() // "Бобик лает."


🟠Для создания иерархий классов (например, типологии)
Наследование отлично подходит для задач, связанных с созданием деревьев типов или категорий. Например, у вас есть базовый класс Shape (Форма), и от него наследуются конкретные фигуры: Circle, Rectangle, Triangle.

   open class Shape {
open fun draw() {
println("Рисуется форма.")
}
}

class Circle : Shape() {
override fun draw() {
println("Рисуется круг.")
}
}

class Rectangle : Shape() {
override fun draw() {
println("Рисуется прямоугольник.")
}
}

// Использование:
val shapes: List<Shape> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}


🟠Для использования полиморфизма
Полиморфизм позволяет использовать родительские классы для работы с объектами наследников. Это полезно, если у вас есть методы, которые работают с разными типами объектов, но с единым интерфейсом. Вызов методов draw() у всех объектов, не задумываясь об их конкретном типе, благодаря полиморфизму.

   fun render(shape: Shape) {
shape.draw()
}

render(Circle()) // "Рисуется круг."
render(Rectangle()) // "Рисуется прямоугольник."


🟠Когда концептуально существует отношение "is-a" (является)
Если наследник действительно является вариантом (подтипом) родительского класса, наследование логично. Например: Собака является животным (Dog is an Animal). Прямоугольник является фигурой (Rectangle is a Shape).

🚩Когда наследование не полезно

🟠Когда отношение "is-a" отсутствует
Если объекты не имеют строгого отношения "является", наследование использовать нельзя. Например: Класс Car (Машина) и Boat (Лодка) лучше объединить через общие свойства (интерфейсы или композицию), чем делать лодку наследником машины.

   open class Bird {
open fun fly() {
println("Птица летит.")
}
}

class Penguin : Bird() {
override fun fly() {
throw UnsupportedOperationException("Пингвин не умеет летать!")
}
}


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

   class Car : Engine, Wheels

Используйте композицию:

   class Car {
private val engine = Engine()
private val wheels = Wheels()
}


🟠Сложные иерархии классов
Если вы создаете слишком глубокие или разветвленные иерархии, код становится сложным для понимания, тестирования и изменения. Например, изменение в базовом классе может неожиданно затронуть всех наследников.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что будет со scope, если внутри него упадет картинка?

Зависит от типа CoroutineScope:
- Если корутина не обернута в try-catch, то весь scope завершится с ошибкой.
- Если используется supervisorScope, падение одной корутины не остановит другие.
- Если в launch ошибка не обработана, то scope отменится.
- Если используется async с await(), исключение выбросится при вызове await().
Для устойчивости лучше оборачивать код в try-catch или использовать supervisorScope.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊4👍1🔥1
🤔 Как можно гарантировать быструю отрисовку чатов?

Чтобы чат работал быстро и без лагов, нужно:
Оптимизировать RecyclerView (ViewHolder, DiffUtil, Payloads).
Минимизировать работу в UI Thread (использовать Coroutines, Paging 3).
Оптимизировать загрузку изображений (Glide, Coil).
Использовать Room + Flow / LiveData для офлайн-кеша.

🚩Оптимизация `RecyclerView`

🟠Используем `ListAdapter` + `DiffUtil`
Вместо notifyDataSetChanged() лучше использовать ListAdapter, который сам обновляет только изменённые сообщения.
class ChatAdapter : ListAdapter<Message, ChatViewHolder>(DIFF_CALLBACK) {

companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Message>() {
override fun areItemsTheSame(oldItem: Message, newItem: Message): Boolean =
oldItem.id == newItem.id

override fun areContentsTheSame(oldItem: Message, newItem: Message): Boolean =
oldItem == newItem // data class сравнит автоматически
}
}
}


🟠Используем `Payloads` для частичного обновления
Если, например, только статус сообщения изменился, не нужно перерисовывать всё сообщение!
override fun getChangePayload(oldItem: Message, newItem: Message): Any? {
return if (oldItem.status != newItem.status) "STATUS_CHANGED" else null
}


Пример в onBindViewHolder()
override fun onBindViewHolder(holder: ChatViewHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.contains("STATUS_CHANGED")) {
holder.updateStatus() // Обновляем только статус
} else {
super.onBindViewHolder(holder, position, payloads)
}
}


🚩Оптимизируем загрузку сообщений (Coroutines, Paging 3)

Запросы в БД в Worker Thread (Coroutines + Room)
fun loadMessages(): Flow<List<Message>> = messageDao.getMessages()


Используем Paging 3 для подгрузки сообщений
val pager = Pager(
config = PagingConfig(pageSize = 20),
pagingSourceFactory = { messageDao.getPagedMessages() }
).flow.cachedIn(viewModelScope)


🚩Загружаем изображения асинхронно (Glide, Coil)
Если чат содержит аватары или изображения, они должны загружаться лениво и в фоновом потоке.
Glide.with(context)
.load(user.avatarUrl)
.circleCrop()
.placeholder(R.drawable.placeholder)
.into(imageView)


🚩Используем Room для офлайн-кеша

Чтобы сообщения не загружались из сети при каждом запуске, сохраняем их в Room.
@Dao
interface MessageDao {
@Query("SELECT * FROM messages ORDER BY timestamp DESC")
fun getMessages(): Flow<List<Message>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertMessages(messages: List<Message>)
}


🚩Оптимизируем `RecyclerView` для плавного скролла

Используем setHasFixedSize(true), если сообщения не меняют размер
recyclerView.setHasFixedSize(true)


Используем setItemViewCacheSize(), чтобы кэшировать ViewHolder
recyclerView.setItemViewCacheSize(20)


Отключаем анимации RecyclerView (если лагает)
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что такое принципы SOLID?

SOLID — это пять принципов объектно-ориентированного программирования, направленных на создание гибкого, поддерживаемого и расширяемого кода. Эти принципы включают: Single Responsibility (единственная ответственность), Open/Closed (открытость для расширения, закрытость для изменений), Liskov Substitution (замена Барбары Лисков), Interface Segregation (разделение интерфейсов) и Dependency Inversion (инверсия зависимостей). Применение SOLID помогает улучшить архитектуру приложений и уменьшить сложность кода.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 Как собираем код?

Сборка Android-приложения проходит несколько этапов:
Компиляция (.kt/.java → .class → .dex)
Объединение ресурсов (XML, PNG → R.java)
Сборка APK/AAB
Подписывание и оптимизация
Установка и запуск на устройстве

🚩Компиляция кода (`.kt/.java → .dex`)

Компиляция Kotlin/Java в байткод JVM (.class)
Kotlin-код (.kt) → Java-байткод (.class)
Java-код (.java) → .class
- Используются компиляторы:
- kotlinc (Kotlin Compiler)
- javac (Java Compiler)
// Kotlin-код (MainActivity.kt)
fun main() {
println("Привет, Android!")
}

kotlinc MainActivity.kt -d MainActivity.class


Преобразование .class в `.dex*
Android не использует байткод JVM, а преобразует .class в Dalvik Executable (.dex).
d8 MainActivity.class --output classes.dex


🚩Компиляция ресурсов (`XML, PNG → R.java → .dex`)

Android Gradle Plugin (AGP) обрабатывает ресурсы
- res/layout/*.xml → UI файлы
- res/drawable/*.png → Иконки
- AndroidManifest.xml → Метаинформация
public final class R {
public static final class layout {
public static final int activity_main = 0x7f0a0000;
}
}


🚩Сборка APK/AAB

APK (старый формат) и AAB (новый формат Google Play)
classes.dex + res/ + AndroidManifest.xml объединяются
Проходит обфускация (ProGuard / R8)
APK/AAB упаковывается в ZIP-архив
zipalign -v 4 app.apk signed_app.apk


🚩Подписывание и оптимизация

APK/AAB должен быть подписан перед установкой
apksigner sign --ks my-release-key.jks --out signed_app.apk app-release.apk


Установка и запуск
adb install signed_app.apk
adb shell am start -n com.example.app/.MainActivity


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🤔 Можно ли в Android для организации многопоточного кода использовать Thread? Какие могут быть проблемы?

Можно, но с ограничениями:
- Thread — низкоуровневый механизм. Требует ручного управления.
- Проблемы:
- Утечки памяти (если не завершён правильно).
- Сложность отмены.
- Нет управления жизненным циклом.
- Потенциальная блокировка UI, если использовать неосторожно.
Поэтому чаще рекомендуются Handler, Executor, Coroutine, WorkManager, которые безопаснее и проще в управлении.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
🤔 Есть ли отличия между launch и async в обработке ошибок?

Да, ошибки (Exceptions) обрабатываются по-разному в launch и async!

🚩Ошибки в `launch` – падают сразу

launch сразу выбрасывает исключение, и если нет try-catch, корутина завершает родительский CoroutineScope.
fun main() = runBlocking {
launch {
println("Начало работы")
throw RuntimeException("Ошибка в launch!")
}
delay(100) // Код не выполнится, так как `launch` упадёт
println("Этот код не выполнится")
}


Вывод в консоль
Начало работы
Exception in thread "main" java.lang.RuntimeException: Ошибка в launch!


Решение
Использовать try-catch внутри launch.
launch {
try {
throw RuntimeException("Ошибка в launch!")
} catch (e: Exception) {
println("Ошибка поймана: ${e.message}")
}
}


🚩Ошибки в `async` – остаются в `Deferred`, падают при `await()`

В async ошибка не выбрасывается сразу, а сохраняется в Deferred<T>. Она появится только при вызове await().
fun main() = runBlocking {
val deferred = async {
println("Начало работы async")
throw RuntimeException("Ошибка в async!")
}
delay(100) // Код выполнится, так как ошибка пока не выброшена
println("Этот код выполнится")

deferred.await() // 💥 Ошибка падает только здесь!
}


Вывод в консоль
Начало работы async
Этот код выполнится
Exception in thread "main" java.lang.RuntimeException: Ошибка в async!


Решение
Использовать try-catch при await().
try {
deferred.await()
} catch (e: Exception) {
println("Ошибка поймана: ${e.message}")
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3💊1
🤔 Назови способы передавать данные между двумя приложениями?

- Intent с setPackage — для вызова компонента другого приложения.
- ContentProvider — как общий интерфейс работы с данными.
- Broadcasts — обмен событиями.
- FileProvider — обмен файлами.
- AIDL/Bound Services — сложный межпроцессный обмен.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Как подключить BroadcastReceiver, чтобы он смог получать сообщения?

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

🚩Создание BroadcastReceiver

Создадим простой BroadcastReceiver, который будет реагировать на определённое событие, например, на получение SMS.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Сообщение получено!", Toast.LENGTH_SHORT).show();
}
}


🚩Регистрация ресивера

🟠Статическая регистрация в манифесте
Можно зарегистрировать ресивер в файле AndroidManifest.xml. Это удобно, когда вы хотите, чтобы ресивер всегда был активен и слушал определённые системные события, например, перезагрузку устройства или получение SMS.
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.myapp">

<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>

</application>

</manifest>


🟠Динамическая регистрация в коде
Иногда нужно регистрировать ресивер только на время выполнения определённой активности или службы. В этом случае лучше использовать динамическую регистрацию.
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myBroadcastReceiver = new MyBroadcastReceiver();
}

@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(myBroadcastReceiver, filter);
}

@Override
protected void onStop() {
super.onStop();
unregisterReceiver(myBroadcastReceiver);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2