Kotlin | Вопросы собесов
2.56K subscribers
30 photos
972 links
Download Telegram
🤔 Зачем нужны Data Class и Classes в Kotlin?

Data Class удобен для представления структур данных, таких как модели, а обычные классы позволяют реализовывать логику и обработку поведения.

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

В Java класс Object является корневым классом для всех остальных классов. Это значит, что все классы в Java неявно наследуются от Object, если явно не указано другое.

🚩Основные методы класса `Object`

🟠`equals(Object obj)`
Определяет, равны ли два объекта. По умолчанию использует сравнение по ссылке (==), но может быть переопределён для логического сравнения.
     class Person {
String name;

Person(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}

public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
}
}


🟠`hashCode()`
Возвращает числовой хеш-код объекта, используемый, например, в HashMap. Если переопределяем equals(), нужно переопределить и hashCode().
     @Override
public int hashCode() {
return Objects.hash(name);
}


🟠`toString()`
Возвращает строковое представление объекта. По умолчанию – имя класса + хеш-код, но лучше переопределять.
     @Override
public String toString() {
return "Person{name='" + name + "'}";
}


🟠`getClass()`
Возвращает объект Class, описывающий класс объекта.
     System.out.println(p1.getClass().getName()); // Person


🟠`clone()`
Создаёт копию объекта (поверхностное клонирование). Объект должен реализовать Cloneable, иначе будет CloneNotSupportedException.
     class Person implements Cloneable {
String name;

Person(String name) {
this.name = name;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


🟠`finalize()` *(устарел, не рекомендуется использовать)*
Вызывается перед удалением объекта сборщиком мусора. Лучше использовать try-with-resources и close().
     @Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}


🟠`wait()`, `notify()`, `notifyAll()`
Методы для работы с многопоточностью. wait() – приостанавливает поток до вызова notify(). notify() – пробуждает один поток. notifyAll() – пробуждает все потоки.
     class SharedResource {
synchronized void doWait() throws InterruptedException {
wait();
}

synchronized void doNotify() {
notify();
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как в оригинальном Java equals работает, что он сравнивает?

1. Класс Object:
- В equals по умолчанию сравниваются ссылки на объекты (ссылочное равенство).
2. Переопределение:
- Для пользовательских классов метод equals переопределяют, чтобы сравнивать содержимое объектов.


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

Пропадание пользовательских данных при повороте экрана в Android-приложениях является распространенной проблемой, связанной с тем, как Android управляет жизненным циклом активности. Когда устройство поворачивается, система уничтожает текущую активность и создает её заново, что приводит к потере данных, если они не были сохранены должным образом.

🚩Причины пропадания данных при повороте экрана

🟠Пересоздание активности
🟠Неправильное управление состоянием

🚩Способы предотвращения потери данных

Сохранение состояния с помощью onSaveInstanceState и onRestoreInstanceState
class 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 в ViewModel
class 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💊1
🤔 Какие ещё виды map знаешь в RxJava?

Кроме map и flatMap, в RxJava есть switchMap — отменяет предыдущий поток при каждом новом вызове, concatMap — сохраняет порядок выполнения, и exhaustMap — игнорирует новые вызовы до завершения текущего.


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

Запрос данных из двух таблиц обычно выполняется с помощью операции объединения (JOIN) в SQL. Алгоритм и его сложность зависят от типа объединения, структуры данных и используемой базы данных.

🚩Пример запроса

Предположим, у нас есть две таблицы: employees и departments. Мы хотим получить список сотрудников вместе с их отделами.
SELECT employees.name, departments.name
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;


🚩Алгоритмы выполнения JOIN

1⃣Nested Loop Join (Вложенные циклы)
Для каждой строки из первой таблицы выполняется поиск соответствующих строк во второй таблице.
for each row in employees:
for each row in departments:
if row.employees.department_id == row.departments.id:
yield (row.employees.name, row.departments.name)


2⃣Hash Join (Хеш-объединение)
Создается хеш-таблица для одной из таблиц на основе ключа соединения. Для каждой строки из другой таблицы проверяется наличие соответствующего ключа в хеш-таблице.
hash_table = {}
for each row in departments:
hash_table[row.id] = row.name
for each row in employees:
if row.department_id in hash_table:
yield (row.name, hash_table[row.department_id])


3⃣Sort-Merge Join (Сортировка и слияние)
Обе таблицы сортируются по ключу соединения, затем выполняется слияние отсортированных списков.
sorted_employees = sort(employees, key=lambda x: x.department_id)
sorted_departments = sort(departments, key=lambda x: x.id)
i, j = 0, 0
while i < len(sorted_employees) and j < len(sorted_departments):
if sorted_employees[i].department_id == sorted_departments[j].id:
yield (sorted_employees[i].name, sorted_departments[j].name)
i += 1
j += 1
elif sorted_employees[i].department_id < sorted_departments[j].id:
i += 1
else:
j += 1


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

Kotlin предоставляет более лаконичный и безопасный синтаксис по сравнению с Java, что уменьшает количество шаблонного кода. Он встроен в экосистему Android и полностью совместим с существующими Java-библиотеками. Kotlin поддерживает функции, такие как null-безопасность, корутины для асинхронного программирования и расширяемость, что повышает производительность разработки. Кроме того, Kotlin позволяет писать код быстрее и с меньшим количеством ошибок.

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

TransactionTooLargeException — это исключение, которое возникает, если передаваемые данные превышают лимит 1MB в Binder.
Чаще всего ошибка появляется при передаче больших объектов через Intent, Bundle, `onSaveInstanceState().

🚩Как воспроизвести `TransactionTooLargeException`?

Передача большого Bitmap через Intent (ОШИБКА!)
val bitmap: Bitmap = getLargeBitmap() //  Очень большой объект

val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // Ошибка: TransactionTooLargeException

startActivity(intent)


Ошибка
android.os.TransactionTooLargeException: data parcel size 2MB is larger than 1MB


🚩Как избежать `TransactionTooLargeException`?

Использовать FileProvider и Uri вместо Bitmap
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}

val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(intent)


🚩Использовать `ViewModel` вместо `onSaveInstanceState()`

Ошибка: сохранение больших данных в onSaveInstanceState()
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable("largeData", myLargeObject) // ОШИБКА!
super.onSaveInstanceState(outState)
}


Решение → Используем ViewModel, чтобы хранить данные в памяти:
class MyViewModel : ViewModel() {
var largeData: MyLargeObject? = null
}


🚩Хранить данные в `SharedPreferences` или `Room`

Если данные не нужны постоянно → используем SharedPreferences
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_data", jsonData)
.apply()


Если данные большие и важные → используем Room
@Dao
interface MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveData(data: MyData)
}


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

Можно описать набор состояний, каждое из которых — отдельный подкласс sealed-класса. Это удобно для моделирования состояний UI, например: Loading, Success, Error. Все возможные состояния известны компилятору, что делает обработку безопасной.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Как подключить 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
👍4
🤔 Способы навигации в Kotlin

Jetpack Navigation Component — современный способ навигации, основанный на графе навигации и безопасных аргументах. Позволяет легко переходить между фрагментами и активностями, а также управлять состоянием навигации.
FragmentTransaction — ручной способ добавления, замены и удаления фрагментов.
Navigation через Intent — используется для переключения между активностями или фрагментами внутри приложения или между приложениями.
Explicit и Implicit Intents — явные и неявные намерения для навигации между компонентами.
NavHostFragment и NavController — связаны с использованием графа навигации для управления фрагментами в приложениях Android.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7🔥1
🤔 Может ли потеря state быть связанной с фрагментом?

Да, потеря состояния (state) может быть связана с фрагментами (Fragments) в Android. Это довольно распространённая проблема, особенно при работе с динамическими интерфейсами. Давайте разберёмся, почему она возникает, как её предотвратить и какие решения существуют.

🚩Почему происходит потеря состояния во фрагментах?

🟠Жизненный цикл фрагмента
Фрагменты имеют сложный жизненный цикл, который тесно связан с активностью. Основные этапы:
onCreate() — создаётся фрагмент, инициализируются объекты.
onViewCreated() — создаётся View (UI компоненты).
onStart() / onResume() — фрагмент становится видимым и активным.
onPause() / onStop() — фрагмент приостанавливается.
onDestroyView() — уничтожается только View (UI), но сам фрагмент всё ещё существует.
onDestroy() — полностью уничтожается фрагмент.
Фрагменты могут пересоздаваться системой, например, при смене ориентации экрана или нехватке памяти. Если разработчик неправильно сохраняет состояние фрагмента, оно может быть утеряно.

🟠Удаление View фрагмента системой
Когда фрагмент переходит в состояние onDestroyView(), его View уничтожается, но сам объект фрагмента сохраняется. Если пользователь вернётся к этому фрагменту, View будет пересоздана, и вы потеряете все изменения, сделанные ранее, если они не сохранены явно.

🟠Проблемы с менеджером фрагментов
Использование FragmentManager или FragmentTransaction с неправильными методами, такими как replace() или add(), без должного управления стэком (back stack), может привести к пересозданию или дублированию фрагментов, что вызывает потерю состояния.

🟠Проблемы с `savedInstanceState`
Фрагменты, как и активности, используют механизм сохранения состояния через BundleonSaveInstanceState). Если состояние не сохранено правильно, данные могут быть потеряны.

🚩Как предотвратить потерю состояния во фрагментах?

🟠Сохранение данных в `onSaveInstanceState`
При уничтожении фрагмента система вызывает метод onSaveInstanceState(). Здесь вы можете сохранить важные данные в Bundle.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key_text", editText.text.toString())
}


При пересоздании фрагмента данные можно восстановить в onViewCreated()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val restoredText = savedInstanceState?.getString("key_text")
editText.setText(restoredText)
}


🟠Использование `ViewModel`
Для хранения состояния, которое переживает уничтожение и пересоздание фрагмента, лучше использовать архитектурный компонент ViewModel.
1⃣Создайте ViewModel
class MyViewModel : ViewModel() {
val textData = MutableLiveData<String>()
}


2⃣Используйте его во фрагменте
class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

// Восстановление данных
viewModel.textData.observe(viewLifecycleOwner) { text ->
editText.setText(text)
}

// Сохранение данных при изменении
editText.addTextChangedListener {
viewModel.textData.value = it.toString()
}
}
}


🟠Использование `FragmentManager` правильно
При работе с фрагментами всегда добавляйте их в back stack, если вы хотите сохранить состояние.
val fragment = MyFragment()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit()


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

Если используется делегирование интерфейса, то необходимо переопределить функции этого интерфейса. Это может быть getValue, setValue или любые кастомные функции, если используется пользовательский делегат.


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

В Android есть два основных типа потоков:
1. UI Thread (главный поток) → отвечает за интерфейс и обработку событий.
2. Worker Thread (фоновый поток) → используется для долгих операций (запросы в сеть, работа с БД).

🚩`UI Thread` – главный поток

- Отвечает за отрисовку интерфейса (Activity, Fragment, View).
- Обрабатывает нажатия, свайпы, анимации.
- Все setText(), setImageResource() и другие UI-методы работают только здесь. Если делать тяжёлые операции в UI Thread, приложение зависнет (ANR - Application Not Responding)!
val url = URL("https://example.com")
val connection = url.openConnection() as HttpURLConnection // Ошибка!


🚩`Worker Thread` – фоновый поток

- Выполняет долгие операции, не блокируя UI:
Запросы в сеть (Retrofit, OkHttp)
Чтение/запись в БД (Room, SQLite)
Обработку файлов, JSON, XML
Любые тяжёлые вычисления
CoroutineScope(Dispatchers.IO).launch {
val url = URL("https://example.com")
val connection = url.openConnection() as HttpURLConnection // Работает!
}


🚩Как переключаться между `UI Thread` и `Worker Thread`?
Способ 1: Coroutines (лучший вариант)
CoroutineScope(Dispatchers.IO).launch {
val data = fetchData() // Работает в `Worker Thread`

withContext(Dispatchers.Main) {
textView.text = data // Назад в `UI Thread`
}
}


Способ 2: Handler + Thread (старый вариант)
val handler = Handler(Looper.getMainLooper())

Thread {
val data = fetchData() // Работает в `Worker Thread`

handler.post {
textView.text = data // Назад в `UI Thread`
}
}.start()


Способ 3: AsyncTask (УСТАРЕЛ, НЕ ИСПОЛЬЗОВАТЬ!)
class MyTask : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String {
return fetchData() // `Worker Thread`
}

override fun onPostExecute(result: String) {
textView.text = result // `UI Thread`
}
}


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

Это диапазон целых чисел, заданный началом, концом и шагом.
Он используется для итераций и проверки вхождения числа в диапазон.


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

В Kotlin есть три основных типа коллекций:
List — упорядоченный список элементов.
Set — множество уникальных элементов.
Map — коллекция пар "ключ-значение".

🚩List — упорядоченный список

List — это коллекция, в которой можно хранить дубликаты, а элементы доступны по индексу.
val numbers = listOf(1, 2, 3, 4, 5) // Immutable (нельзя изменять)
val mutableNumbers = mutableListOf(1, 2, 3) // Можно изменять

mutableNumbers.add(4) // Добавляем элемент
mutableNumbers.removeAt(1) // Удаляем элемент по индексу

println(mutableNumbers) // [1, 3, 4]


🚩Set — множество уникальных элементов

Set — это коллекция, в которой нет дубликатов.
val numbers = setOf(1, 2, 3, 3, 4, 5) // Дубликаты удаляются автоматически
println(numbers) // [1, 2, 3, 4, 5]

val mutableNumbers = mutableSetOf(1, 2, 3)
mutableNumbers.add(3) // Не добавится, потому что уже есть
mutableNumbers.add(4) // Добавится

println(mutableNumbers) // [1, 2, 3, 4]


🚩Map — коллекция пар "ключ-значение"

Map — это структура данных, в которой каждому ключу соответствует одно значение.
val userMap = mapOf(
1 to "Alice",
2 to "Bob",
3 to "Charlie"
) // Immutable

val mutableUserMap = mutableMapOf(1 to "Alice", 2 to "Bob")
mutableUserMap[3] = "Charlie" // Добавляем новый ключ-значение
mutableUserMap.remove(1) // Удаляем элемент по ключу

println(mutableUserMap) // {2=Bob, 3=Charlie}


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

- private — для инкапсуляции данных внутри класса;
- protected — если нужно разрешить доступ наследникам и классам в пакете;
- public — для API, доступного извне;
- package-private (без модификатора) — для доступа внутри одного пакета.
Лучше всего ограничивать доступ настолько, насколько это возможно.


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

Google постоянно ограничивает работу сервисов в Android, чтобы:
Уменьшить расход батареи
Оптимизировать использование памяти
Защитить пользователя от фоновых процессов, "убивающих" производительность

🟠Запрет на запуск сервисов в фоне (Android 8+)
Сервис нельзя запустить из фона, если приложение не активно.
startService(Intent) выдаст ошибку, если приложение не на переднем плане.
1. Использовать Foreground Service (с уведомлением).
2. Использовать JobIntentService / WorkManager.
class MyForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, "channelId")
.setContentTitle("Сервис работает")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()

startForeground(1, notification) // Запускаем сервис в Foreground
}

override fun onBind(intent: Intent?): IBinder? = null
}


🟠Запрет на старт сервисов из фона (Android 10+)
Если сервис запущен из фона (например, через BroadcastReceiver), он не запустится.
Исключение – если сервис работает в Foreground или с AlarmManager.
1. Использовать WorkManager (лучший вариант).
2. Использовать Foreground Service с уведомлением.
3. Использовать AlarmManager для периодических задач.

🟠Ограничение работы сервисов в спящем режиме (Doze Mode, Android 6+)
В спящем режиме (Doze Mode) система отключает сервисы.
Приложения не могут выполнять фоновые задачи.
Использовать Foreground Service.
Использовать JobScheduler / WorkManager.
Использовать Firebase Cloud Messaging (FCM) для пробуждения приложения.

🟠Запрет на работу сервисов после закрытия приложения (Android 9+)
Если пользователь смахнул приложение из списка недавних, фоновые сервисы будут убиты.
Решение → Foreground Service + startForeground() или JobScheduler.

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

1. Поля из супер-класса не включаются в автоматически сгенерированные методы equals, hashCode, toString дочернего data-класса.
2. Только свойства, объявленные непосредственно в конструкторе data-класса, участвуют в этих методах.
3. Это ограничивает использование data-классов с наследованием, так как супер-класс теряет влияние на данные.


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

Анонимный класс внутри inline-функции будет инстанцироваться при каждом вызове функции, что ломает цель inline — избавляться от накладных расходов на объекты.

🚩Что такое анонимный класс в Kotlin?

Анонимный класс — это класс без имени, который можно создать с помощью object : Interface {}.
interface ClickListener {
fun onClick()
}

fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата!")
}
}

listener.onClick()
}


🚩Анонимный класс в `inline`-функции

Если мы создадим анонимный класс внутри inline-функции, он будет создаваться каждый раз при вызове функции!
inline fun setClickListener(action: () -> Unit) {
val listener = object : ClickListener {
override fun onClick() {
action()
}
}
listener.onClick()
}

fun main() {
setClickListener { println("Нажато!") }
}


Ошибка компиляции
Inline function cannot contain object expressions


🚩Как обойти ограничение?

Если нужно использовать inline, можно передавать лямбду вместо анонимного класса.
Рабочий вариант
inline fun setClickListener(noinline action: () -> Unit): ClickListener {
return object : ClickListener {
override fun onClick() {
action()
}
}
}

fun main() {
val listener = setClickListener { println("Нажато!") }
listener.onClick()
}


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

ViewGroup является наследником класса View, который является базовым для всех элементов пользовательского интерфейса. ViewGroup выступает как контейнер для других View, предоставляя им общую структуру и управление дочерними элементами. Это позволяет создавать сложные иерархии интерфейсов.

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