Kotlin | Вопросы собесов
2.56K subscribers
30 photos
972 links
Download Telegram
🤔 Как бы ты сохранял структуру Markdown в базу данных/на диск?

Сохранение структуры Markdown в базу данных или на диск зависит от целей и требований приложения. Рассмотрим несколько вариантов.

🟠Сохранение Markdown в виде строки (Plain Text)
Самый простой способ — хранить Markdown как обычный текст в базе данных или файле.
База данных (SQLite, Room):
@Entity
data class Note(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String // Здесь хранится Markdown-текст
)


@Dao
interface NoteDao {
@Insert
suspend fun insert(note: Note)

@Query("SELECT * FROM Note WHERE id = :id")
suspend fun getNote(id: Long): Note?
}


Файл на диске
fun saveMarkdownToFile(context: Context, filename: String, content: String) {
val file = File(context.filesDir, filename)
file.writeText(content, Charsets.UTF_8)
}

fun readMarkdownFromFile(context: Context, filename: String): String {
val file = File(context.filesDir, filename)
return if (file.exists()) file.readText(Charsets.UTF_8) else ""
}


🟠Хранение Markdown в виде JSON (Парсинг в структуру)
Если нужно анализировать структуру Markdown (например, извлекать заголовки, ссылки, списки), можно парсить Markdown в JSON и сохранять в базу данных.
{
"title": "Android Markdown Guide",
"content": [
{
"type": "header",
"level": 1,
"text": "Введение"
},
{
"type": "paragraph",
"text": "Markdown — это простой язык разметки..."
},
{
"type": "list",
"items": [
"Простота",
"Гибкость",
"Совместимость"
]
}
]
}


Сохранение в базу данных (Room + TypeConverter):
@Entity
data class MarkdownNote(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val jsonContent: String // Храним JSON-структуру
)

class MarkdownConverter {
@TypeConverter
fun fromJson(value: String): List<MarkdownBlock> {
return Gson().fromJson(value, object : TypeToken<List<MarkdownBlock>>() {}.type)
}

@TypeConverter
fun toJson(content: List<MarkdownBlock>): String {
return Gson().toJson(content)
}
}


Парсинг Markdown в JSON с помощью библиотеки (например, flexmark-java)
fun parseMarkdownToJson(markdown: String): String {
val document = Parser.builder().build().parse(markdown)
val blocks = mutableListOf<MarkdownBlock>()

document.children.forEach { node ->
when (node) {
is Heading -> blocks.add(MarkdownBlock("header", node.level, node.text.toString()))
is Paragraph -> blocks.add(MarkdownBlock("paragraph", text = node.text.toString()))
is BulletList -> {
val items = node.children.map { it.text.toString() }
blocks.add(MarkdownBlock("list", items = items))
}
}
}
return Gson().toJson(blocks)
}


🟠Сохранение Markdown в виде HTML
Если Markdown в основном нужен для отображения, можно сразу конвертировать его в HTML и хранить в базе/файлах.
Конвертация Markdown → HTML с помощью flexmark-java:
fun convertMarkdownToHtml(markdown: String): String {
val renderer = HtmlRenderer.builder().build()
val document = Parser.builder().build().parse(markdown)
return renderer.render(document)
}


Сохранение в базу:
@Entity
data class HtmlNote(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val htmlContent: String
)


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

Многопоточность позволяет выполнять тяжёлые задачи без блокировки UI. В Android используют ExecutorService, Thread, Handler, Coroutines, WorkManager, RxJava, LiveData и другие средства в зависимости от задач.


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

Переход от списка (например, списка элементов в RecyclerView) к экрану деталей элемента в Android является очень распространённой задачей. Для реализации таких переходов используются Intent, Bundle, ViewModel, а также инструменты навигации, такие как Navigation Component.

🟠Переход с использованием Intent и Bundle
Это базовый способ передачи данных от одного экрана (активности) к другому.
1. При нажатии на элемент списка (RecyclerView) создается Intent.
2. В Intent передаются данные (например, ID элемента или вся информация в виде Parcelable или Serializable объекта).
3. Новый экран (деталка) запускается с помощью метода startActivity(intent).
4. На экране детализации данные извлекаются из Intent.
// Предположим, что в адаптере RecyclerView вы обрабатываете клик на элементе
val intent = Intent(this, DetailActivity::class.java)
// Передаем ID элемента через Intent
intent.putExtra("ITEM_ID", item.id)
startActivity(intent)


Деталка (DetailActivity)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)

// Получаем данные из Intent
val itemId = intent.getIntExtra("ITEM_ID", -1)

// Используем itemId, чтобы загрузить данные о выбранном элементе
loadItemDetails(itemId)
}

fun loadItemDetails(id: Int) {
// Например, загрузка из базы данных или сети
}


🟠Переход с использованием Navigation Component
Navigation Component — это современный способ управления переходами между экранами в Android. Он значительно упрощает реализацию навигации и передачи данных.
1. Вы создаете граф навигации (nav_graph), где определяете все экраны (фрагменты) и связи между ними.
2. Переходы между экранами задаются как действия (actions) внутри графа.
3. Данные передаются через аргументы (arguments), которые указываются в nav_graph.

1⃣Настройка nav_graph (res/navigation/nav_graph.xml)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="https://schemas.android.com/apk/res/android"
app:startDestination="@id/listFragment">

<fragment
android:id="@+id/listFragment"
android:name="com.example.ListFragment"
android:label="Список" >
<action
android:id="@+id/action_listFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>

<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="Детали" >
<argument
android:name="itemId"
app:argType="integer" />
</fragment>

</navigation>


2⃣Переход из списка (ListFragment)
val action = ListFragmentDirections.actionListFragmentToDetailFragment(itemId = 123)
findNavController().navigate(action)


3⃣Получение аргумента в деталке (DetailFragment)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Получаем аргумент, переданный через Navigation Component
val itemId = arguments?.getInt("itemId") ?: -1

// Используем itemId для загрузки деталей
loadItemDetails(itemId)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Почему многие отказываются от MVP?

– Требует много шаблонного кода,
– Сложно масштабировать,
– Не очень гибко при асинхронных данных,
– Современные альтернативы (MVVM, MVI) лучше сочетаются с LiveData, Flow, State и coroutines.


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

Передача фото в редактор зависит от типа редактора:
1. Внешний редактор (например, Google Photos, Snapseed).
2. Встроенный редактор внутри приложения.

🚩Передача фото во внешний редактор (Intent)

Если редактор — другое приложение, используем Intent.ACTION_EDIT.
Как передать фото в редактор?
fun openPhotoEditor(context: Context, uri: Uri) {
val intent = Intent(Intent.ACTION_EDIT).apply {
setDataAndType(uri, "image/*")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Разрешение на чтение
}
context.startActivity(Intent.createChooser(intent, "Выберите редактор"))
}


🚩Передача в `Activity` своего приложения (FileProvider)

Если редактор — внутри приложения, можно передавать фото через Intent с Uri.
Отправка фото в редактор
fun openEditor(context: Context, photoFile: File) {
val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", photoFile)

val intent = Intent(context, PhotoEditorActivity::class.java).apply {
putExtra("PHOTO_URI", uri.toString())
}

context.startActivity(intent)
}


Получение фото в редакторе (PhotoEditorActivity)
class PhotoEditorActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val uriString = intent.getStringExtra("PHOTO_URI")
val uri = uriString?.let { Uri.parse(it) }

uri?.let {
imageView.setImageURI(it) // Показываем фото
}
}
}


🚩Если фото приходит в `byte[]` (с сервера)

Если фото уже в памяти (byte[]), можно передать его как Parcelable.
Отправка
val bitmap = ... // Получили Bitmap
val byteArray = ByteArrayOutputStream().apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, this)
}.toByteArray()

val intent = Intent(context, PhotoEditorActivity::class.java).apply {
putExtra("PHOTO_BYTES", byteArray)
}

context.startActivity(intent)


Получение
val byteArray = intent.getByteArrayExtra("PHOTO_BYTES")
byteArray?.let {
val bitmap = BitmapFactory.decodeByteArray(it, 0, it.size)
imageView.setImageBitmap(bitmap)
}


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

В data class можно использовать только свойства, объявленные в первичном конструкторе (val или var). Эти свойства автоматически участвуют в:
- equals, hashCode;
- toString;
- copy и componentN.
Остальные свойства не считаются частью "данных" класса.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Что нужно сделать в Android-проекте чтобы начать рисовать UI на экране?

Чтобы начать рисовать пользовательский интерфейс (UI) на экране в Android-проекте, необходимо выполнить несколько шагов, которые включают настройку проекта, создание макета и взаимодействие с основными компонентами Android.

1⃣Создание Android-проекта
Первый шаг — создание Android-проекта в Android Studio. Это можно сделать, выбрав шаблон «Empty Activity», который предоставляет минимальный набор для разработки приложения.
Укажите имя проекта.
Выберите язык программирования (Java или Kotlin).
Убедитесь, что минимальная версия SDK подходит для вашей целевой аудитории.

2⃣Настройка макета (XML-файл)
Android UI в основном создаётся с использованием XML-файлов, которые определяют структуру интерфейса.
По умолчанию файл макета находится в каталоге:
res/layout/activity_main.xml


#### Пример простого макета:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Привет, мир!"
android:textSize="24sp"
android:padding="16dp"/>

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Нажми меня"/>
</LinearLayout>


🟠Подключение макета к активности
Макет нужно связать с логикой приложения в Java или Kotlin. Это делается с помощью метода setContentView() в Activity.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Указываем, какой XML-файл использовать для интерфейса
setContentView(R.layout.activity_main)

// Найдём элементы интерфейса и добавим логику
val textView = findViewById<TextView>(R.id.textView)
val button = findViewById<Button>(R.id.button)

button.setOnClickListener {
textView.text = "Кнопка нажата!"
}
}
}


🟠Добавление кастомного рисования (по необходимости)
Если нужно нарисовать что-то вручную (например, графику, линии, или кастомные фигуры), можно создать свой собственный класс, унаследованный от View, и переопределить метод onDraw().
class CustomView(context: Context) : View(context) {
private val paint = Paint().apply {
color = Color.RED
strokeWidth = 10f
}

override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)

// Рисуем линию
canvas?.drawLine(100f, 100f, 400f, 400f, paint)
}
}


Чтобы использовать этот класс, можно добавить его в макет или программно
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Устанавливаем кастомный View вместо макета
setContentView(CustomView(this))
}
}


🟠Запуск приложения
Теперь можно запустить приложение на эмуляторе или реальном устройстве:
Нажмите Run в Android Studio.
Убедитесь, что устройство подключено или выбран эмулятор.
После запуска вы увидите созданный интерфейс.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Из каких частей состоит проект и какая часть за что отвечает?

– src/ — исходный код,
– res/ — ресурсы (строки, изображения, макеты),
– manifest — описание компонентов, разрешений, запуска,
– build.gradle — настройки сборки, зависимости,
– libs/ — внешние библиотеки,
– assets/ — файлы, доступные в рантайме.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
🤔 Зачем нужны Data Class и Sealed Classes?

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

🚩Зачем они нужны

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

🟠Сокращение кода
Автоматически генерирует методы equals(), hashCode(), и toString(), а также copy() и компонентные функции для объектов данных. Это избавляет от необходимости ручной реализации этих методов, что уменьшает количество кода и возможность ошибок.

🟠Упрощение передачи данных
Идеально подходят для передачи данных между различными частями приложения, например, между слоями в архитектуре MVVM или при передаче данных между активностями и фрагментами.

🟠Поддержка неизменяемости
С помощью них легко создавать неизменяемые объекты, что способствует безопасной работе с данными, особенно в многопоточной среде.

🚩Зачем они нужны

Sealed Class используются для определения закрытых иерархий классов, где все потомки известны и ограничены. Они полезны по следующим причинам:

🟠Полное покрытие случаев в `when`
Гарантируют, что все возможные подтипы обработаны в выражениях when, что предотвращает ошибки во время выполнения из-за пропущенных случаев. Это упрощает управление состояниями и делает код более безопасным и предсказуемым.

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

🟠Инкапсуляция
Поскольку все расширения таких классов должны быть объявлены в том же файле, это способствует лучшей инкапсуляции и организации кода.

data class User(val name: String, val age: Int)


Пример Sealed Class
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: Throwable) : Result()
}

fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success with data: ${result.data}")
is Result.Failure -> println("Failure with error: ${result.error.message}")
}
}


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

В RxJava управление потоками осуществляется через subscribeOn и observeOn. Первый определяет поток, в котором будет происходить генерация данных (источник), второй — поток, в котором обрабатываются все дальнейшие операторы и подписка.


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

В Android при нажатии пользователя на экран вызывается событие ACTION_DOWN.

🚩Разбор работы событий касания

Android использует систему обработки касаний через MotionEvent. Когда пользователь касается экрана, система генерирует разные типы событий:
ACTION_DOWN – вызывается в момент первого касания.
ACTION_MOVE – вызывается, когда пользователь двигает палец по экрану.
ACTION_UP – вызывается, когда пользователь убирает палец с экрана.
ACTION_CANCEL – вызывается, если система прерывает касание (например, из-за входящего звонка).

🚩Как обрабатывать нажатие

Для обработки событий касания нужно переопределить метод onTouchEvent() в View или использовать setOnTouchListener().
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.d("TouchEvent", "Палец коснулся экрана")
return true
}
MotionEvent.ACTION_UP -> {
Log.d("TouchEvent", "Палец отпущен")
}
}
return super.onTouchEvent(event)
}


Можно также назначить слушатель
view.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
Log.d("TouchEvent", "Нажатие зафиксировано")
true
} else {
false
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как будут вести себя LinkedList и ArrayList, если вставить в них элемент?

В LinkedList вставка в начало или середину имеет сложность O(1), так как требуется только обновление ссылок, но доступ к элементу занимает O(n). В ArrayList вставка занимает O(n) из-за необходимости сдвига элементов, но доступ по индексу — O(1).

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

Чтобы обрабатывать жесты в Android, используйте класс GestureDetector. Он помогает отслеживать стандартные жесты: одиночные нажатия, свайпы, долгие нажатия, двойные касания и т.д.

🚩Как использовать GestureDetector?

Создайте экземпляр GestureDetector, передав Context и слушателя (GestureDetector.OnGestureListener).
Передавайте события касания в gestureDetector.onTouchEvent(event) из метода onTouchEvent().
class GestureActivity : AppCompatActivity(), GestureDetector.OnGestureListener {
private lateinit var gestureDetector: GestureDetector

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gestureDetector = GestureDetector(this, this)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}

override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val deltaX = e2!!.x - e1!!.x
if (deltaX > 0) Log.d("Gesture", "Swipe Right") else Log.d("Gesture", "Swipe Left")
return true
}

override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) {}
override fun onSingleTapUp(e: MotionEvent?) = false
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent?) {}
}


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

1. Используйте метод startActivityForResult:
- Передайте Intent, а затем обработайте результат в методе onActivityResult.
2. В современных API используйте ActivityResultLauncher для управления результатами.


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

RxJava может вызывать утечки памяти, если подписки (Disposable) не очищаются правильно.
Утечки памяти происходят, когда RxJava-события продолжают выполняться после уничтожения Activity/Fragment, удерживая ссылки на View или Context.

🚩Используем `CompositeDisposable`

CompositeDisposable собирает все подписки и очищает их при onDestroy().
class MainActivity : AppCompatActivity() {
private val disposables = CompositeDisposable() // Собираем подписки

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }

disposables.add(disposable) // Добавляем подписку
}

override fun onDestroy() {
super.onDestroy()
disposables.clear() // Очищаем подписки, предотвращая утечку памяти
}
}


🚩Используем `autoDispose` (для Jetpack Lifecycle)

Если используем Jetpack ViewModel или LifecycleOwner, можно автоматически отвязывать подписки.
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Observable.interval(1, TimeUnit.SECONDS)
.autoDispose(viewLifecycleOwner) // Автоочистка при `onDestroyView()`
.subscribe { println("Tick: $it") }
}
}


🚩Используем `Disposable.dispose()` вручную

Если подписка не в CompositeDisposable, её можно очистить вручную.
private var disposable: Disposable? = null

fun startObserving() {
disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }
}

fun stopObserving() {
disposable?.dispose() // Очищаем подписку
}


🚩Используем `ViewModel` + `LiveData` вместо RxJava

RxJava может удерживать Activity/Fragment, вызывая утечки памяти.
Лучше использовать ViewModel + LiveData!
class MyViewModel : ViewModel() {
private val _timer = MutableLiveData<Long>()
val timer: LiveData<Long> = _timer

init {
Observable.interval(1, TimeUnit.SECONDS)
.subscribe { _timer.postValue(it) }
}
}


Подписка в Activity (без утечек!)
viewModel.timer.observe(this) { println("Tick: $it") }


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли отправить своё broadcast-сообщение?

Да. Можно использовать:
- sendBroadcast(Intent) — для обычной широковещательной рассылки.
- sendOrderedBroadcast() — для последовательной доставки.
- LocalBroadcastManager (устарел, но можно использовать аналоги).
Это позволяет передавать события между компонентами приложения или уведомлять другие приложения (если экспортировано).


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

Чтобы Retrofit мог возвращать Observable, Single, Maybe или Flowable из RxJava, нужно добавить RxJava Adapter.

🚩Добавление зависимости

В build.gradle.kts (Kotlin DSL)
dependencies {
implementation("com.squareup.retrofit2:adapter-rxjava3:2.9.0") // Адаптер для RxJava 3
implementation("io.reactivex.rxjava3:rxjava:3.1.8") // RxJava 3
}


🚩Подключение `RxJava3CallAdapterFactory`

Добавляем адаптер в Retrofit.Builder
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create()) // Преобразование JSON
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // Поддержка RxJava
.build()


🚩Использование RxJava в API

Теперь можно возвращать RxJava-объекты вместо Call<>.
interface ApiService {
@GET("users/{id}")
fun getUser(@Path("id") userId: Int): Single<User>
}


Пример с Observable<> (несколько данных или обновления)
interface ApiService {
@GET("users")
fun getUsers(): Observable<List<User>>
}


Пример с Flowable<> (если нужен Backpressure)
interface ApiService {
@GET("posts")
fun getPosts(): Flowable<List<Post>>
}


🚩Подписка и обработка результата

Пример подписки в ViewModel (RxJava 3 + LiveData)
class UserViewModel(private val apiService: ApiService) : ViewModel() {

private val _userLiveData = MutableLiveData<User>()
val userLiveData: LiveData<User> = _userLiveData

fun fetchUser(userId: Int) {
apiService.getUser(userId)
.subscribeOn(Schedulers.io()) // Запрос в фоновом потоке
.observeOn(AndroidSchedulers.mainThread()) // Обновление UI в главном потоке
.subscribe({ user ->
_userLiveData.value = user
}, { error ->
Log.e("UserViewModel", "Ошибка загрузки", error)
})
}
}


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

В Android Manifest файле описываются основные компоненты приложения, такие как активности, службы (сервисы), приемники широковещательных сообщений (broadcast receivers) и провайдеры контента. Также в нем указываются разрешения, которые требуются приложению, а также минимальная и целевая версии платформы Android.

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

Чтобы добавить кастомные атрибуты в Custom View, нужно:
Создать attrs.xml и описать атрибуты.
Добавить их в styleable и получить в Custom View.
Использовать атрибуты в XML или Kotlin.

🟠Создаём `attrs.xml`
Создаём файл res/values/attrs.xml, если его нет:
<resources>
<declare-styleable name="CustomButton">
<attr name="customText" format="string"/>
<attr name="customTextSize" format="dimension"/>
<attr name="customTextColor" format="color"/>
</declare-styleable>
</resources>


🟠Создаём `Custom View` и получаем атрибуты
Теперь создаём CustomButton.kt
class CustomButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatButton(context, attrs, defStyleAttr) {

init {
context.theme.obtainStyledAttributes(attrs, R.styleable.CustomButton, 0, 0).apply {
try {
val text = getString(R.styleable.CustomButton_customText) ?: "Default"
val textSize = getDimension(R.styleable.CustomButton_customTextSize, 16f)
val textColor = getColor(R.styleable.CustomButton_customTextColor, Color.BLACK)

setText(text)
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
setTextColor(textColor)
} finally {
recycle() // Освобождаем ресурсы
}
}
}
}


🟠Используем `Custom View` в XML
Теперь можно использовать CustomButton в activity_main.xml
<com.example.customviews.CustomButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:customText="Нажми меня"
app:customTextSize="20sp"
app:customTextColor="@android:color/holo_red_dark"/>


🟠Как изменить атрибуты в коде?
Можно обновлять свойства прямо в Kotlin
val button = findViewById<CustomButton>(R.id.customButton)
button.text = "Новый текст"
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24f)
button.setTextColor(Color.BLUE)


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

Работа срочная, есть дедлайн.
Заплачу примерно 3000-5000 руб.
Работа на 1-2 дня.

Если интересно напишите мне @kivaiko сообщение:
Привет, я по поводу разметки тегами вопросов для собеседований Android разработчика