---
title: "Android – Компоненти и View"
url: "https://romankryvolapov.com/bg/android-components-and-views/"
description: "Жизнени цикли на Activity, Fragment, View и Service, устройство на Context, Intent, WorkManager, RecyclerView и разликите между MVP, MVVM, MVC и MVI."
language: bg
published: 2022-02-24
updated: 2026-11-13
---
## Какъв е жизненият цикъл на Activity, Fragment и View

## Activity Lifecycle . (жизнен цикъл на Activity)

**onCreate():** \
Извиква се при създаването на Activity. Това е мястото за инициализация на основните компоненти на Activity, като потребителския интерфейс (UI) и данните.

**onStart():** \
Извиква се, когато Activity става видима за потребителя.

**onResume():** \
Извиква се, когато Activity започва да взаимодейства с потребителя. Activity е на преден план и потребителят може да взаимодейства с нея.

...

**onPause():** \
Извиква се, когато Activity преминава на заден план, но все още е видима. Използва се за запазване на данни, спиране на анимации или други ресурси, които не трябва да продължават да работят във фонов режим.

**onStop():** \
Извиква се, когато Activity вече не е видима за потребителя. Това се случва, когато Activity се затваря или преминава във фонов режим.

**onDestroy():** \
Извиква се, преди Activity да бъде окончателно унищожена. Това е мястото за освобождаване на всички останали ресурси.

**onRestart():** \
Извиква се, когато Activity е била спряна и се стартира отново, преди onStart().

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.util.Log


class MainActivity : AppCompatActivity() {

    private val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG, "onCreate called")
    }

    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart called")
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume called")
    }

    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause called")
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop called")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy called")
    }

    override fun onRestart() {
        super.onRestart()
        Log.d(TAG, "onRestart called")
    }

}
```

## Fragment Lifecycle . (жизнен цикъл на Fragment)

**onAttach():** \
Извиква се, когато Fragment се прикрепя към своята Activity.

**onCreate():** \
Извиква се при създаването на Fragment. Тук могат да се извършват инициализации, които не зависят от потребителския интерфейс.

**onCreateView():** \
Извиква се за създаването на потребителския интерфейс на Fragment. Тук трябва да се инфлейтне (надуе) макетът на Fragment.

**onViewCreated():** \
Извиква се, след като изгледът е създаден. Тук могат да се извършват окончателните инициализации на изгледите.

**onActivityCreated():** \
Извиква се, когато методът onCreate на Activity е приключил.

**onStart():** \
Извиква се, когато Fragment става видим за потребителя.

**onResume():** \
Извиква се, когато Fragment става активен и е готов да взаимодейства с потребителя.

...

**onPause():** \
Извиква се, когато Fragment преминава на заден план, но все още е видим.

**onStop():** \
Извиква се, когато Fragment вече не се вижда.

**onDestroyView():** \
Извиква се, преди изгледът на Fragment да бъде унищожен.

**onDestroy():** \
Извиква се, преди Fragment да бъде унищожен.

**onDetach():** \
Извиква се, когато Fragment се отделя от своята Activity.

```kotlin
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.util.Log
import androidx.fragment.app.Fragment


class ExampleFragment : Fragment() {

    private val TAG = "ExampleFragment"

    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.d(TAG, "onAttach called")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "onCreate called")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.d(TAG, "onCreateView called")
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_example, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d(TAG, "onViewCreated called")
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.d(TAG, "onActivityCreated called")
    }

    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart called")
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume called")
    }

    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause called")
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop called")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(TAG, "onDestroyView called")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy called")
    }

    override fun onDetach() {
        super.onDetach()
        Log.d(TAG, "onDetach called")
    }

}
```

## View Lifecycle . (жизнен цикъл на View)

**Constructor:** \
при създаването на потребителски изглед първо трябва да припокриете конструктора, който инициализира View – в него могат да се извършват необходимите изчисления

**onAttachedToWindow():** \
Извиква се, когато View се прикрепя към прозореца.

**onMeasure(int widthMeasureSpec, int heightMeasureSpec):** \
Извиква се за определяне на размера на View и на неговите дъщерни елементи.

**onLayout(boolean changed, int left, int top, int right, int bottom):** \
Извиква се за задаване на размера и позицията на View и на неговите дъщерни елементи.

**onDraw(Canvas canvas):** \
Извиква се за отрисуване на съдържанието на View.

...

**onDetachedFromWindow():** \
Извиква се, когато View се отделя от прозореца.

```kotlin
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
import android.view.View


class CustomView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val TAG = "CustomView"

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        Log.d(TAG, "onAttachedToWindow called")
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        Log.d(TAG, "onMeasure called")
        // Example: Set the size to be exactly 200x200 pixels
        setMeasuredDimension(200, 200)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        Log.d(TAG, "onLayout called")
        // Layout the child views if any
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        Log.d(TAG, "onDraw called")
        // Draw the custom view content
        canvas?.drawColor(android.graphics.Color.RED)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        Log.d(TAG, "onDetachedFromWindow called")
    }

}
```

## ViewModel Lifecycle . (жизнен цикъл на ViewModel)

**onCleared():** \
Извиква се, когато ViewModel се унищожава. Тук могат да се освобождават ресурси, като например отмяна на задачи, затваряне на нишки и т.н.

```kotlin
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel


class MyViewModel : ViewModel() {

    override fun onCleared() {
        super.onCleared()
        // Освобождаване на ресурси, ако е необходимо
    }

}
```

## Service Lifecycle . (жизнен цикъл на Service)

**onCreate():** \
Извиква се при първото създаване на Service. Тук могат да се извършват инициализации на необходимите ресурси.

**onStartCommand(Intent intent, int flags, int startId):** \
Извиква се при всяко стартиране на Service. Тук се извършва основната работа на Service.

**onBind(Intent intent):** \
Извиква се, когато компонент (например Activity) се свързва със Service чрез bindService(). Този метод се използва само за свързани Service.

...

**onUnbind(Intent intent):** \
Извиква се, когато компонент се отделя от свързания Service.

**onRebind(Intent intent):** \
Извиква се, ако компонент се свърже повторно със Service след извикването на onUnbind().

**onDestroy():** \
Извиква се, преди Service да бъде унищожен. Тук могат да се освобождават ресурси.

```kotlin
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log


class MyService : Service() {

    private val TAG = "MyService"

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "Service Created")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "Service Started")

        // Изпълнение на фонова работа
        Thread(Runnable {
            try {
                // Симулация на дълга работа
                Thread.sleep(3000)
                Log.d(TAG, "Service Running")
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            stopSelf()
        }).start()

        // Ако Service трябва да бъде рестартиран след прекратяване, връщаме START_STICKY
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        Log.d(TAG, "Service Bound")
        return null
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "Service Destroyed")
    }

}
```

![](/images/2019/02/1_0ZSbfAf9P2MDEqMTMosZdA.webp)

![](/images/2019/02/1_8Y9fXrqxH28vPJe_VcfwuA.webp)

## Какви слоеве има в чистата архитектура и за какво отговарят

Обикновено приложението се разделя на 3 слоя, които могат да се намират в различни модули – Presentation, Domain, Data:

**Presentation:**\
UI, ViewModel и други подобни класове

**Domain / Business Logic:**\
интерфейси на UseCase и техните реализации\
интерфейси на хранилищата (без реализация)\
локални модели на данни, с които работи приложението

**Data:**\
реализации на интерфейсите на хранилищата\
мрежови модели на данни\
модели на базата данни\
интерфейси на Network API

**Обикновено зависимостите в Gradle се настройват по следния начин:**

**Presentation:**\
implementation project(“:domain”)\
implementation project(“:data”)

**Data:**\
implementation project(‘:domain’)

**Domain:**\
// no

## От какви елементи се състои едно Android приложение

**Activity:** \
това е основният компонент на потребителския интерфейс, който обикновено представлява един екран на приложението. Всяка Activity има свой жизнен цикъл и може да стартира други Activity.

**Fragment:** \
това са компоненти, които могат да се използват вътре в Activity за изграждане на по-сложен потребителски интерфейс. Fragment също имат свой жизнен цикъл и могат да се използват за създаване на повторно използваеми компоненти.

**Service:** \
това са компоненти, които изпълняват фонови операции в приложението, без да взаимодействат с потребителя. Service могат да работят във фонов режим дори след като потребителят е затворил приложението.

**Broadcast Receiver:** \
това са компоненти, които позволяват на приложението да получава съобщения от системата или от други приложения, дори когато приложението не е активно. Broadcast Receiver могат да се използват за реагиране на системни събития, като промяна на състоянието на мрежата или на зареждането на батерията.

**Content Provider:** \
това са компоненти, които позволяват на приложението да съхранява данни и да ги споделя с други приложения или със системата. Content Provider могат да се използват за достъп до данни, съхранявани в база данни или на отдалечен сървър.

**AndroidManifest.xml:** \
това е файл, който съдържа информация за компонентите на приложението и неговите настройки. Манифестът на приложението определя и разрешенията, необходими за достъп до различните функции на устройството.

## Каква е разликата между MVP, MVVM, MVC, MVI

**Model-View-Controller (MVC):**\
разделя приложението на три компонента: Model, View и Controller.

**Model:** \
Управлява данните и бизнес логиката.

**View:** \
Отговаря за показването на данните и за взаимодействието с потребителя.

**Controller:** \
Посредник между View и Model, обработва действията на потребителя и обновява View.

```kotlin
// Model
data class User(val name: String, val age: Int)

// Controller
class UserController(private val view: UserView) {

    private val user = User("John Doe", 25)

    fun loadUser() {
        view.showUser(user)
    }

}

// View
interface UserView {
    fun showUser(user: User)
}

// Activity implementing View
class MainActivity : AppCompatActivity(), UserView {

    private lateinit var controller: UserController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        controller = UserController(this)
        controller.loadUser()
    }

    override fun showUser(user: User) {
        // Update UI with user data
        findViewById<TextView>(R.id.textView).text = "${user.name}, ${user.age}"
    }

}
```

**Model-View-Presenter (MVP)**:\
разделя приложението на три компонента: Model, View и Presenter.

**Model:** \
Управлява данните и бизнес логиката.

**View:** \
Отговаря за показването на данните и за взаимодействието с потребителя.

**Presenter:** \
Посредник между View и Model, обработва действията на потребителя и обновява View.

```kotlin
// Model
data class User(val name: String, val age: Int)

// View
interface UserView {
    fun showUser(user: User)
}

// Presenter
class UserPresenter(private val view: UserView) {

    private val user = User("John Doe", 25)

    fun loadUser() {
        view.showUser(user)
    }

}

// Activity implementing View
class MainActivity : AppCompatActivity(), UserView {

    private lateinit var presenter: UserPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        presenter = UserPresenter(this)
        presenter.loadUser()
    }

    override fun showUser(user: User) {
        // Update UI with user data
        findViewById<TextView>(R.id.textView).text = "${user.name}, ${user.age}"
    }

}
```

**Model-View-ViewModel (MVVM):**\
разделя приложението на три компонента: Model, View и ViewModel.

**Model:** \
Управлява данните и бизнес логиката.

**View:** \
Отговаря за показването на данните и за взаимодействието с потребителя.

**ViewModel:** \
Посредник между View и Model, управлява данните за View и обработва действията на потребителя.

```kotlin
// Model
data class User(val name: String, val age: Int)

// ViewModel
class UserViewModel : ViewModel() {

    private val _user = MutableLiveData<User>()
    val user: LiveData<User> get() = _user

    fun loadUser() {
        _user.value = User("John Doe", 25)
    }

}

// Activity
class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        viewModel.user.observe(this, Observer { user ->
            // Update UI with user data
            findViewById<TextView>(R.id.textView).text = "${user.name}, ${user.age}"
        })
        viewModel.loadUser()
    }

}
```

**Model-View-Intent (MVI):**\
основава се на концепциите на реактивното програмиране и на еднопосочния поток на данните (unidirectional data flow).

**Model:** \
Управлява данните и бизнес логиката.

**View:** \
Отговаря за показването на данните и за взаимодействието с потребителя.

**Intent:** \
Определя намеренията на потребителя и ги насочва към обработка.

```kotlin
// Model
data class User(val name: String, val age: Int)

// View State
data class UserViewState(val user: User? = null)

// Intent
sealed class UserIntent {
    object LoadUser : UserIntent()
}

// ViewModel
class UserViewModel : ViewModel() {

    private val _viewState = MutableLiveData<UserViewState>()
    val viewState: LiveData<UserViewState> get() = _viewState

    fun processIntents(intent: UserIntent) {
        when (intent) {
            is UserIntent.LoadUser -> loadUser()
        }
    }

    private fun loadUser() {
        _viewState.value = UserViewState(User("John Doe", 25))
    }

}

// Activity
class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        viewModel.viewState.observe(this, Observer { viewState ->
            viewState.user?.let { user ->
                // Update UI with user data
                findViewById<TextView>(R.id.textView).text = "${user.name}, ${user.age}"
            }
        })
        // Dispatch intent to load user
        viewModel.processIntents(UserIntent.LoadUser)
    }

}
```

**MVP:** \
Удобен за малки приложения. Presenter управлява директно View и Model.

**MVVM:** \
Подхожда добре за приложения с реактивно програмиране и голямо количество UI логика. ViewModel осигурява двупосочно свързване на данните.

**MVC:** \
Най-простият шаблон. Controller управлява взаимодействието между Model и View.

**MVI:** \
Подходящ е за сложни приложения с реактивно програмиране и unidirectional data flow. Intent и View State правят логиката на приложението предсказуема и лесна за тестване.

## Какво е Android ViewModel

**Android ViewModel:** \
това е компонент на архитектурата Jetpack, който помага да се запазват и управляват данните на UI компонентите (Activity, Fragment) при завъртане на екрана, повторно създаване на приложението и други промени в жизнения цикъл на приложението.\
ViewModel е част от архитектурния шаблон Model-View-ViewModel (MVVM) и обикновено се използва заедно с LiveData, за да предоставя актуални данни на потребителския интерфейс.\
ViewModel има само един метод от жизнения цикъл – OnCleared()

**Android SharedViewModel:** \
това е компонент на архитектурата Jetpack, който позволява обмен на данни между два или повече свързани UI компонента (Activity, Fragment) чрез един и същ екземпляр на ViewModel.\
SharedViewModel обикновено се използва, когато два или повече UI компонента трябва да имат достъп до едни и същи данни, но не са пряко свързани помежду си. Например когато имаме списък с елементи в един Fragment, а подробностите за елемента се показват в друг Fragment, който е в друга Activity.

## Какво представляват Android Broadcast Receivers

Компоненти на Android, които позволяват на приложението да получава системни и потребителски съобщения (broadcast) от други приложения, както и от самата система.\
Broadcast Receiver слуша и филтрира съобщенията и ако събитието отговаря на зададените филтри, изпълнява кода, свързан с това събитие.\
Примери за такива събития са промяна на състоянието на мрежата, промяна на нивото на батерията, получаване на съобщение от друго приложение и т.н.

Broadcast Receiver може да се използва за изпълнение на някакви задачи на устройството, например за уведомяване на потребителя за промени в състоянието на устройството или за стартиране на задачи във фонов режим.\
За да използвате Android Broadcast Receiver, трябва да създадете клас, който разширява класа BroadcastReceiver и припокрива метода onReceive(), който ще се извиква при получаване на broadcast съобщение.

```kotlin
class MyBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        // обработка на полученото съобщение
    }

}
```

**След това трябва да регистрираме този Broadcast Receiver във файла AndroidManifest.xml:**

```xml
<receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_NAME" />
    </intent-filter>
</receiver>
```

В този пример MyBroadcastReceiver ще обработва съобщения с действие “android.intent.action.ACTION_NAME”. При получаване на такова съобщение ще се извика методът onReceive() на този Broadcast Receiver.\
Broadcast Receiver може да се регистрира и динамично в кода чрез методите registerReceiver() и unregisterReceiver() на класа Context.

## Какво е Content Provider

компонент на архитектурата на приложенията в Android, който осигурява достъп до данните на приложението отвън. Може да се използва за обмен на данни между приложения, за предоставяне на достъп до данни от други приложения или за запазване и извличане на данни от база данни.\
Ето пример за използване на Content Provider за запазване и извличане на данни от SQLite база данни в Android:

**Дефиниране на Content Provider в манифеста на приложението:**

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    <application>
        ...
        <provider
            android:name=".MyContentProvider"
            android:authorities="com.example.myapp.provider"
            android:exported="false" />
    </application>
</manifest>
```

**Създаване на класа MyContentProvider:**\
**Т**ой ще наследява ContentProvider и ще дефинира базата данни и URI, които ще се използват за достъп до данните

```kotlin
class MyContentProvider : ContentProvider() {

    private lateinit var dbHelper: MyDatabaseHelper

    override fun onCreate(): Boolean {
        context?.let {
            dbHelper = MyDatabaseHelper(it)
        }
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<String>?,
        selection: String?,
        selectionArgs: Array<String>?,
        sortOrder: String?
    ): Cursor? {
        val db = dbHelper.readableDatabase
        val cursor = db.query(
            TABLE_NAME,
            projection,
            selection,
            selectionArgs,
            null,
            null,
            sortOrder
        )
        cursor.setNotificationUri(context?.contentResolver, uri)
        return cursor
    }

    override fun getType(uri: Uri): String? {
        return "vnd.android.cursor.dir/$AUTHORITY.$TABLE_NAME"
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        val db = dbHelper.writableDatabase
        val id = db.insert(TABLE_NAME, null, values)
        context?.contentResolver?.notifyChange(uri, null)
        return ContentUris.withAppendedId(uri, id)
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        val db = dbHelper.writableDatabase
        val count = db.delete(TABLE_NAME, selection, selectionArgs)
        context?.contentResolver?.notifyChange(uri, null)
        return count
    }

    override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<String>?
    ): Int {
        val db = dbHelper.writableDatabase
        val count = db.update(TABLE_NAME, values, selection, selectionArgs)
        context?.contentResolver?.notifyChange(uri, null)
        return count
    }

    companion object {
        const val AUTHORITY = "com.example.myapp.provider"
        const val TABLE_NAME = "my_table"
        val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY/$TABLE_NAME")
    }

}
```

**Използване на Content Provider за достъп до данни от друго приложение:**

```kotlin
val cursor = contentResolver.query(MyContentProvider.CONTENT_URI, null, null, null, null)
```

## Какво е Android Context

Базов абстрактен клас, чиято реализация се осигурява от системата Android. Този клас има методи за достъп до специфичните за конкретното приложение ресурси и класове и служи за изпълнение на операции на ниво приложение, като стартиране на Activity, изпращане на broadcast съобщения, получаване на намерения (intent) и други. От класа Context наследяват такива големи и важни класове като Application, Activity и Service, затова всички негови методи са достъпни от тези класове

**Activity Context:** \
Създава се при създаването на Activity и се унищожава заедно с нея. Контекстът е тежък обект. Когато се говори за изтичане на памет в Android, се има предвид изтичане на контекст, т.е. ситуация, при която контекстът на Activity се пази и след извикването на Activity.onDestroy(). Не предавайте контекста на Activity на друг обект, ако не знаете колко дълго ще живее този обект.

**Application Context:** \
Синглтон. Application Context се създава при създаването на обекта Application и живее, докато е жив процесът на приложението. Поради тази причина Application Context може безопасно да се инжектира в други синглтони в приложението. Не се препоръчва Application Context да се използва за стартиране на Activity, защото е необходимо да се създаде нова задача (task), нито за layout inflation, защото се използва темата по подразбиране.

**Контекстът в кода може да се получи по един от следните начини:**

**getBaseContext:** \
получаване на референция към базовия контекст

**getApplicationContext:** \
получаване на референция към обекта на приложението

**getContext:** \
вътре в Activity или Service – получаване на референция към този обект)

**this:** \
същото като getContext

**MainActivity.this:** \
вътре във вложен клас или метод – получаване на референция към обекта MainActivity

**getActivity:** \
вътре във Fragment – получаване на референция към обекта на родителската Activity

**Има разлика и между тези два метода:**

**getContext():** \
връща стойност Context, която допуска NULL.

**requireContext():** \
връща ненулева стойност Context или хвърля изключение, ако той не е наличен.

Ако кодът ви се намира във фаза от жизнения цикъл, в която знаете, че вашият Fragment е прикрепен към контекст, просто използвайте requireContext(), за да получите Context, а също и за да са доволни статичните анализатори по отношение на потенциални проблеми с NPE.

Ако кодът ви е извън обичайния жизнен цикъл на Fragment (да кажем, в асинхронен callback), може да е по-добре да използвате getContext(), като сами проверявате върнатата стойност и продължавате да я използвате само ако не е равна на null.

## Какво е WorkManager

Библиотека на Android за планиране и изпълнение на асинхронни задачи във фонов режим. Тя предоставя прост начин за изпълнение на задачи дори когато приложението вече е затворено, а също автоматично управлява стартирането на задачите, за да минимизира потреблението на ресурси на устройството.

```kotlin
class MyWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    override fun doWork(): Result {
        // Тук се изпълнява асинхронната задача, например изтегляне на файл
        return Result.success()
    }
}

val myWorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build()
WorkManager.getInstance(context)
    .enqueue(myWorkRequest)

// пример за няколко
val workA = OneTimeWorkRequestBuilder<MyWorkerA>().build()
val workB = OneTimeWorkRequestBuilder<MyWorkerB>().build()
val workC = OneTimeWorkRequestBuilder<MyWorkerC>().build()
val workD = OneTimeWorkRequestBuilder<MyWorkerD>().build()
WorkManager.getInstance(context)
    .beginWith(workA, workB, workC)
    .combine()
    .then(workD)
    .enqueue()
```

**enqueue():** \
поставя задачата в опашката за изпълнение. Тя ще бъде изпълнена, щом устройството е готово за това (например при наличие на интернет връзка, достатъчен заряд на батерията и т.н.).

**beginWith():** \
използва се за стартиране на няколко задачи в зададен ред.

**combine():** \
използва се за стартиране на няколко задачи паралелно и за изчакване на приключването им, преди да се изпълни следващата задача.

**WorkManager и LiveData:** \
позволява да се получават уведомления за състоянието на изпълнение на задачите в WorkManager. Например можете да създадете LiveData обект и да го използвате за показване на прогреса на изпълнение на задачата.

```kotlin
class MyViewModel(application: Application) : AndroidViewModel(application) {

    private val workManager = WorkManager.getInstance(application)
    private val workInfoLiveData = MutableLiveData<WorkInfo>()

    fun startWork() {
        val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
            .build()
        workManager.enqueue(workRequest)
        workManager.getWorkInfoByIdLiveData(workRequest.id)
            .observeForever { workInfo ->
                workInfo?.let {
                    workInfoLiveData.postValue(workInfo)
                }
            }
    }

    fun getWorkInfoLiveData(): LiveData<WorkInfo> {
        return workInfoLiveData
    }

}

class MyActivity : AppCompatActivity() {

    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
        viewModel.getWorkInfoLiveData().observe(this, { workInfo ->
            if (workInfo != null && workInfo.state == WorkInfo.State.RUNNING) {
                // Показваме прогреса на изпълнението
            } else if (workInfo != null && workInfo.state.isFinished) {
                // Задачата е завършена
            }
        })
        viewModel.startWork()
    }

}
```

**WorkManager и RxJava:** \
може да се използва библиотеката RxWorkManager, която предоставя удобен начин за създаване на Observable потоци от WorkRequest обекти. Ето пример за използване на RxWorkManager за стартиране на OneTimeWorkRequest и получаване на резултата във вид на Single:

```kotlin
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
    .build()

val workObservable = RxWorkManager
    .getInstance(application)
    .getWorkInfoByIdObservable(workRequest.id)
    .flatMap { workInfo ->
        if (workInfo.state == WorkInfo.State.SUCCEEDED) {
            val outputData = workInfo.outputData
            Single.just(outputData.getString("result"))
        } else {
            Single.error(Throwable("Work failed"))
        }
    }

workObservable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ result ->
            // Обработка на успешния резултат
        },{ error ->
            // Обработка на грешката
        }
    )

WorkManager.getInstance(application).enqueue(workRequest)
```

**WorkManager и корутини:** \
може да се използва библиотеката kotlinx-coroutines-play-services, която предоставя удобен начин за стартиране и проследяване на задачи в WorkManager с помощта на корутини.

```kotlin
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
    .build()
val workInfoDeferred = WorkManager.getInstance(context)
    .getWorkInfoById(workRequest.id)
    .asDeferred()

// Стартираме задачата във фонова нишка
GlobalScope.launch(Dispatchers.IO) {
    WorkManager.getInstance(context).enqueue(workRequest)
}

// Изчакваме резултата от изпълнението на задачата в главната нишка
val workInfo = workInfoDeferred.await()
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
    val outputData = workInfo.outputData
    val result = outputData.getString("result")
    // Обработка на успешния резултат
} else {
    // Обработка на грешката
}
```

## Програмите в Android работят ли на JVM?

Не, байткодът на JVM се преобразува в байткод на ART (Android Runtime) и след това се изпълнява. Преди версия 5.0 вместо ART е имало DVM (Dalvik)

## Опишете архитектурата на платформата Android

**Ядро на Linux:**\
Сърцето на Android, в което се реализират сигурността на междупроцесното взаимодействие и работата с паметта на ниско ниво.

**Hardware Abstraction Layer (HAL):**\
Интерфейси за работа с хардуера. Драйвер за USB, Bluetooth, програмен интерфейс OpenGL. Слоят, който дава на Android независимост от платформата.

**Android Runtime и нативни библиотеки:**\
Това, което изпълнява потребителския код: компилатори, garbage collector, интерпретатор на байткод.

**Android Framework:**\
Java API, чрез което потребителската програма взаимодейства със системата. Осигуряване на жизнения цикъл на системните компоненти.

**Android Applications:**\
Самите приложения, както потребителски, така и системни (календар, камера и т.н.).

## Къде се съхраняват Bundle

представлява клас, който се използва за предаване на данни между компонентите на приложението, като Activity, Fragment и Service. Bundle действа като контейнер за двойки „ключ-стойност“, където ключовете са низове, а стойностите могат да бъдат от различни типове, включително примитивни типове и обекти, реализиращи интерфейса Serializable или Parcelable.

**Основни сценарии за използване на Bundle:**\
Предаване на данни между Activity\
Запазване на състоянието на Activity\
Предаване на данни между Fragment\
Предаване на данни към Service

Bundle се съхраняват в системния клас ActivityManagerService. За всяка стартирана Activity се създава инстанция на класа ActivityRecord. Този клас има поле icicle от тип Bundle. Именно в това поле се записва състоянието след извикването на onSaveInstanceState().

При спиране на Activity Bundle се изпраща към системния процес чрез Binder IPC. След това върху класа ActivityManagerService се извиква методът activityStopped(), който приема обект Bundle. Този метод намира ActivityRecord, съответстващ на спряната Activity, и записва получения Bundle в полето icicle на класа ActivityRecord.

**Предаване на данни между Activity:**

```kotlin
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            val bundle = Bundle().apply {
                putString("KEY_MESSAGE", "Hello from MainActivity")
                putInt("KEY_NUMBER", 123)
            }
            intent.putExtras(bundle)
            startActivity(intent)
        }
    }

}
```

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_second.*

class SecondActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        val bundle = intent.extras
        val message = bundle?.getString("KEY_MESSAGE")
        val number = bundle?.getInt("KEY_NUMBER")
        textView.text = "$message, Number: $number"
    }

}
```

**Запазване на състоянието на Activity:**

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {

    private var counter: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Възстановяване на състоянието
        savedInstanceState?.let {
            counter = it.getInt("KEY_COUNTER", 0)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // Запазване на състоянието
        outState.putInt("KEY_COUNTER", counter)
    }

}
```

**Предаване на данни между Fragment:**

```kotlin
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_first.*


class FirstFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        button.setOnClickListener {
            val bundle = Bundle().apply {
                putString("KEY_MESSAGE", "Hello from FirstFragment")
            }
            val secondFragment = SecondFragment().apply {
                arguments = bundle
            }
            fragmentManager?.beginTransaction()
                ?.replace(R.id.fragment_container, secondFragment)
                ?.addToBackStack(null)
                ?.commit()
        }
    }

}
```

```kotlin
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_second.*


class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_second, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val message = arguments?.getString("KEY_MESSAGE")
        textView.text = message
    }

}
```

## Опишете структурата на обекта Intent

Представлява механизъм за стартиране на действия вътре в приложението или между различни приложения. Intent се използва за различни задачи, като стартиране на Activity, предаване на данни между компонентите, стартиране на Service, broadcast разпращане на съобщения и т.н.

**Основни видове Intent:**

**Explicit Intent (Явни намерения):** \
Използват се за стартиране на определен компонент вътре във вашето приложение. Вие изрично указвате класа на компонента, който трябва да бъде стартиран.

```kotlin
// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
    }
}


// SecondActivity.kt
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
    }
}
```

**Предаване на данни:**

```kotlin
// MainActivity.kt
val intent = Intent(this, SecondActivity::class.java).apply {
    putExtra("EXTRA_MESSAGE", "Hello, SecondActivity!")
}
startActivity(intent)


// SecondActivity.kt
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        val message = intent.getStringExtra("EXTRA_MESSAGE")
        findViewById<TextView>(R.id.textView).text = message
    }
}
```

**Implicit Intent (Неявни намерения):** \
Използват се за изпълнение на действие без указване на конкретен компонент. Вместо това указвате желаното действие, а системата намира подходящ компонент, който да го изпълни.

**Стартиране на уеб браузър за отваряне на URL:**

```kotlin
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
startActivity(intent)
```

**Изпращане на имейл:**

```kotlin
val intent = Intent(Intent.ACTION_SENDTO).apply {
    data = Uri.parse("mailto:") // Само за имейл приложения
    putExtra(Intent.EXTRA_EMAIL, arrayOf("example@example.com"))
    putExtra(Intent.EXTRA_SUBJECT, "Subject of Email")
}
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
}
```

**Основни атрибути:**

**Action (Действие):** \
Низ, който определя действието, което трябва да се изпълни, например ACTION_VIEW, ACTION_SEND, ACTION_MAIN и т.н.

**Data (Данни):** \
URI, който указва данните, върху които трябва да се изпълни действието.

**Category (Категория):** \
Допълнителни характеристики за филтриране на компонентите, които могат да обработват намеренията.

**Extras (Допълнителни данни):** \
Допълнителни данни, които могат да се предадат на компонента, например putExtra("key", value).

**Component (Компонент):** \
Името на класа на компонента, който трябва да бъде стартиран (за явни намерения).

## Какво е Fragment Result API

В някои случаи може да ви се наложи да предадете еднократна стойност между два Fragment или между Fragment и неговата Activity домакин. Например може да имате Fragment, който чете QR кодове и предава данните обратно на предишния Fragment. Започвайки от fragment 1.3.0-alpha04, всеки FragmentManager реализира FragmentResultOwner. Това означава, че FragmentManager може да действа като централно хранилище на резултатите от Fragment. Тази промяна позволява на компонентите да си взаимодействат, като задават резултати от Fragment и слушат тези резултати, без да е необходимо компонентите да имат директни референции един към друг.

За да предадете данни обратно към Fragment A от Fragment B, първо задайте слушател на резултати на Fragment A, който получава резултата. Извикайте setFragmentResultListener() на FragmentManager на Fragment A, както е показано в следния пример:

```kotlin
// Изпращаме резултата към друг Fragment
val someData = "123"
SomeFragmentManager?.setFragmentResult(
  BUNDLE_KEY,
  bundleOf(STRING_KEY to someData)
)
```

```kotlin
// получаваме резултата в друг Fragment
SomeFragmentManager?.setFragmentResultListener(
 BUNDLE_KEY, viewLifecycleOwner
) { _, bundle ->
   bundle.getString(STRING_KEY)?.let(viewModel::setString) }
```

## Какво е Zygote в Android

В операционната система Android процесът Zygote (от англ. „zygote“ – „зигота“) е специален процес, който се стартира при старта на системата и се занимава с предварителна инициализация, компилиране и кеширане на кода на приложенията, за да ускори стартирането им.

При стартиране на ново приложение Zygote създава нов процес, който наследява предварително компилирания код от Zygote, което също помага да се ускори стартирането на приложението и да се намали потреблението на памет.

Zygote предоставя и системни услуги и ресурси, които могат да се използват в приложенията, като системни уиджети, услуги за местоположение, сертификати за сигурност и т.н.

Zygote играе важна роля в работата на Android, тъй като позволява да се ускори стартирането на приложенията и осигурява по-ефективно използване на ресурсите на системата.

## Как в Kotlin могат да се задават параметри в xml за Custom View

```kotlin
class CustomView @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null,
  defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

  init {
    setupAttributes(attrs)
  }

  private fun setupAttributes(attrs: AttributeSet?) {
    context.withStyledAttributes(attrs, R.styleable.CustomView) {
      getBoolean(R.styleable.CustomView_variable_name, true).let { variable ->
      }
    }
  }

}
```

```xml
// в attrs.xml добавяме
<declare-styleable name="CustomView">
  <attr format="boolean" name="variable_name" />
</declare-styleable>

// сега в макета на Fragment можем да добавим
<com._.CustomView
  android:id="@+id/customView"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  app:variable_name="true"
  />

// сега в прегледа и в приложението ще се виждат
// промените, свързани с тази променлива
// ако това е нужно само за прегледа,
// използваме tools:variable_name="true" вместо app:variable_name="true"
```

**Основни типове променливи:**

```xml
<attr format="boolean" name="name">
<attr format="string"name="name">
<attr format="integer"name="name">
<attr format="reference"name="name">
<attr format="color"name="name">
<attr format="reference|color"name="name">
<attr format="dimension"name="name">
<attr format="enum" name="gravity">
    <enum name="start" value="0" />
    <enum name="end" value="1" />
</attr>
```

## Какво е Spannable

Интерфейс, който разширява CharSequence и позволява да се добавят стилове и други атрибути към части от текста. Това е мощен инструмент за работа с текст, който позволява да се променя неговото показване, да се добавят връзки, цветове, изображения и други ефекти.

**Spannable:** \
Интерфейс, който разширява CharSequence и позволява да се променят стиловете на текста.

**SpannableString:** \
Клас, който реализира Spannable и се използва за прилагане на стилове към низове.

**SpannableStringBuilder:** \
Клас, който позволява да се създава и променя текст с различни стилове.

**Типове Spans:**

**StyleSpan:** \
Прилага стил (например удебелен или курсив).

**ForegroundColorSpan:** \
Променя цвета на текста.

**BackgroundColorSpan:** \
Променя цвета на фона на текста.

**UnderlineSpan:** \
Добавя подчертаване към текста.

**StrikethroughSpan:** \
Добавя зачертаване на текста.

**ClickableSpan:** \
Прави текста кликаем и позволява изпълнение на действия при натискане.

**ImageSpan:** \
Вмъква изображение в текста.

**URLSpan:** \
Създава хипервръзка.

```kotlin
import android.graphics.Color
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.*
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.textView)
        val spannableString = SpannableString("Hello, World! This is a spannable string.")
        // Прилагане на удебелен стил към текста "Hello"
        val boldSpan = StyleSpan(android.graphics.Typeface.BOLD)
        spannableString.setSpan(boldSpan, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        // Прилагане на цвят на текста към "World"
        val colorSpan = ForegroundColorSpan(Color.RED)
        spannableString.setSpan(colorSpan, 7, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        // Прилагане на подчертаване към "This"
        val underlineSpan = UnderlineSpan()
        spannableString.setSpan(underlineSpan, 14, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        // Прилагане на зачертан текст към "spannable"
        val strikethroughSpan = StrikethroughSpan()
        spannableString.setSpan(strikethroughSpan, 23, 33, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        // Прилагане на кликаем текст към "string"
        val clickableSpan = object : ClickableSpan() {
            override fun onClick(widget: View) {
                // Действие при кликане върху текста
            }
            override fun updateDrawState(ds: TextPaint) {
                super.updateDrawState(ds)
                ds.isUnderlineText = false // Премахваме подчертаването
            }
        }
        spannableString.setSpan(clickableSpan, 34, 40, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        textView.text = spannableString
        textView.movementMethod = android.text.method.LinkMovementMethod.getInstance()
    }

}
```

**Използване на SpannableStringBuilder:**

```kotlin
val spannableStringBuilder = SpannableStringBuilder()
spannableStringBuilder.append("Bold Text")
spannableStringBuilder.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannableStringBuilder.append(" and ")
spannableStringBuilder.append("Red Text")
spannableStringBuilder.setSpan(ForegroundColorSpan(Color.RED), 14, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannableStringBuilder
```

## Каква е разликата между build type, flavor и build variant

**Build Type:**\
използва се, за да се зададат настройки на билда, например дали ще се използва proguard и с какъв сертификат се подписва apk. Най-често използваните типове билд: debug и release. Типът билд се задава с параметъра buildTypes в gradle файла.

```groovy
buildTypes {
   debug {
       applicationIdSuffix ".debug"
       debuggable true
       minifyEnabled false
   }
   release {
       minifyEnabled false
       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
   }
}
```

**Flavor:**\
използва се за определяне на билдове на приложението с различна функционалност. Това могат да бъдат платени функции (фичъри), различни таргет API, различни ресурси. Задава се с параметъра productFlavors в gradle файла.

```groovy
productFlavors {
  	free {
      dimension "version"
       applicationId "ru.itsobes.flavors.free"
      resValue "string", "flavored_app_name", "Free It Sobes App"
    }
	paid {
       dimension "version"
       applicationId "ru.itsobes.flavors.paid"
       resValue "string", "flavored_app_name", "Paid It Sobes App"
    }
}
```

**Build Variant:**\
това е комбинация от build type и flavor. За описаните по-горе build type и flavor се създават четири build variant: freeDebug, freeRelease, paidDebug, paidRelease.

## Какво е SharedPreferences

**SharedPreferences:** \
съхранява данни във вид на двойки ключ и стойност в папките на приложението. Има Editor за редактиране, в който има commit(), който връща резултата от запазването, и apply(), който не връща резултат.\
SharedPreferences има OnSharedPreferenceChangeListener, който позволява да се следят промените по ключ

**EncryptedSharedPreferences:** \
това е криптирана реализация на sharedPreferences

**DataStore:** \
това е заместител на SharedPreferences, който отстранява повечето от тези недостатъци. DataStore включва напълно асинхронен API, който използва Kotlin Coroutines и Flow.

## Какво е Android ViewBinding

Механизъм в Android, който генерира връзки (binding) към XML макетите по време на компилация. Това ви позволява безопасно и ефективно да взаимодействате с изгледите в кода, като отпада необходимостта от метода findViewById и намалява вероятността от грешки от типа NullPointerException.

**Основни предимства на ViewBinding:**

**Типова безопасност:** \
Кодът, генериран от ViewBinding, гарантира, че имате достъп само до съществуващи изгледи.

**Избягване на NullPointerException:** \
ViewBinding генерира класове, които са null-безопасни, което намалява вероятността да получите NullPointerException.

**Опростяване на кода:** \
Премахва необходимостта от изрично извикване на findViewById, което прави кода по-чист и по-прост.

```groovy
android {
    viewBinding {
        enabled = true
    }
}
```

**Използване на ViewBinding в Activity:**

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, ViewBinding!" />
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click me" />
</LinearLayout>
```

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.viewbindingexample.databinding.ActivityMainBinding


class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.textView.text = "Updated with ViewBinding!"
        binding.button.setOnClickListener {
            binding.textView.text = "Button Clicked!"
        }
    }

}
```

**Използване на ViewBinding във Fragment:**

```xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, ViewBinding in Fragment!" />
</FrameLayout>
```

```kotlin
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.viewbindingexample.databinding.FragmentExampleBinding


class ExampleFragment : Fragment() {

    private var _binding: FragmentExampleBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textView.text = "Updated with ViewBinding in Fragment!"
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}
```

**Включване на ViewBinding:** \
В build.gradle се включва поддръжката на ViewBinding чрез добавяне на блока viewBinding { enabled = true }.

**Създаване на инстанция на класа за binding:** \
В MainActivity и ExampleFragment се създава инстанция на класа за binding, генериран въз основа на името на XML макета (например ActivityMainBinding за activity_main.xml).

**Инициализация на binding:** \
В onCreate за Activity и в onCreateView за Fragment обектът за binding се инициализира чрез извикване на метода inflate.

**Достъп до изгледите:** \
Вместо да се използва findViewById, елементите на интерфейса са достъпни през обекта за binding, например binding.textView и binding.button.

**Управление на жизнения цикъл във Fragment:** \
В onDestroyView на Fragment обектът за binding _binding се занулява, за да се предотврати изтичане на памет.

## Как работи RecyclerView

Това е инструмент за показване на списъци с големи обеми данни в Android. Той подобрява производителността и оптимизира използването на паметта, като използва повторно (рециклира) изгледите на елементите от списъка, когато излязат извън екрана.

**Как работи RecyclerView:**

**Повторно използване на изгледите:** \
Когато елемент от списъка излезе извън екрана, неговият изглед се поставя в пул за повторно използване. Това подобрява производителността и намалява натоварването на паметта.

**Мениджър на компоновката (LayoutManager):** \
Определя как елементите ще бъдат разположени вътре в RecyclerView. В примера се използва LinearLayoutManager, но са налични също GridLayoutManager и StaggeredGridLayoutManager.

**Адаптер:** \
Адаптерът осигурява връзката между данните и изгледите. Той създава нови изгледи при необходимост и ги попълва с данни.

**Основни компоненти на RecyclerView:**

**Adapter:** \
предоставя данните за елементите от списъка и създава нови изгледи на елементите (ViewHolder).

**ViewHolder:** \
държи изгледите на елементите от списъка и осигурява повторното им използване.

**LayoutManager:** \
отговаря за измерването и позиционирането на елементите вътре в RecyclerView.

**Основни методи на RecyclerView.Adapter**

**onCreateViewHolder:** \
Създава нови изгледи на елементите от списъка (ViewHolder).

**onBindViewHolder:** \
Попълва изгледа с данни от набора от данни.

**getItemCount:** \
Връща общия брой елементи в набора от данни.

```groovy
dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
}
```

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item"
        android:textSize="18sp" />
</LinearLayout>
```

```xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
```

```kotlin
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView


class MyAdapter(private val itemList: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView = itemView.findViewById(R.id.textView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_view, parent, false)
        return MyViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = itemList[position]
        holder.textView.text = currentItem
    }

    override fun getItemCount() = itemList.size

}
```

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView


class MainActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: MyAdapter
    private lateinit var itemList: List<String>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById(R.id.recyclerView)
        // Инициализация на данните
        itemList = listOf("Item 1", "Item 2", "Item 3", "Item 4", "Item 5")
        // Настройка на адаптера и layout manager
        adapter = MyAdapter(itemList)
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this)
    }

}
```

## Каква е разликата между Custom View и Custom Drawing

**Custom View:**\
това е потребителски уиджет, който се създава чрез разширяване на съществуващ клас View или негов подклас. При създаването на потребителски изглед можем да определяме нови атрибути, методи и обработчици на събития, а също и да променяме начина, по който изгледът се показва. Custom View може да се използва повторно в различни макети и дава възможност да се създават собствени компоненти на интерфейса.

**Custom Drawing:**\
това е процесът на рисуване върху платно (canvas) с помощта на методите на класа Canvas. Този подход позволява на екрана да се рисуват различни елементи, например линии, правоъгълници, кръгове, текст, изображения и т.н. Custom Drawing може да се използва, когато е необходимо да се създадат динамични елементи на интерфейса, които не могат да се постигнат със стандартните елементи на потребителския интерфейс.

## Какво представляват Deep Link и App Link

**Deep Link:**\
това е връзка към конкретно съдържание вътре в мобилно приложение. Това позволява на потребителите да преминават директно към нужния екран на приложението, без допълнителни стъпки, като търсене в приложението. Приложение, което поддържа Deep Link, може да обработва определени URL адреси и да стартира съответните екрани на приложението, ако са налични на устройството на потребителя.

**App Link:**\
това е по-напреднала версия на Deep Link, която позволява на приложението да се регистрира за обработване на определени URL адреси като свои собствени. Това означава, че потребителят може да премине към съдържанието на приложението през уебсайт и системата автоматично ще отвори съответното приложение, ако е инсталирано на устройството на потребителя. Приложението може да предоставя на уебсайта и информация за себе си, като икони и заглавия, което прави процеса по-интуитивен и привлекателен за потребителите.

## Какво е Navigation Architecture Component, за какво е нужен и как работи

**NavGraph:**\
списък с Fragment, който ще създаваме и попълваме. NavController ще може да показва Fragment само от този списък

**NavHostFragment:**\
контейнер. Вътре в него NavController ще показва Fragment.

**NavController:**\
обект, който управлява навигацията на приложението в NavHost. Той координира смяната на съдържанието на дестинациите в NavHost, докато потребителят се придвижва из приложението.

```xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_main"
    app:startDestination="@id/mainFragment">

  <fragment
      android:id="@+id/mainFragment"
      android:name="com._.Main"
      android:label="mainFragment"
      tools:layout="@layout/fragment_main">
    <action
        android:id="@+id/action_mainFragment_to_settingsFragment"
        app:destination="@id/settingsFragment"
        app:popUpTo="@id/settingsFragment"
        app:popUpToInclusive="true"/>
    <action
        android:id="@+id/action_mainFragment_to_settingsBottomSheetFragment"
        app:destination="@id/settingsBottomSheetFragment" />
  </fragment>

  <fragment
      android:id="@+id/settingsFragment"
      android:name="com._.settingsFragment"
      android:label="settingsFragment"
      tools:layout="@layout/fragment_settings">
  </fragment>

  <dialog
      android:id="@+id/settingsBottomSheetFragment"
      android:name="com._.SettingsBottomSheetFragment"
      android:label="settingsBottomSheetFragment"
      tools:layout="@layout/bottom_sheet_settings" />

</navigation>
```

## Какво е Content Uri

URI (Uniform Resource Identifier) за достъп до данни вътре в приложенията на Android. Използва се за получаване на достъп до различни типове данни, като изображения, видео, звук, контакти, календари и т.н.\
Content Uri обикновено имат формат „content://authority/path/id“, където „authority“ е идентификаторът на доставчика на съдържание, „path“ е пътят до конкретния ресурс, а „id“ е уникалният идентификатор на ресурса.\
Например за достъп до снимките, съхранявани в галерията на устройството, може да се използва следният Content Uri: „content://media/external/images/media“. За достъп до конкретно изображение в края на Uri може да се добави идентификаторът на изображението: „content://media/external/images/media/123“.\
Content Uri се използва за обмен на данни между различни приложения и компоненти на Android, а също и за изпълнение на операции за четене и запис на данни, като добавяне, обновяване и изтриване на записи.

## Как се прави анимация на преходите за навигационния компонент

В папката res се създава папка anim с ресурси за анимация\
Във файла res/navigation/nav_graph.xml в раздела за дизайн избираме стрелката на прехода между екраните и в панела с атрибути вдясно отваряме раздела Animations.

Преходът има четири свойства, които могат да се анимират:\
**enterAnim:** влизане в дестинацията\
**exitAnim:** излизане от дестинацията\
**popEnterAnim:** влизане в дестинацията чрез pop action\
**popExitAnim:** излизане от дестинацията чрез pop action\
View може да се анимира с помощта на Transition Framework

## Какви са предимствата и недостатъците на png и svg и кога кой от тях е по-добре да се използва

PNG – растерен формат, при голямо увеличение ще се виждат пикселите, SVG – векторен и не зависи от разделителната способност на екрана

## Назовете всички случаи, в които се извикват onSaveInstanceState и onRestoreInstanceState

**onSaveInstanceState():**\
извиква се преди унищожаването на Activity, например при завъртане на екрана, свиване на приложението или промяна на конфигурацията на устройството. Този метод се използва за запазване на състоянието на Activity, което може да се загуби при унищожаването и възстановяването на Activity. Извиква се също преди Activity да бъде унищожена от системата в случай на недостиг на памет. В този случай методът се използва за запазване на състоянието на Activity, което може да бъде възстановено при необходимост.

**onRestoreInstanceState():**\
извиква се, след като Activity е била възстановена от състоянието, запазено в onSaveInstanceState(). Този метод се използва за възстановяване на състоянието на Activity, запазено в onSaveInstanceState(). Може да бъде извикан не само след завъртане на екрана или промяна на конфигурацията на устройството, но и в други случаи, когато Activity е била унищожена и пресъздадена от системата, например при възстановяване след аварийно спиране на приложението.

## Какво означават dpi, dp, sp, dip, px, mm, in и pt

**dpi:**\
плътност на екрана, брой пиксели на инч.

**dp, sp:**\
абстрактни независими от плътността пиксели за размерите на елементите и на текста. По-рано писахме за съотношението между dp и px.

**dip:**\
синоним на dp, който не се споменава в документацията, но компилаторът го разбира.

**px:**\
размер, изразен във физически брой пиксели на екрана.

**mm, in:**\
физически размер на екрана, съответно в милиметри и инчове (inches).

**pt:**\
типографска точка (point), също физически размер, равен на 1/72 инч.

## Какви планировчици на задачи има в Android

**Handler:**\
позволява изпълнението на задачи с отложен във времето старт. Не е планировчик на задачи в чист вид, тъй като работи на ниво процес на приложението, а не на операционната система. Ако приложението е спряно, кодът, планиран чрез Handler, няма да бъде изпълнен. Handler позволява да се изпращат съобщения към други нишки със забавяне или без, а също и да се обработват получените съобщения. Handler винаги е свързан с Looper, който от своя страна е свързан с някаква нишка. При създаването на Handler в конструктора може да се предаде обект Looper. Ако се използва конструкторът по подразбиране, Handler се създава в текущата нишка. Ако към нишката не е свързан Looper, при създаването на Handler се хвърля RuntimeException.

**AlarmManager:**\
стартира планирани операции, дори ако приложението е спряно. Като операции се използват PendingIntent. AlarmManager е наличен от API v1, но по подразбиране не работи в Doze Mode. За работа в Doze Mode се използва методът setAndAllowWhileIdle(). Този метод е наличен от API v23.

**JobScheduler:**\
позволява работа в Doze Mode и е наличен от API v21.\
Системата групира задачите, планирани чрез JobScheduler. Когато в Doze Mode се появи прозорец, се изпълняват наведнъж няколко задачи. Този подход пести батерията на устройството.

**WorkManager:**\
библиотека от Android Jetpack. WorkManager работи от API v14. Google препоръчва да се използва WorkManager вместо решенията, разработени по-рано. Под капака WorkManager използва JobScheduler на устройства с API v23+. На версии API 14–22 се използва GCMNetworkManager или AlarmManager.

## Кои са основните Layout в Android и каква е тяхната производителност

**LinearLayout:**\
това е най-простият тип Layout, който се ориентира към хоризонтално или вертикално разположение на дъщерните елементи. Работи бързо на всички версии на Android.

**RelativeLayout:**\
това е тип Layout, който позволява дъщерните елементи да се разполагат един спрямо друг или спрямо родителския елемент. Сложните йерархии от RelativeLayout могат да доведат до влошаване на производителността, затова е по-добре да се избягва използването на този тип Layout, ако е възможно.

**GridLayout:**\
това е тип Layout, който позволява да се създават таблици и елементите да се разполагат в пресечната точка на редове и колони. GridLayout може да бъде много ефективен, ако се използва в правилния контекст.

**ConstraintLayout:**\
това е тип Layout, който позволява да се създават сложни йерархии от View с помощта на ограничения (constraints). ConstraintLayout може да бъде ефективен, ако е настроен правилно, и дори може да надмине по производителност RelativeLayout.

**FrameLayout:**\
това е прост тип Layout, който може да съдържа само един дъщерен елемент. Работи бързо, но не е подходящ за разполагане на няколко елемента.

**TableLayout:**\
предназначен е за разполагане на елементи във вид на таблица. Може да бъде удобен за екрани с голямо количество данни или за показване на списък.

**GridLayout:**\
позволява елементите да се разполагат във вид на мрежа. Особено полезен е за екрани с голямо количество данни, като екрани със списъци или таблици.

**ScrollView:**\
използва се за създаване на превъртаемо съдържание. Този тип layout може да бъде полезен за екрани с голямо количество текст или изображения.

## Може ли AndroidManifest да съдържа няколко main activity

Под main activity се разбира Activity, чийто intent-filter съдържа ACTION_MAIN и CATEGORY_LAUNCHER.\
В AndroidManifest могат да се добавят няколко main activity. \
За всяка Activity ще се появи икона в менюто с приложения. \
По подразбиране ще се използват иконата и името на приложението, зададени в атрибутите icon и label на елемента application в манифеста. Тези атрибути могат да се заменят в елемента activity.\
Този механизъм може да е полезен за библиотеки, които се използват в debug/testing билдове. Така библиотеката leakcanary добавя икона за стартиране на своята Activity от менюто с приложения.

## Какво представляват мулти декларациите

Генерираните за data класове компонентни функции позволяват да се използват в мулти декларации.

```kotlin
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age")
// извежда "Jane, 35 years of age"
```

## Какви видове Service има в Android

**Foreground Service:**\
Service, който се изпълнява на преден план и предоставя на потребителя забележима функционалност. Фонов Service може да се превърне във foreground Service чрез извикване на метода startForeground().

**Background Service:**\
Service, който се изпълнява във фонов режим и не предоставя на потребителя забележима функционалност. За разлика от foreground Service, background Service може да бъде унищожен от системата във всеки момент, когато системата има нужда да освободи памет.

**Bound Service:**\
Service, който осигурява връзка между приложението и друга част на системата, като друго приложение или услуга. Bound Service може да взаимодейства с клиента, като му предоставя обект IBinder чрез метода onBind(). Клиентите могат да използват този обект за извикване на методи, които са дефинирани в Service. Когато всички клиенти се отделят от Service, той се унищожава.

**IntentService:**\
това е подклас на Service, който се използва за изпълнение на задачи във фонов режим.\
IntentService автоматично създава нова нишка за обработката на всеки intent, изпратен към Service. Когато intent бъде обработен, Service автоматично приключва. Ако по време на работата на Service пристигне нов intent, той се добавя в опашка и се обработва по ред.\
IntentService осигурява и синхронизация, като блокира новите intent, докато не приключи обработката на предишните.\
Основното приложение на IntentService е изпълнението на продължителни операции, като изтегляне на данни от мрежата, обработка на изображения, запазване на данни в база данни и т.н. Всички тези операции могат да се изпълняват във фонов режим, за да не се блокира главната нишка на потребителския интерфейс.

**JobIntentService:**\
това е остарял клас в Android, който е предназначен за изпълнение на фонови задачи, които могат да се изпълняват във фонов режим с помощта на Service. Той е въведен в Android Support Library за осигуряване на обратна съвместимост. JobIntentService работи подобно на IntentService, но има допълнителна функционалност за обработка на задачи, които трябва да се изпълнят във фонов режим, и за обработката им във фонов режим дори след като приложението е било спряно. За разлика от обикновения IntentService, JobIntentService автоматично управлява жизнения цикъл на Service и гарантира, че Service няма да бъде спрян, докато не бъдат изпълнени всички задачи.\
Започвайки от Android 12, за стартиране на фонови задачи се препоръчва да се използва WorkManager вместо JobIntentService.

**Пример за използване на Service:**\
Създайте клас за Service, който наследява Service. Например създайте файл MyService.kt със следното съдържание:

```kotlin
class MyService : Service() {

    private val binder = MyServiceBinder()

    override fun onCreate() {
        // Изпълнява се при създаването на Service
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Изпълнява се при стартирането на Service
        return START_STICKY
    }

    override fun onDestroy() {
        // Изпълнява се при приключването на Service
    }

    override fun onBind(intent: Intent?): IBinder? {
        // метод, който трябва да бъде припокрит в класа Service,
        // за да предостави на клиента (обикновено Activity) възможност да се свърже със Service чрез интерфейса IBinder.
        // Този метод трябва да върне обект IBinder,
        // който клиентът може да използва за взаимодействие със Service.
        // Ако Service не предоставя възможност за връзка с клиента,
        // този метод може просто да връща null.
        return binder
    }

inner class MyServiceBinder : Binder() {
        fun getService(): MyService {
            return this@MyService
        }
    }
}
```

Добавете Service във файла на манифеста на приложението, като укажете името на класа на Service и разрешения, ако е необходимо. Например добавете следния код във файла AndroidManifest.xml

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

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

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="false" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

Стартиране и спиране

```kotlin
val serviceIntent = Intent(this, MyService::class.java)
startService(serviceIntent)

val serviceIntent = Intent(this, MyService::class.java)
stopService(serviceIntent)
```

## Кои класове служат за достъп до сензорите

**SensorManager:**\
входна точка за работа със сензорите и listener-ите на техните събития. Системен Service, който се получава по името Context.SENSOR_SERVICE;

**Sensor:**\
представлява отделно взет сензор. Дава различна метаинформация (енергопотребление, точност, производител и т.н.);

**SensorEventListener:**\
интерфейс за реализиране на обработчици на събития, идващи от сензорите. В него се реализира логиката за обработка на входящите данни;

**SensorEvent:**\
отделно събитие от сензор: данни и точност на измерването им.

## Кои са основните анотации и методи в Room

**@Entity(tableName = “users”):** \
data class

**@Entity(primaryKeys = \[“firstName”, “lastName”\]):** \
използва се composite primary key

**@Entity(ignoredColumns = \[“picture”\]):** \
игнориране

**@PrimaryKey(autoGenerate = true):** \
първичен ключ в data class

**@ColumnInfo(name = “first_name”):** \
име на колоната, ако то трябва да се различава от името на променливата

**@Ignore:** \
да не се използва това поле или този конструктор

**@Dao:** \
абстрактен клас или интерфейс за работа с таблица

**@Transaction:** \
изпълнява се като една транзакция, атомарно

**@Database(entities = \[User::class\], version = 1):** \
абстрактен клас, който наследява RoomDatabase

```kotlin
@Database(entities = [User::class], version = 1):
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database-name").build()
```

**@TypeConverters(DateConverter.class):**

```kotlin
class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
    return value?.let { Date(it) }
  }
@TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
    return date?.time?.toLong()
  }
}
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun userDao(): UserDao
}
val db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build()
```

**@Query(“SELECT \* FROM Users”):** \
sql заявка

**@Insert(onConflict = OnConflictStrategy.REPLACE):** \
вмъкване в таблица

**@Delete:** \
изтриване от таблица

**@Update:** \
обновяване

**@DatabaseView:** \
капсулира заявка в клас

**@Fts3 или @Fts4:** \
в Room 2.1.0 – използване на пълнотекстово търсене FTS

**@AutoValue:** \
https://github.com/google/auto/blob/master/value/userguide/index.md

**@MapInfo(keyColumn = “userName”, valueColumn = “bookName”):** \
връща съпоставяне между таблици

**@Embedded(prefix = “address”):** \
взема полетата от маркирания клас и ги счита за полета на таблицата, prefix е нужен, ако имената съвпадат

**@Relation(parentColumn = “userId”, entityColumn = “userOwnerId”):** \
ще търси къде parentColumn е равно на entityColumn, може да има и свойство associateBy = Junction(PlaylistSongCrossRef::class), където

```kotlin
@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)
```

**Зависимост един към един:**

```kotlin
@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)
@Entity
data class Library(
    @PrimaryKey val libraryId: Long,
    val userOwnerId: Long
)
data class UserAndLibrary(
    @Embedded val user: User,
    @Relation(parentColumn = "userId", entityColumn = "userOwnerId")
    val library: Library
)
@Transaction
@Query("SELECT * FROM User")
fun getUsersAndLibraries(): List<UserAndLibrary>
```

**Зависимост един към много:**

```kotlin
@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)
@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: Long,
    val playlistName: String
)
data class UserWithPlaylists(
    @Embedded val user: User,
    @Relation(parentColumn = "userId", entityColumn = "userCreatorId")
    val playlists: List<Playlist>
)
@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylists(): List<UserWithPlaylists>
```

**Зависимост много към много:**

```kotlin
@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)
@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)
@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)
data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef::class))
    val songs: List<Song>
)
data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(parentColumn = "songId", entityColumn = "playlistId", associateBy = Junction(PlaylistSongCrossRef::class))
    val playlists: List<Playlist>
)
@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistsWithSongs(): List<PlaylistWithSongs>
@Transaction
@Query("SELECT * FROM Song")
fun getSongsWithPlaylists(): List<SongWithPlaylists>
```

С помощта на Join могат да се обединяват таблици в Room по начин, алтернативен на Embedded и Relation; заявките при това са по-сложни за писане със SQL синтаксис, но работят много по-бързо от автоматичното обединяване чрез Embedded и Relation

**Асинхронни заявки:**\
coroutines: suspend fun loadUserById(id: Int): User\
RX: public Single loadUserById(int id);\
Guava: public ListenableFuture loadUserById(int id);

**Абониране за промени:**\
coroutines: fun loadUserById(id: Int): Flow\
RX: public Flowable loadUserById(int id);\
LiveData: public LiveData loadUserById(int id);

**Предварително попълване:**

```kotlin
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .build()
// или
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromFile(File("mypath"))
    .build()
```

**Миграции:**

```kotlin
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .fallbackToDestructiveMigration()
    .build()
// или
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .addMigrations(MIGRATION_1_2)
    .build()
    val MIGRATION_1_2 = object : Migration(1, 2) {
      override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " +
          "PRIMARY KEY(`id`))")
      }
    }
// или
@Database(version = 2, entities = [User::class], autoMigrations = [AutoMigration (from = 1, to = 2)])
```

[ Copyright: Roman Kryvolapov ](https://t.me/RomanKryvolapov)
