- px (pixels) — абсолютные пиксели экрана.
- dp (density-independent pixels) — масштабируемые пиксели, зависят от плотности экрана.
- sp (scale-independent pixels) — как dp, но учитывают пользовательские настройки размера шрифта.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
В Java класс
Object является корневым классом для всех остальных классов. Это значит, что все классы в Java неявно наследуются от Object, если явно не указано другое. Определяет, равны ли два объекта. По умолчанию использует сравнение по ссылке (
==), но может быть переопределён для логического сравнения. class Person {
String name;
Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
}
}
Возвращает числовой хеш-код объекта, используемый, например, в
HashMap. Если переопределяем equals(), нужно переопределить и hashCode(). @Override
public int hashCode() {
return Objects.hash(name);
}
Возвращает строковое представление объекта. По умолчанию – имя класса + хеш-код, но лучше переопределять.
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
Возвращает объект
Class, описывающий класс объекта. System.out.println(p1.getClass().getName()); // Person
Создаёт копию объекта (поверхностное клонирование). Объект должен реализовать
Cloneable, иначе будет CloneNotSupportedException. class Person implements Cloneable {
String name;
Person(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Вызывается перед удалением объекта сборщиком мусора. Лучше использовать
try-with-resources и close(). @Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}
Методы для работы с многопоточностью.
wait() – приостанавливает поток до вызова notify(). notify() – пробуждает один поток. notifyAll() – пробуждает все потоки. class SharedResource {
synchronized void doWait() throws InterruptedException {
wait();
}
synchronized void doNotify() {
notify();
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Во фрагменте можно получить доступ к активности через requireActivity() или activity, и вызвать у неё метод, предварительно приведя к нужному типу (например, activity as MyActivity). Лучше всего — через интерфейс, реализуемый активностью, для слабой связанности.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
По умолчанию нельзя, потому что дженерики стираются (
Type Erasure) во время компиляции. fun <T> printType(value: T) {
println(T::class.simpleName) // ❌ Ошибка! Нельзя использовать `T::class`
}Если используем
inline fun, можно сделать дженерик "реальным" (reified). inline fun <reified T> printType(value: T) {
println(T::class.simpleName) // ✅ Работает!
}
fun main() {
printType(123) // Int
printType("Hello") // String
printType(3.14) // Double
}Если дженерик используется в классе, то
reified не поможет. class MyGenericClass<T>(private val type: KClass<T>) {
fun printType() {
println(type.simpleName) // ✅ Работает
}
}
fun main() {
val obj = MyGenericClass(String::class)
obj.printType() // String
}Для сложных дженериков (
List<T>, Map<K, V>) используем typeOf<T>() (только с reified). import kotlin.reflect.typeOf
inline fun <reified T> printGenericType() {
val type = typeOf<T>()
println(type) // ✅ List<Int>, Map<String, Boolean> и т. д.
}
fun main() {
printGenericType<List<Int>>() // kotlin.collections.List<kotlin.Int>
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
В MVI (Model-View-Intent) есть проблема: события, которые не нужно хранить в
State, могут повторно отобразиться при пересоздании экрана (например, при повороте экрана или навигации назад). Показ
Toast / Snackbar Навигация (
openScreen()) Ошибки, которые нужно показать один раз
Это
LiveData, которая отправляет событие только один раз и не пересылает его новым подписчикам. class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
}
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}ViewModel с
SingleLiveEvent class MyViewModel : ViewModel() {
val eventShowToast = SingleLiveEvent<String>()
fun onButtonClicked() {
eventShowToast.value = "Привет, это Toast!"
}
}Activity/Fragment подписывается
viewModel.eventShowToast.observe(viewLifecycleOwner) { message ->
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}В
StateFlow все события хранятся в State (что не подходит для одноразовых событий). Вместо этого используем
SharedFlow с replay = 0, чтобы событие не повторялось. ViewModel с
SharedFlow class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()
fun onButtonClicked() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("Привет, это Toast!"))
}
}
}
sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
object NavigateToNextScreen : UiEvent()
}Подписка в UI:
lifecycleScope.launchWhenStarted {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.iss.onessage, Toast.LENGTH_SHORT).show()
is UiEvent.NavigateToNextScreen -> findNavController().navigate(R.id.nextFragment)
}
}
}Если в
State всё-таки нужно хранить событие, но не показывать его повторно, можно использовать EventWrapper. class EventWrapper<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) null else {
hasBeenHandled = true
content
}
}
}ViewModel с
StateFlow: data class UiState(val message: EventWrapper<String>? = null)
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(UiState())
val state = _state.asStateFlow()
fun onButtonClicked() {
_state.value = UiState(message = EventWrapper("Привет, это Toast!"))
}
}
UI подписывается и проверяет
EventWrapper: viewModel.state.collect { state ->
state.message?.getContentIfNotHandled()?.let { message ->
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Да, есть.
- В launch {} ошибка автоматически передается вверх и может быть обработана CoroutineExceptionHandler.
- В async {} ошибки НЕ передаются автоматически, они остаются внутри Deferred<T>.
- Чтобы поймать ошибку в async {}, нужно вызвать await() внутри try-catch.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Проблемы с элементами списка в Android-приложениях могут быть разнообразными. Давайте рассмотрим некоторые из наиболее распространённых проблем и способы их решения.
// Пример использования Glide для загрузки изображений
Glide.with(context)
.load(imageUrl)
.into(imageView)
class MyAdapter(private val itemList: List<Item>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = itemList[position]
holder.textView.text = item.text
Glide.with(holder.itemView.context).load(item.imageUrl).into(holder.imageView)
}
override fun getItemCount() = itemList.size
} class ItemDiffCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
}
// Использование в адаптере
val diffCallback = ItemDiffCallback()
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(myAdapter)val liveData = MutableLiveData<List<Item>>()
liveData.observe(viewLifecycleOwner, Observer { items ->
myAdapter.submitList(items)
})
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1💊1
DiffUtil используется в RecyclerView.Adapter для эффективного обновления списка:
- Вычисляет разницу между старым и новым списком.
- Обновляет только те элементы, которые реально изменились.
- Повышает производительность и визуальную плавность.
Незаменим при работе с динамическими данными в списках.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Чтобы начать рисовать пользовательский интерфейс (UI) на экране в Android-проекте, необходимо выполнить несколько шагов, которые включают настройку проекта, создание макета и взаимодействие с основными компонентами Android.
Первый шаг — создание Android-проекта в Android Studio. Это можно сделать, выбрав шаблон «Empty Activity», который предоставляет минимальный набор для разработки приложения.
Укажите имя проекта.
Выберите язык программирования (Java или Kotlin).
Убедитесь, что минимальная версия SDK подходит для вашей целевой аудитории.
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
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Nothing — это специальный bottom type (нижний тип), который означает: Функция никогда не возвращает результат (
throw, error()). Код после
Nothing недостижим. fun fail(): Nothing {
throw IllegalStateException("Ошибка!") // Никогда не возвращает значение
}Все классы в Kotlin могут иметь инстансы (объекты), кроме
Nothing. Nothing нельзя создать (instantiate), потому что он не имеет конструктора. Любая переменная типа
Nothing просто не существует. val x: Nothing = Nothing() // ❌ Ошибка: у Nothing нет конструктора
Используется в
throwfun fail(): Nothing = throw IllegalArgumentException("Ошибка")Используется в
TODO()fun getData(): String {
TODO("Функция ещё не реализована")
}Используется в
if-else, где один вариант throwfun getValue(x: Int): String {
return if (x > 0) "Позитивное число" else throw IllegalArgumentException("Отрицательное!")
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Чтобы обеспечить:
– удобство тестирования,
– повторное использование логики,
– снижение связанности,
– улучшение читаемости и поддержки кода.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Переход от списка (например, списка элементов в
RecyclerView) к экрану деталей элемента в Android является очень распространённой задачей. Для реализации таких переходов используются Intent, Bundle, ViewModel, а также инструменты навигации, такие как Navigation Component.Это базовый способ передачи данных от одного экрана (активности) к другому.
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 — это современный способ управления переходами между экранами в Android. Он значительно упрощает реализацию навигации и передачи данных.
1. Вы создаете граф навигации (
nav_graph), где определяете все экраны (фрагменты) и связи между ними.2. Переходы между экранами задаются как действия (actions) внутри графа.
3. Данные передаются через аргументы (arguments), которые указываются в
nav_graph.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>
val action = ListFragmentDirections.actionListFragmentToDetailFragment(itemId = 123)
findNavController().navigate(action)
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
👍1💊1
В Kotlin есть let, apply, run, also, with. Они позволяют писать лаконичный и безопасный код при работе с объектами, особенно nullable, и цепочками вызовов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Dalvik Virtual Machine (DVM) и Android Runtime (ART) — это две среды выполнения для запуска Android-приложений. DVM использовалась в ранних версиях Android, в то время как ART пришла на замену DVM, начиная с Android 5.0 (Lollipop). Основное различие между ними заключается в способе выполнения кода и производительности.
Dalvik Virtual Machine (DVM): Just-In-Time (JIT) компиляция
Dalvik использует JIT-компиляцию (Just-In-Time), что означает, что код приложения компилируется в машинный код во время выполнения (runtime).
Когда приложение запускается, DVM интерпретирует байт-код (.dex-файлы), а при необходимости компилирует часть кода "на лету" для повышения производительности.
Этот подход требует дополнительных ресурсов во время работы приложения, что увеличивает задержки (лаг) при запуске и потребляет больше CPU и батареи.
ART использует AOT-компиляцию (Ahead-Of-Time), которая компилирует весь код приложения в машинный код заранее — во время установки приложения.
Это устраняет необходимость интерпретации и JIT-компиляции во время работы приложения, что снижает нагрузку на процессор и улучшает производительность.
Dalvik (DVM):
Поскольку JIT-компиляция происходит во время работы приложения, это создает дополнительную нагрузку на процессор и замедляет выполнение.
Производительность ниже из-за частой интерпретации кода.
ART:
Благодаря AOT-компиляции приложения запускаются быстрее и работают плавнее.
Потребление ресурсов (CPU, батарея) значительно ниже, поскольку интерпретация и компиляция кода уже выполнены на этапе установки.
Dalvik (DVM):
Приложения запускаются медленнее, так как DVM интерпретирует код во время каждого запуска.
ART:
Приложения запускаются быстрее, так как код уже компилирован в машинный код на этапе установки.
Dalvik (DVM):
Потребляет больше батареи из-за того, что JIT-компиляция выполняется постоянно во время работы приложения.
ART:
Более энергоэффективен, так как большая часть работы выполнена заранее, и процессор не нагружается так сильно.
Dalvik (DVM):
Приложения устанавливаются быстрее, так как код не компилируется заранее.
ART:
Приложения устанавливаются медленнее, так как на этапе установки выполняется AOT-компиляция.
Например, установка приложения в ART может занимать больше времени, чем в DVM, из-за компиляции кода.
Dalvik (DVM):
DVM использует меньше памяти на устройстве, так как код компилируется только во время работы приложения, и машинный код не сохраняется.
ART:
AOT-компиляция увеличивает размер приложения, так как компилированный машинный код сохраняется на устройстве. Это требует больше места в памяти.
Dalvik (DVM):
Ограниченные возможности отладки, так как JIT-компиляция не предоставляет доступа к заранее оптимизированному коду.
ART:
ART позволяет разработчикам использовать более продвинутые инструменты отладки (например, профилирование исполнения) и лучше анализировать производительность приложений.
Dalvik (DVM):
Dalvik был изначально разработан для устройств с ограниченными ресурсами (медленные процессоры, малый объем оперативной памяти).
Приложения работали в основном в условиях ограниченного оборудования.
ART:
ART ориентирован на современные устройства с мощными процессорами и большим объемом памяти.
Он лучше справляется с современными требованиями приложений.
- Установка быстрая.
- При запуске приложения DVM интерпретирует и компилирует код. Это требует времени и ресурсов.
- Приложение может работать медленно из-за интерпретации кода в реальном времени.
- Установка занимает больше времени, так как код компилируется сразу.
- Запуск приложения быстрый, потому что код уже готов к исполнению.
- Приложение работает плавно, так как отсутствует необходимость компиляции во время выполнения.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Forwarded from Идущий к IT
🔥 Записал видос "Как за 3 минуты настроить Автоотклики на вакансии HeadHunter" больше не придется заниматься этой унылой рутиной
📺 Видео: https://youtu.be/G_FOwEGPwlw
Please open Telegram to view this post
VIEW IN TELEGRAM
💊1