Рефлексия — это механизм, позволяющий программе исследовать и изменять свою структуру (классы, методы, поля) во время выполнения. Она позволяет вызывать приватные методы, создавать экземпляры классов по имени и т.д. Это мощный, но небезопасный и затратный по производительности инструмент.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Является жадной коллекцией, что означает, что все операции над элементами выполняются немедленно и целиком. Когда вы применяете функции к списку, такие как
map, filter и т.д., все элементы проходят через каждую функцию сразу же. В данном примере все элементы списка numbers сначала умножаются на 2, затем фильтруются. Вся работа выполняется сразу для каждого элемента.val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers
.map { it * 2 }
.filter { it > 5 }
println(result) // Output: [6, 8, 10]
Является ленивой коллекцией. Это означает, что элементы обрабатываются по мере необходимости. Функции, применяемые к последовательности, создают цепочку операций, которая выполняется только при обращении к элементам. В этом примере
numbers сначала преобразуется в Sequence. Операции map и filter создают цепочку, которая выполняется только при преобразовании обратно в List с помощью toList(). Это позволяет избежать лишних операций и обрабатывать элементы по мере необходимости.val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.asSequence()
.map { it * 2 }
.filter { it > 5 }
.toList()
println(result) // Output: [6, 8, 10]
List: Все элементы обрабатываются сразу при вызове функций.
Sequence: Элементы обрабатываются по мере необходимости, что может повысить эффективность.
List: Подходит для небольших коллекций, где выполнение всех операций сразу не критично.
Sequence: Эффективен для больших коллекций или длинных цепочек операций, так как уменьшает количество промежуточных коллекций.
List: Может использовать больше памяти из-за создания промежуточных коллекций.
Sequence: Уменьшает использование памяти, обрабатывая элементы по одному.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Сильная ссылка - это ссылка, которая напрямую указывает на объект и предотвращает его сборку сборщиком мусора.
Использование слабых ссылок (
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
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2💊1
В 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💊1
Нет, zip сам по себе не гарантирует параллельность.
Если ты просто напишешь zip(firstCall(), secondCall()), оба вызова начнутся последовательно, и zip просто объединит их результаты.
Чтобы получить параллельность:
- Запусти оба вызова через async.
- Затем передай их await() в zip.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1
Библиотека Lifecycle в Android Jetpack помогает управлять и контролировать жизненный цикл компонентов Android, таких как Activities и Fragments. Она упрощает создание компонентов, которые осведомлены о своем жизненном цикле и могут корректно реагировать на изменения в нем. Это позволяет избегать утечек памяти и некорректной работы компонентов при изменении конфигураций или переходах между состояниями.
Это интерфейс, который определяет класс как владеющий жизненным циклом. Activity и Fragment уже реализуют этот интерфейс.
class MyActivity : AppCompatActivity(), LifecycleOwner {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}Это интерфейс, который позволяет классу наблюдать за изменениями в жизненном цикле компонента. Методы, аннотированные
@OnLifecycleEvent, будут вызываться при соответствующих событиях жизненного цикла.class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
// Код, который выполняется при старте жизненного цикла
Log.d("MyObserver", "onStart called")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
// Код, который выполняется при остановке жизненного цикла
Log.d("MyObserver", "onStop called")
}
}Это класс, который содержит информацию о текущем состоянии и позволяет другим объектам наблюдать за изменениями в жизненном цикле.
class MyActivity : AppCompatActivity() {
private lateinit var myObserver: MyObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myObserver = MyObserver()
lifecycle.addObserver(myObserver)
}
}dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
}class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
Log.d("MyObserver", "Activity resumed")
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
Log.d("MyObserver", "Activity paused")
}
}class MyActivity : AppCompatActivity() {
private lateinit var myObserver: MyObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myObserver = MyObserver()
lifecycle.addObserver(myObserver)
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
- Стек (Stack) — хранит локальные переменные, параметры, вызовы функций. Объём ограничен (обычно от 512 КБ до 2 МБ на поток).
- Куча (Heap) — используется для динамического размещения объектов (new). Размер зависит от платформы и может быть от нескольких МБ до нескольких ГБ.
Стек быстрее, но меньше. Куча медленнее (и управляется GC), но объёмнее.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Пропадание пользовательских данных при повороте экрана в Android-приложениях является распространенной проблемой, связанной с тем, как Android управляет жизненным циклом активности. Когда устройство поворачивается, система уничтожает текущую активность и создает её заново, что приводит к потере данных, если они не были сохранены должным образом.
Сохранение состояния с помощью
onSaveInstanceState и onRestoreInstanceStateclass MainActivity : AppCompatActivity() {
private var userData: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState != null) {
userData = savedInstanceState.getString("USER_DATA_KEY")
// Восстановите данные в пользовательском интерфейсе
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("USER_DATA_KEY", userData)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
userData = savedInstanceState.getString("USER_DATA_KEY")
// Восстановите данные в пользовательском интерфейсе
}
}Использование Retain Fragment. Этот метод сохраняет данные, используя фрагмент, который сохраняет своё состояние при пересоздании активности.
class RetainFragment : Fragment() {
var userData: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
}
class MainActivity : AppCompatActivity() {
private lateinit var retainFragment: RetainFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val fragmentManager = supportFragmentManager
retainFragment = fragmentManager.findFragmentByTag("RETAIN_FRAGMENT") as RetainFragment?
?: RetainFragment().also {
fragmentManager.beginTransaction().add(it, "RETAIN_FRAGMENT").commit()
}
// Используйте данные из RetainFragment
val userData = retainFragment.userData
}
}Использование
SavedStateHandle в ViewModelclass UserViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
var userData: String?
get() = savedStateHandle.get("USER_DATA_KEY")
set(value) = savedStateHandle.set("USER_DATA_KEY", value)
}
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(UserViewModel::class.java)
// Используйте данные из ViewModel
val userData = viewModel.userData
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Dagger – это фреймворк для внедрения зависимостей (DI), который помогает управлять созданием и передачей объектов в приложении. В основе Dagger лежат граф зависимостей, который состоит из следующих ключевых элементов:
@Module – это класс, который предоставляет зависимости. @Provides – аннотация, указывающая, что метод создаёт объект для графа Dagger. @Module
class NetworkModule {
@Provides
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
@Inject в конструкторе – позволяет Dagger автоматически создавать объект без @Module. class ApiService @Inject constructor(private val retrofit: Retrofit) {
fun fetchData() { /*...*/ }
}@Component – это интерфейс, который соединяет модули и классы, где нужны зависимости. @Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity) // Указываем, куда внедрять зависимости
}
Используется для создания одного экземпляра объекта на всё приложение.
@Singleton
@Component(modules = [NetworkModule::class])
interface AppComponent
Если у нас есть интерфейс и его реализация, лучше использовать
@Binds вместо @Provides. interface Repository {
fun getData(): String
}
class RepositoryImpl @Inject constructor() : Repository {
override fun getData() = "Data from repository"
}
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindRepository(repo: RepositoryImpl): Repository
}Если нужно создать зависимости с разными областями жизни (например,
Activity, Fragment), используют @Subcomponent. @Subcomponent
interface ActivityComponent {
fun inject(activity: MainActivity)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
ли double, так как они подвержены проблемам сравнения с плавающей точкой. switch работает с int, byte, short, char, enum, String, а также с их обёрточными типами.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
В Android при нажатии пользователя на экран вызывается событие ACTION_DOWN.
Android использует систему обработки касаний через MotionEvent. Когда пользователь касается экрана, система генерирует разные типы событий:
ACTION_DOWN – вызывается в момент первого касания.
ACTION_MOVE – вызывается, когда пользователь двигает палец по экрану.
ACTION_UP – вызывается, когда пользователь убирает палец с экрана.
ACTION_CANCEL – вызывается, если система прерывает касание (например, из-за входящего звонка).
Для обработки событий касания нужно переопределить метод
onTouchEvent() в View или использовать setOnTouchListener(). override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.d("TouchEvent", "Палец коснулся экрана")
return true
}
MotionEvent.ACTION_UP -> {
Log.d("TouchEvent", "Палец отпущен")
}
}
return super.onTouchEvent(event)
}Можно также назначить слушатель
view.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
Log.d("TouchEvent", "Нажатие зафиксировано")
true
} else {
false
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2🔥2💊1
APK (Android Package) — это архив с кодом приложения, ресурсами и манифестом.
Приложение должно быть подписано, чтобы его можно было установить на устройство.
- Это черновая версия APK, которая не имеет цифровой подписи.
- Такой APK можно запустить только в эмуляторе или при отладке (
debug build). - Google Play не принимает неподписанные APK.
При сборке
debug-версии в Android Studio: gradlew assembleDebug
Попытка установить неподписанный APK
adb install app-unsigned.apk
Ошибка
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]
- Подписанный APK содержит цифровую подпись, которая гарантирует, что код не был изменён.
- Android проверяет ключ подписи перед установкой.
Google Play требует подписанный APK или AAB.
Как подписать APK вручную?
apksigner sign --ks my-release-key.jks --out app-signed.apk app-unsigned.apk
Подпись APK гарантирует*
Целостность → код не был изменён после сборки.
Подлинность → приложение подписано разработчиком, а не злоумышленником.
Обновления → только приложения с тем же ключом могут обновлять старую версию.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
- public — доступен везде (по умолчанию).
- internal — доступен в пределах модуля.
- protected — доступен внутри класса и подклассов.
- private — доступен внутри файла или класса.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
sealed class – это ограниченная иерархия классов, где можно создавать разные подклассы с разными свойствами. enum class – это фиксированный набор однотипных объектов, которые не имеют разной структуры. Когда значения не изменятся (например, дни недели, цвета, статусы).
Когда у всех значений одинаковая структура.
enum class Status {
LOADING, SUCCESS, ERROR
}Можно добавлять свойства и методы
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF");
fun printHex() = println(hex)
}
fun main() {
val color = Color.RED
println(color.hex) // #FF0000
color.printHex() // #FF0000
}Когда у состояний разные параметры и поведение.
Когда нужен
when, который проверяет все возможные подклассы. sealed class Status {
object Loading : Status()
data class Success(val data: String) : Status()
data class Error(val message: String) : Status()
}Использование с
when (без else) fun handleStatus(status: Status) {
when (status) {
is Status.Loading -> println("Загрузка...")
is Status.Success -> println("Данные: ${status.data}")
is Status.Error -> println("Ошибка: ${status.message}")
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Основные операторы:
- merge — объединяет элементы из нескольких источников.
- concat — объединяет источники последовательно.
- zip — комбинирует элементы из разных источников попарно.
- combineLatest — объединяет последние элементы.
- switchMap — переключается на новый поток и отменяет предыдущий.
- amb — выбирает тот источник, который быстрее начнёт эмиссию.
- join, groupJoin — объединяют потоки с логикой по времени жизни элементов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
ArrayList реализован на основе массива. Амортизированное O(1). Если массив не заполнен, элемент добавляется в конец списка. В случае, если массив заполнен, требуется выделение нового массива и копирование элементов, что занимает O(n) времени.
val arrayList = ArrayList<Int>()
arrayList.add(1) // Быстрая вставка в конец
O(n). Необходимо сдвинуть все элементы, начиная с позиции вставки, чтобы освободить место для нового элемента. Это требует времени, пропорционального количеству элементов после позиции вставки.
arrayList.add(0, 2) // Медленная вставка в начало
arrayList.add(1, 3) // Медленная вставка в середину
LinkedList реализован на основе узлов, где каждый узел содержит ссылку на следующий и/или предыдущий узел (в случае двусвязного списка).val linkedList = LinkedList<Int>()
linkedList.addFirst(1) // Быстрая вставка в начало
O(1) (если есть ссылка на последний узел) или O(n) (если необходимо пройти весь список). Если список двусвязный и хранится ссылка на последний элемент, вставка в конец осуществляется за O(1). В противном случае, требуется пройти весь список до конца.
linkedList.addLast(2) // Быстрая вставка в конец, если есть ссылка на последний узел
O(n). Необходимо пройти список до нужной позиции и изменить ссылки узлов.
linkedList.add(1, 3) // Медленная вставка в середину
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
- FragmentManager управляет фрагментами: добавление, поиск, стек возврата.
- FragmentTransaction используется для выполнения операций с фрагментами (добавление, замена, удаление). Это как "транзакция базы данных", применяемая к UI.
Transaction создаётся через FragmentManager.beginTransaction() и применяется через commit().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2💊2