PendingIntent — это обёртка над Intent, позволяющая другим компонентам (например, системным) выполнить Intent от имени вашего приложения, даже если оно сейчас не активно.
Используется:
- В уведомлениях (NotificationManager).
- Для запуска BroadcastReceiver или Service.
- В AlarmManager.
Он играет роль разрешения: система может отложить выполнение кода, но с уже подтверждёнными правами.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Этот инструмент показывает время, затраченное на отрисовку каждого кадра. Использование этого инструмента позволяет выявить "тяжелые" кадры и измерить улучшения после оптимизации.
Предоставляет набор инструментов для анализа производительности приложения.
Создание и использование тестов производительности помогает количественно оценить улучшения. Вы можете использовать библиотеку Jetpack Benchmark для создания и выполнения тестов производительности.
build.gradle:dependencies {
androidTestImplementation "androidx.benchmark:benchmark-junit4:1.1.0"
androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test:rules:1.3.0"
}import androidx.benchmark.junit4.BenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun myFunctionBenchmark() {
benchmarkRule.measureRepeated {
// Вызов вашей функции или кода для тестирования производительности
myFunction()
}
}
}
Используйте журналирование для измерения времени выполнения определенных операций.
val startTime = System.currentTimeMillis()
// Ваш код
val endTime = System.currentTimeMillis()
Log.d("Performance", "Время выполнения: ${endTime - startTime} мс")
StrictMode помогает обнаружить операции, которые могут замедлить работу приложения, такие как работа с сетью или базой данных в главном потоке.
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}Systrace позволяет собирать и анализировать трассировки производительности системы, предоставляя детализированные данные о времени выполнения различных операций.
adb shell am broadcast -a com.android.systemui.screenshot.ScreenshotService.ACTION_SYSTRACE.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍3
По умолчанию нельзя, потому что дженерики стираются (
Type Erasure) во время компиляции. fun <T> printType(value: T) {
println(T::class.simpleName) // ❌ Ошибка! Нельзя использовать `T::class`
}Если используем
inline fun, можно сделать дженерик "реальным" (reified). inline fun <reified T> printType(value: T) {
println(T::class.simpleName) // ✅ Работает!
}
fun main() {
printType(123) // Int
printType("Hello") // String
printType(3.14) // Double
}Если дженерик используется в классе, то
reified не поможет. class MyGenericClass<T>(private val type: KClass<T>) {
fun printType() {
println(type.simpleName) // ✅ Работает
}
}
fun main() {
val obj = MyGenericClass(String::class)
obj.printType() // String
}Для сложных дженериков (
List<T>, Map<K, V>) используем typeOf<T>() (только с reified). import kotlin.reflect.typeOf
inline fun <reified T> printGenericType() {
val type = typeOf<T>()
println(type) // ✅ List<Int>, Map<String, Boolean> и т. д.
}
fun main() {
printGenericType<List<Int>>() // kotlin.collections.List<kotlin.Int>
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
– drawable/ — изображения,
– layout/ — xml-макеты,
– values/ — строки, цвета, стили, размеры,
– raw/, assets/ — аудио, видео, JSON и прочее.
Хранятся данные, не изменяемые во время выполнения.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Класс-наследник в Kotlin получает доступ к функционалу родительского класса через:
Наследование (
:) — доступ ко всем open методам и свойствам. Ключевое слово
super — вызов методов родителя. Переопределение (
override) — изменение поведения. Класс-наследник получает доступ ко всем
open методам и свойствам родителя. open class Animal(val name: String) {
fun eat() {
println("$name ест")
}
}
class Dog(name: String) : Animal(name)
fun main() {
val dog = Dog("Шарик")
dog.eat() // Шарик ест (метод унаследован)
}Пример
open class Animal {
open fun makeSound() {
println("Животное издаёт звук")
}
}
class Dog : Animal() {
override fun makeSound() {
super.makeSound() // Вызываем метод родителя
println("Гав-гав!")
}
}
fun main() {
val dog = Dog()
dog.makeSound()
}Вывод
Животное издаёт звук
Гав-гав!
Класс-наследник может изменить поведение родительского метода.
open class Animal {
open fun move() {
println("Животное двигается")
}
}
class Bird : Animal() {
override fun move() {
println("Птица летит") // Переопределяем метод
}
}
fun main() {
val bird = Bird()
bird.move() // Птица летит
}Пример
open class Animal(val name: String)
class Dog(name: String, val breed: String) : Animal(name) {
fun info() {
println("Имя: $name, Порода: $breed") // Используем `name` из родителя
}
}
fun main() {
val dog = Dog("Бобик", "Лабрадор")
dog.info() // Имя: Бобик, Порода: Лабрадор
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
1. dispatchTouchEvent() — распределяет событие.
2. onTouchEvent() — обрабатывает вью, если не перехвачено.
3. onClick() — вызывается, если был ACTION_UP без движения.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
XML — это eXtensible Markup Language (Расширяемый язык разметки).
eXtensible → Расширяемый (можно создавать свои теги).
Markup → Разметка (использует теги
<tag> для структурирования данных). Language → Язык (формат хранения и передачи данных).
В Android:
Разметка UI (
activity_main.xml). Конфигурация (
AndroidManifest.xml). Ресурсы (
strings.xml, colors.xml). В веб-разработке:
Конфигурации (
.xml файлы в серверных приложениях). Форматы обмена данными (SOAP, RSS, SVG).
Пример XML-кода
<user>
<name>Алиса</name>
<age>25</age>
</user>
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Java: byte, short, int, long, float, double, char, boolean.
Kotlin использует обёртки (Int, Double, Boolean и др.), которые компилируются в примитивы при необходимости.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
В MVI (Model-View-Intent) есть проблема: события, которые не нужно хранить в
State, могут повторно отобразиться при пересоздании экрана (например, при повороте экрана или навигации назад). Показ
Toast / Snackbar Навигация (
openScreen()) Ошибки, которые нужно показать один раз
Это
LiveData, которая отправляет событие только один раз и не пересылает его новым подписчикам. class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
}
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}ViewModel с
SingleLiveEvent class MyViewModel : ViewModel() {
val eventShowToast = SingleLiveEvent<String>()
fun onButtonClicked() {
eventShowToast.value = "Привет, это Toast!"
}
}Activity/Fragment подписывается
viewModel.eventShowToast.observe(viewLifecycleOwner) { message ->
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}В
StateFlow все события хранятся в State (что не подходит для одноразовых событий). Вместо этого используем
SharedFlow с replay = 0, чтобы событие не повторялось. ViewModel с
SharedFlow class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
fun onButtonClicked() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("Привет, это Toast!"))
}
}
}
sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
object NavigateToNextScreen : UiEvent()
}Подписка в UI:
lifecycleScope.launchWhenStarted {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.iss.onessage, Toast.LENGTH_SHORT).show()
is UiEvent.NavigateToNextScreen -> findNavController().navigate(R.id.nextFragment)
}
}
}Если в
State всё-таки нужно хранить событие, но не показывать его повторно, можно использовать EventWrapper. class EventWrapper<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) null else {
hasBeenHandled = true
content
}
}
}ViewModel с
StateFlow: data class UiState(val message: EventWrapper<String>? = null)
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(UiState())
val state = _state.asStateFlow()
fun onButtonClicked() {
_state.value = UiState(message = EventWrapper("Привет, это Toast!"))
}
}
UI подписывается и проверяет
EventWrapper: viewModel.state.collect { state ->
state.message?.getContentIfNotHandled()?.let { message ->
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
- Появился Material Design.
- Новая ART (Android Runtime) вместо Dalvik.
- Поддержка 64-битных устройств.
- Project Volta — улучшения в энергопотреблении.
- Многопользовательская поддержка на телефонах.
- Улучшенная работа с уведомлениями.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊10🔥2👍1
В Android можно создавать интерфейс без XML с помощью Jetpack Compose или программного кода (View в Kotlin/Java).
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()
}
}
}
Если 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
👍2
- equals(Object obj)
- hashCode()
- toString()
- getClass()
- wait()
- notify()
- notifyAll()
- clone()
- finalize() (устаревший)
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊5🔥2
В Kotlin Coroutines есть несколько диспетчеров (
Dispatchers), но Default и IO используются чаще всего. Dispatchers.Default — для тяжёлых вычислений (CPU-операции). Dispatchers.IO — для операций ввода-вывода (сеть, файлы, БД). Этот диспетчер используется, если код загружает процессор (например, сложные вычисления).
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()
}
Этот диспетчер оптимизирован для ввода-вывода (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
👍3
- Использование блоков synchronized,
- Мьютексы,
- java.util.concurrent (например, ConcurrentHashMap),
- Иммутабельные объекты,
- Логика через ExecutorService.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4💊1
Сильная ссылка - это ссылка, которая напрямую указывает на объект и предотвращает его сборку сборщиком мусора.
Использование слабых ссылок (
WeakReference) позволяет определить, был ли объект освобожден сборщиком мусора.LeakCanary использует
ObjectWatcher для отслеживания объектов. Если объект не освобожден, ObjectWatcher уведомляет об утечке.import java.lang.ref.WeakReference;
public class MemoryLeakExample {
public static void main(String[] args) {
// Создание объекта
MyObject myObject = new MyObject();
// Создание слабой ссылки на объект
WeakReference<MyObject> weakRef = new WeakReference<>(myObject);
// Удаление сильной ссылки
myObject = null;
// Вызов сборщика мусора
System.gc();
// Проверка, была ли слабая ссылка освобождена
if (weakRef.get() == null) {
System.out.println("Object has been garbage collected");
} else {
System.out.println("Object is still alive");
}
}
static class MyObject {
// Некоторая логика класса
}
}
Убедитесь, что на объект нет сильных ссылок. Только слабые, мягкие или фантомные ссылки не предотвращают сборку объекта.
Если объект становится недоступным через сильные ссылки, сборщик мусора может его освободить.
Слабые ссылки могут быть использованы для проверки того, был ли объект освобожден.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
- ArrayList, LinkedList,
- HashSet, TreeSet, LinkedHashSet,
- HashMap, TreeMap, LinkedHashMap,
- PriorityQueue, ArrayDeque.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Передача больших данных (например, изображений, видео, JSON) между
Activity требует оптимального подхода, потому что: Intent.putExtra() имеет ограничение по размеру (~1MB). Передача
Bitmap в Intent может вызвать TransactionTooLargeException. Большие данные лучше передавать через
Uri, БД или FileProvider. Неправильный способ (НЕ ДЕЛАТЬ!) –
Bitmap через Intent val bitmap: Bitmap = getBitmap()
val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // ❌ ОПАСНО! Может вызвать Exception
startActivity(intent)
Сохраняем изображение во
File и получаем Uri fun saveBitmapToFile(context: Context, bitmap: Bitmap): Uri {
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}
return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
}Передаём
Uri через Intent val uri = saveBitmapToFile(this, bitmap)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Даем доступ другому Activity
}
startActivity(intent)
Получаем
Uri в ImageActivity и загружаем изображение val uriString = intent.getStringExtra("image_uri")
val uri = Uri.parse(uriString)
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
imageView.setImageBitmap(bitmap)Если изображение уже хранится в базе данных (
Room), передаём ID записи, а не сам файл. val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image_id", imageId) // Передаём только ID
startActivity(intent)
Сохраняем путь
val filePath = saveBitmapToFile(this, bitmap).toString()
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_image", filePath)
.apply()
Читаем путь в
Activity val filePath = getSharedPreferences("app_prefs", MODE_PRIVATE)
.getString("last_image", null)
val bitmap = BitmapFactory.decodeFile(filePath)
imageView.setImageBitmap(bitmap)Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
Использовать remember, derivedStateOf, key и мемоизацию функций. Также важно следить, чтобы State не обновлялся без необходимости, а структура UI не пересоздавалась без причины.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Если профайлер показывает, что рендеринг какого-либо фрейма занял 120 миллисекунд, это означает, что этот фрейм выполнялся слишком долго, что приводит к фризам и лагам в пользовательском интерфейсе.
В Android интерфейс обновляется 60 раз в секунду (частота 60 FPS). Это значит, что каждый кадр (фрейм) должен рендериться не дольше 16,67 мс (1000 мс / 60 FPS).
Если рендеринг кадра занимает 120 мс, то за это время устройство должно было бы нарисовать 7 кадров (120 / 16,67 ≈ 7). Однако оно успело обработать только один, что приводит к заметному подтормаживанию.
Тяжёлые вычисления в основном потоке (UI Thread) – например, сложные математические операции, работа с JSON, парсинг файлов.
Долгие операции с рендерингом – сложные векторные изображения, перегруженные
Canvas.draw() или анимации. Синхронные вызовы I/O (чтение файлов, базы данных, сети) – если, например, в
onDraw() идёт обращение к диску или базе данных. Неоптимальный layout – глубокая иерархия
ViewGroup, частые перерасчёты макетов (measure/layout). Coroutines, Executors, WorkManagerдля поиска "узких мест".
избегать сложных
onDraw(), использовать ViewStub, RecyclerView. убрать ненужные
ViewGroup, использовать ConstraintLayout.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Минимальные шаги:
1. Создать Activity — она является точкой входа для UI.
2. Установить content view — это может быть XML или программно созданный View.
3. Убедиться, что в манифесте указана MainActivity как LAUNCHER.
4. При запуске устройства система вызывает onCreate(), и в этот момент UI "привязывается" к экрану.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊6🔥1