Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
Код для работы с SQLite в Android не генерируется автоматически — разработчик сам пишет SQL-запросы и управляет БД.
Но если используется Room (ORM для SQLite), то код генерируется на этапе компиляции с помощью Annotation Processing (
kapt) или KSP. Если используешь
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, код автоматически генерируется во время компиляции с помощью 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
}
Если используешь
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
– Холодные (cold) — начинают генерировать данные только после подписки (например, Flow, Observable).
– Горячие (hot) — генерируют данные независимо от подписчиков (например, SharedFlow, LiveData, Broadcast).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Сборка Android-приложения проходит несколько этапов:
Компиляция (
.kt/.java → .class → .dex) Объединение ресурсов (
XML, PNG → R.java) Сборка APK/AAB
Подписывание и оптимизация
Установка и запуск на устройстве
Компиляция 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
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 (новый формат 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
👍3
В Kotlin:
- == вызывает equals(), то есть логическое сравнение значений.
- === — это сравнение по ссылке, то есть проверка, указывают ли обе переменные на один и тот же объект в памяти.
В Java: == сравнивает ссылки для объектов, а equals() — содержимое.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
В Android используются разные единицы измерения для работы с экраном, чтобы адаптировать UI под разные устройства.
px (pixel) – это самая маленькая единица изображения на экране. Не зависит от плотности экрана → на разных устройствах элементы могут выглядеть слишком маленькими или большими.
<TextView
android:layout_width="100px"
android:layout_height="50px"/>
dp (density-independent pixel) – это абстрактная единица измерения, которая адаптируется под плотность экрана (dpi). Рекомендуется для всех размеров UI, чтобы интерфейс выглядел одинаково на разных устройствах.
<TextView
android:layout_width="100dp"
android:layout_height="50dp"/>
Пример в Kotlin
val sizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics
)
sp (scale-independent pixel) – как dp, но ещё учитывает настройки шрифта пользователя. Используется только для размеров шрифтов. <TextView
android:text="Привет, мир!"
android:textSize="16sp"/>
Пример в Kotlin
val textSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics
)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Рекомендуемый способ:
- Создать Bundle, положить туда параметры (putString, putInt и т.д.).
- Установить arguments фрагменту.
- В onCreate() фрагмента извлечь через requireArguments().
Это безопасный способ, поддерживаемый Android-системой (в т.ч. при пересоздании фрагмента).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Это ситуация, когда приложение выделяет память для каких-либо объектов, но затем не освобождает её, даже когда эти объекты больше не нужны. Это приводит к тому, что используемая память накапливается, что может со временем замедлить работу приложения и привести к его аварийному завершению (крашу) из-за нехватки доступной памяти.
В Android утечки памяти могут возникнуть из-за особенностей работы виртуальной машины (ART или Dalvik), а также из-за того, что сборщик мусора (Garbage Collector, GC) не может освободить память для объектов, на которые по-прежнему существуют ссылки. Это происходит в следующих случаях:
Если объект ссылается на другой объект, который больше не нужен, последний не может быть освобождён GC. Например:
Статические ссылки, которые продолжают "удерживать" объект.
Замыкания (closures), которые хранят ссылки на контекст активности.
Некоторые объекты системы Android, такие как
Context, View, Handler, хранят ссылки на компоненты приложения (например, Activity), из-за чего их нельзя освободить.Ошибки, такие как регистрация слушателей (listeners) без последующей отписки, использование таймеров, которые продолжают работать даже после уничтожения активности, и так далее.
public class MyActivity extends AppCompatActivity {
private static TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, Memory Leak!");
}
}Исправленный код
public class MyActivity extends AppCompatActivity {
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, World!");
}
}Не используйте
static для объектов, ссылающихся на Context или Activity. Используйте WeakReference, если нужно сохранить ссылку, которая не должна блокировать сборщик мусора.Если вы регистрируете слушателей (например, через
setOnClickListener), обязательно удаляйте их в методах жизненного цикла, например, в onDestroy().Анонимные классы (например,
Runnable, Handler) могут неявно хранить ссылки на внешние классы, что может привести к утечке памяти.Android Profiler: встроенный инструмент Android Studio для мониторинга использования памяти. LeakCanary: библиотека, которая автоматически обнаруживает утечки памяти в вашем приложении.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Job — это элемент управления жизненным циклом корутины. С его помощью можно:
- отменять корутину;
- отслеживать завершение;
- объединять с другими задачами;
- управлять родительскими и дочерними корутинами.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Это компонент приложения, который предоставляет пользовательский интерфейс (UI), с которым пользователи могут взаимодействовать для выполнения различных действий, таких как набор номера телефона, просмотр фотографий, отправка электронной почты и т. д. Каждая активность представляет собой один экран с пользовательским интерфейсом. Если представить приложение как книгу, то активность будет одной страницей этой книги.
Основная задача — предоставить макет (layout), который будет содержать все элементы пользовательского интерфейса, такие как кнопки, текстовые поля, изображения и прочее, с которыми пользователь может взаимодействовать.
Служит в качестве "лица" приложения для взаимодействия с пользователем, обрабатывая пользовательские входные данные, такие как нажатие кнопок, ввод текста и т. д.
Android управляет активностями через заданный жизненный цикл, который определяет, как активность создается, запускается, останавливается и уничтожается. Разработчики могут переопределять методы жизненного цикла, чтобы добавить свою логику обработки для разных состояний активности.
В приложении обычно есть несколько активностей, и он используется для перехода от одного экрана к другому. Для перехода между активностями используются интенты (Intents), которые не только помогают открыть новую активность, но и могут передавать данные между активностями.
Может взаимодействовать с другими компонентами приложения, такими как
Services, BroadcastReceivers, и ContentProviders, используя интенты и другие механизмы Android для межкомпонентного взаимодействия.class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Конструкторы не переопределяются, потому что они не наследуются. Однако можно перегружать их в одном классе (разные параметры), и вызывать конструкторы суперкласса через super().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
В Android есть несколько способов навигации между экранами. Давай разберём основные.
Каждый экран – это отдельная
Activity, переход осуществляется с помощью Intent. val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
Один
Activity содержит несколько Fragment, навигация через FragmentManager. supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack(null)
.commit()
Google рекомендует
Navigation Component для удобной работы с Fragment. findNavController().navigate(R.id.action_firstFragment_to_secondFragment)
Используется
BottomNavigationView или TabLayout для переключения между экранами. bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.home -> replaceFragment(HomeFragment())
R.id.profile -> replaceFragment(ProfileFragment())
}
true
}Боковое меню (
DrawerLayout) с пунктами навигации. drawerLayout.openDrawer(GravityCompat.START)
Навигация через ссылки (например,
myapp://profile/123). <intent-filter>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="myapp" android:host="profile"/>
</intent-filter>
Используется
NavHost и NavController. NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile") { ProfileScreen(navController) }
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Multibinding позволяет создать коллекции зависимостей одного типа — например, Set или Map. Это особенно полезно для регистрации нескольких обработчиков событий, плагинов или реализаций одного интерфейса.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В многомодульных проектах каждый модуль может иметь свой
AndroidManifest.xml, чтобы: Задавать зависимости (
uses-permission, uses-feature) для конкретного модуля. Определять компоненты (
Activity, Service, BroadcastReceiver) для каждого модуля. Автоматически объединять манифесты всех модулей в
AndroidManifest.xml главного (app) модуля. При сборке Gradle автоматически сливает (
merge) все AndroidManifest.xml в один итоговый файл. app/
├── src/main/AndroidManifest.xml ← Главный манифест
├── java/com/example/MainActivity.kt
├── res/
├── build.gradle
feature_login/
├── src/main/AndroidManifest.xml ← Манифест модуля `login`
├── java/com/example/login/LoginActivity.kt
├── res/
├── build.gradle
feature_chat/
├── src/main/AndroidManifest.xml ← Манифест модуля `chat`
├── java/com/example/chat/ChatActivity.kt
├── res/
├── build.gradle
При сборке манифесты модулей сливаются в манифест главного модуля.
<manifest package="com.example.app">
<application>
<activity android:name=".MainActivity" />
</application>
</manifest>
feature_login (feature_login/src/main/AndroidManifest.xml) <manifest>
<application>
<activity android:name=".login.LoginActivity" />
</application>
</manifest>
feature_chat (feature_chat/src/main/AndroidManifest.xml) <manifest>
<application>
<activity android:name=".chat.ChatActivity" />
</application>
</manifest>
После объединения итоговый
AndroidManifest.xml выглядит так <manifest package="com.example.app">
<application>
<activity android:name=".MainActivity" />
<activity android:name=".login.LoginActivity" />
<activity android:name=".chat.ChatActivity" />
</application>
</manifest>
Например, модуль
camera требует CAMERA, но другие модули — нет. <uses-permission android:name="android.permission.CAMERA" />
Каждый модуль добавляет только свои компоненты (например,
LoginActivity в feature_login). Например, модуль
feature_map использует Google Maps, но feature_login — нет. <uses-library android:name="com.google.android.maps" />
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2
DiffUtil — это утилита для быстрого обновления списков в RecyclerView. Она сравнивает старый и новый список и находит различия, чтобы обновлять только изменённые элементы, а не весь список. Обычное обновление списка без
DiffUtil перерисовывает всё, даже если изменился один элемент. adapter.notifyDataSetChanged() // Полностью обновляет список ❌
DiffUtil находит различия между старым и новым списком и обновляет только изменённые элементы. class MyDiffUtilCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition] // Используем data class (авто `equals()`)
}
}
После создания
DiffUtil.Callback, вызываем DiffUtil.calculateDiff() и передаём результат в adapter. fun updateList(newList: List<User>) {
val diffCallback = MyDiffUtilCallback(userList, newList)
val diffResult = DiffUtil.calculateDiff(diffCallback)
userList = newList // Обновляем старый список
diffResult.dispatchUpdatesTo(this) // Обновляем только изменённые элементы
}Если используем
ListAdapter, DiffUtil уже встроен. class UserAdapter : ListAdapter<User, UserViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(getItem(position)) // `getItem()` уже работает с `ListAdapter`
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem
}
}
}Обновление списка в
ListAdapter adapter.submitList(newList) // DiffUtil работает внутри 🚀
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
В Kotlin для работы с многопоточностью можно использовать
atomic переменные или synchronized блоки. Они помогают избежать гонок данных (race conditions), когда несколько потоков одновременно изменяют одну и ту же переменную. Atomic переменные (AtomicInteger, AtomicLong, AtomicReference и др.) используются, когда нужно быстро и безопасно обновлять одно значение без блокировки потока. Высокая производительность – атомарные операции работают быстрее, чем
synchronized. Потокобезопасность – операции выполняются без блокировок (lock-free).
Удобство – простые методы
incrementAndGet(), compareAndSet() и т. д. Работает только с одним значением – не подходит для сложных операций.
Не подходит для зависимых изменений (например, если изменение одной переменной зависит от другой).
import java.util.concurrent.atomic.AtomicInteger
val counter = AtomicInteger(0)
fun increment() {
counter.incrementAndGet() // Увеличиваем значение безопасно
}
Используется, когда нужно защитить сразу несколько связанных операций от одновременного выполнения в нескольких потоках.
Подходит для сложных операций с несколькими переменными.
Работает с любыми типами данных.
Снижает производительность – потоки ждут, пока один поток закончит работу.
Может вызывать deadlock (взаимную блокировку).
val lock = Any()
var count = 0
fun increment() {
synchronized(lock) {
count++ // Только один поток может изменять `count` одновременно
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
– Сохранить строку как *.svg в файл и загрузить через SvgDecoder,
– Использовать библиотеки (Glide, Coil, androidsvg),
– Преобразовать в Drawable и отобразить через ImageView.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1💊1
При создании классов по сравнению с Java произошли несколько значительных изменений и упрощений. Kotlin предлагает более лаконичный и выразительный синтаксис, что делает код более читаемым и удобным.
В Kotlin объявление классов и их конструкторов значительно упрощено.
В Java
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}В Kotlin
class Person(val name: String, val age: Int)
В Java для объявления статических членов используется ключевое слово
static. В Kotlin вместо этого используются companion object.В Java
public class MyClass {
public static final String CONSTANT = "constant";
public static void staticMethod() {
// Some code
}
}В Kotlin
class MyClass {
companion object {
const val CONSTANT = "constant"
@JvmStatic
fun staticMethod() {
// Some code
}
}
}Kotlin предоставляет специальный тип классов —
data классы, которые автоматически генерируют методы equals(), hashCode(), toString(), copy(), и componentN().В Java
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
// Implementation
}
@Override
public int hashCode() {
// Implementation
}
@Override
public String toString() {
// Implementation
}
}В Kotlin
data class User(val name: String, val age: Int)
В Kotlin свойства объявляются напрямую, и методы доступа (геттеры и сеттеры) генерируются автоматически.
В Java
public class Rectangle {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}В Kotlin
class Rectangle(var width: Int, var height: Int)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1