Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥1
Система Android может перезапустить сервис в нескольких случаях, особенно если это касается долгосрочных или критически важных задач, которые должны продолжаться, даже если приложение было закрыто или убито системой.
Если сервис был запущен с использованием
START_STICKY, система попытается перезапустить его, если он был убит. Этот режим подходит для сервисов, которые выполняют важные фоновые задачи, которые должны продолжаться, даже если приложение было закрыто.public class MyService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Выполнение задачи
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}Если сервис был запущен с использованием
START_REDELIVER_INTENT, система также попытается перезапустить его, если он был убит, но с повторной доставкой последнего Intent. Это полезно для сервисов, которые должны обработать все интенты, даже если они были убиты и перезапущены.public class MyService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Выполнение задачи
return START_REDELIVER_INTENT;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}Foreground сервисы — это сервисы, которые пользователи знают, что они работают, и обычно показывают уведомление. Такие сервисы имеют более высокий приоритет и менее вероятно будут убиты системой. Однако, если это произойдет, система постарается их перезапустить.
public class MyForegroundService extends Service {
@Override
public void onCreate() {
super.onCreate();
// Создание уведомления
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Service is running")
.setSmallIcon(R.drawable.ic_service)
.build();
// Запуск сервиса переднего плана
startForeground(1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Выполнение задачи
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
Когда система запускает новое
Activity в Android, судьба старого Activity зависит от множества факторов, включая флаги намерения (intent flags), конфигурацию задачи (task configuration) и состояние памяти устройства.Если не используются специальные флаги или конфигурации, то при запуске нового
Activity старое Activity остается в стеке задач (back stack).Старое
Activity переходит в состояние onPause(), затем onStop().Новое
Activity создается и проходит состояния onCreate(), onStart() и onResume().Intent intent = new Intent(this, NewActivity.class);
startActivity(intent);
Если флаг
FLAG_ACTIVITY_NEW_TASK установлен, новое Activity запускается в новом отдельном стеке задач, если такого еще нет.Старое
Activity остается в своей задаче.Новое
Activity запускается в новой задаче.Intent intent = new Intent(this, NewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Если флаг
FLAG_ACTIVITY_CLEAR_TOP установлен, новое Activity будет запускаться, а все активности над ним в стеке будут удалены.Если старое
Activity уже в стеке задач, все активности выше него будут удалены.Старое
Activity будет возвращено в состояние onRestart(), onStart() и onResume().Intent intent = new Intent(this, NewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Если флаг
FLAG_ACTIVITY_SINGLE_TOP установлен и новое Activity уже находится на вершине стека, оно не будет пересоздано, а просто получит вызов onNewIntent().Если новое
Activity уже на вершине стека, его метод onNewIntent() будет вызван вместо создания нового экземпляра.Intent intent = new Intent(this, NewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
Если система нуждается в памяти, она может уничтожить старое
Activity, которое находится в состоянии onStop(). Когда пользователь вернется к этому Activity, оно будет пересоздано и его метод onCreate() будет вызван снова.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Да, в Android можно подключиться к сервису после его запуска и взаимодействовать с ним через механизм привязки (binding). Это достигается с помощью метода
bindService(). Привязка к сервису позволяет активити (или другому компоненту) взаимодействовать с уже запущенным сервисом, получать данные от него или отправлять команды.Сначала создайте сервис, который можно будет запускать и к которому можно будет привязываться. В данном примере, используем
BoundService.Запустите сервис с помощью метода
startService().Используйте метод
bindService() для подключения к уже запущенному сервису.public class MyBoundService extends Service {
private final IBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
MyBoundService getService() {
return MyBoundService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public String getGreeting() {
return "Hello from Bound Service!";
}
}<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"
android:padding="16dp">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get Greeting" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="18sp" />
</LinearLayout>
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
В Android используется для выполнения асинхронных задач в фоновом режиме. Самый распространенный способ выполнения задач и передачи результатов — это использование
WorkManager. WorkManager — это библиотека, которая предоставляет гибкий API для планирования однократных или повторяющихся задач, которые гарантированно будут выполнены даже при перезапуске устройства.Создайте класс, который наследует
Worker или CoroutineWorker, и определите работу, которую необходимо выполнить в методе doWork().import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.Data
class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
// Выполните свою задачу
val outputData = Data.Builder()
.putString("key_result", "Task Completed Successfully")
.build()
// Возвращение результата
return Result.success(outputData)
}
}
Запланируйте и запустите задачу, используя
WorkManager. Настройте WorkRequest и передайте данные, если это необходимо.import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Создание WorkRequest
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.build()
// Запуск задачи
WorkManager.getInstance(this).enqueue(workRequest)
}
}
Используйте
LiveData для наблюдения за статусом и результатами выполнения задачи. Это позволяет получать обновления о состоянии задачи в реальном времени.import androidx.lifecycle.Observer
import androidx.work.WorkInfo
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java).build()
WorkManager.getInstance(this).enqueue(workRequest)
// Наблюдение за результатами выполнения задачи
WorkManager.getInstance(this).getWorkInfoByIdLiveData(workRequest.id)
.observe(this, Observer { workInfo ->
if (workInfo != null && workInfo.state.isFinished) {
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
val outputData = workInfo.outputData
val result = outputData.getString("key_result")
// Обработка результата
println("Result: $result")
} else if (workInfo.state == WorkInfo.State.FAILED) {
// Обработка ошибки
println("Task Failed")
}
}
})
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
В Android для обработки сообщений и выполнения задач в основном (главном) потоке часто используются классы
Handler и Looper. Handler позволяет отправлять и обрабатывать Message и Runnable объекты, ассоциированные с Looper. Главный поток приложения имеет свой Looper, который постоянно обрабатывает очередь сообщений и задач.В главном потоке
Handler можно создавать непосредственно, так как Looper уже существует. Это можно сделать в любом месте, например, в Activity или Fragment.import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private Handler mainHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Создание Handler, привязанного к Looper главного потока
mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// Обработка сообщений
if (msg.what == 1) {
// Пример обработки сообщения
String messageData = (String) msg.obj;
System.out.println("Received message: " + messageData);
}
}
};
// Пример отправки сообщения
Message message = Message.obtain();
message.what = 1;
message.obj = "Hello from background thread!";
mainHandler.sendMessage(message);
// Пример отправки задачи
mainHandler.post(new Runnable() {
@Override
public void run() {
System.out.println("Runnable executed on main thread");
}
});
}
}
Сообщения и задачи можно отправлять из любого потока. Например, из фонового потока:
new Thread(new Runnable() {
@Override
public void run() {
// Отправка сообщения
Message message = Message.obtain();
message.what = 1;
message.obj = "Hello from background thread!";
mainHandler.sendMessage(message);
// Отправка задачи
mainHandler.post(new Runnable() {
@Override
public void run() {
System.out.println("Runnable executed on main thread from background thread");
}
});
}
}).start();
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2😁1👀1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2🎉1
Looper и Handler являются основными компонентами для управления очередями сообщений в Android. Looper связывается с потоком, чтобы управлять очередью сообщений этого потока. Каждому потоку может быть назначен только один Looper, который обрабатывает сообщения и задачи, отправленные через Handler.По умолчанию главный поток приложения уже имеет Looper, и мы можем получить его с помощью
Looper.getMainLooper(). Для других потоков мы должны создать Looper вручную.Метод
Looper.prepare() создает Looper для текущего потока. Метод Looper.loop() запускает цикл обработки сообщений для этого Looper.Handler использует Looper, чтобы определять, к какому потоку привязан, и куда отправлять сообщения и задачи.public class LooperThread extends Thread {
public Handler handler;
private Looper looper;
@Override
public void run() {
// Подготовка Looper для текущего потока
Looper.prepare();
// Создание Handler, связанного с этим Looper
handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
// Обработка сообщений
System.out.println("Received message: " + msg.what);
}
};
// Получение ссылки на Looper текущего потока
looper = Looper.myLooper();
// Запуск цикла обработки сообщений
Looper.loop();
}
// Метод для остановки Looper
public void quit() {
if (looper != null) {
looper.quit();
}
}
}import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private LooperThread looperThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Запуск LooperThread
looperThread = new LooperThread();
looperThread.start();
// Отправка сообщения в новый поток через Handler
findViewById(R.id.button_send_message).setOnClickListener(view -> {
if (looperThread.handler != null) {
Message message = Message.obtain();
message.what = 1;
looperThread.handler.sendMessage(message);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// Остановка LooperThread
looperThread.quit();
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
FragmentTransaction — ручной способ добавления, замены и удаления фрагментов.
Navigation через Intent — используется для переключения между активностями или фрагментами внутри приложения или между приложениями.
Explicit и Implicit Intents — явные и неявные намерения для навигации между компонентами.
NavHostFragment и NavController — связаны с использованием графа навигации для управления фрагментами в приложениях Android.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥2
Проверка на наличие
Looper в потоке в Android осуществляется с помощью метода Looper.myLooper(). Метод Looper.myLooper()public class LooperCheck {
public static void main(String[] args) {
// Проверка наличия Looper в главном потоке
if (Looper.myLooper() != null) {
System.out.println("Looper существует в текущем потоке.");
} else {
System.out.println("Looper не существует в текущем потоке.");
}
// Запуск нового потока без Looper
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// Проверка наличия Looper в новом потоке
if (Looper.myLooper() != null) {
System.out.println("Looper существует в новом потоке.");
} else {
System.out.println("Looper не существует в новом потоке.");
}
// Создание Looper в новом потоке
Looper.prepare();
// Проверка снова после создания Looper
if (Looper.myLooper() != null) {
System.out.println("Looper теперь существует в новом потоке.");
} else {
System.out.println("Looper все еще не существует в новом потоке.");
}
// Запуск цикла обработки сообщений
Looper.loop();
}
});
thread.start();
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤2🔥1
Когда поток в Android разбирает пустую очередь сообщений с помощью
Looper.loop(), он будет заблокирован и останется в состоянии ожидания до тех пор, пока не появится новое сообщение или задача в очереди. Это нормальное поведение для потоков с Looper, которые предназначены для обработки сообщений и задач асинхронно.Когда
Looper.loop() вызывается, поток начинает обрабатывать сообщения и задачи из очереди, связанной с Looper.Looper будет продолжать обрабатывать сообщения и задачи, пока они есть в очереди. Если очередь пуста, поток не завершает свою работу. Вместо этого он переходит в состояние ожидания и будет разбужен, когда появится новое сообщение или задача.Если очередь пуста,
Looper вызывает метод MessageQueue.next() для получения следующего сообщения. Этот метод блокируется до тех пор, пока новое сообщение не станет доступным. Это означает, что поток будет находиться в состоянии ожидания и не будет потреблять значительное количество ресурсов, пока не появится новое сообщение.public class LooperThread extends Thread {
public Handler handler;
@Override
public void run() {
// Подготовка Looper для текущего потока
Looper.prepare();
// Создание Handler, связанного с этим Looper
handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
// Обработка сообщений
System.out.println("Received message: " + msg.what);
}
};
// Запуск цикла обработки сообщений
Looper.loop();
}
}
public class MainActivity extends AppCompatActivity {
private LooperThread looperThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Запуск LooperThread
looperThread = new LooperThread();
looperThread.start();
// Отправка сообщения в новый поток через Handler
findViewById(R.id.button_send_message).setOnClickListener(view -> {
if (looperThread.handler != null) {
Message message = Message.obtain();
message.what = 1;
looperThread.handler.sendMessage(message);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// Остановка LooperThread
looperThread.handler.getLooper().quit();
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥2
Позволяют переопределять поведение свойств, делегируя его другим объектам с помощью ключевого слова
by. На уровне компиляции Java компилятор Kotlin генерирует вспомогательные классы и методы доступа, которые используют делегаты.import kotlin.properties.Delegates
class Example {
var p: String by Delegates.observable("<no name>") {
prop, old, new -> println("$old -> $new")
}
}
fun main() {
val e = Example()
e.p = "first"
e.p = "second"
}
Компилятор создает классы для управления состоянием делегированных свойств.
Создаются геттеры и сеттеры, использующие вспомогательные классы.
Внутри методов доступа добавляются вызовы делегата для управления значениями.
public final class Example {
private final Delegates.Observable<String> p$delegate = Delegates.observable("<no name>", (property, oldValue, newValue) -> {
System.out.println(oldValue + " -> " + newValue);
return Unit.INSTANCE;
});
public final String getP() {
return this.p$delegate.getValue(this, Example.class.getDeclaredField("p"));
}
public final void setP(String value) {
this.p$delegate.setValue(this, Example.class.getDeclaredField("p"), value);
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
Когда речь идет о производительности, важно понимать, что делегаты и традиционные геттеры/сеттеры в Kotlin имеют разные цели и их производительность может зависеть от конкретного случая использования.
Геттеры и сеттеры в Kotlin, как и в Java, это простые методы для получения и установки значений полей. Они имеют минимальный оверхед и выполняются очень быстро.
class Example {
var value: String = ""
get() = field
set(value) {
field = value
}
}Делегаты добавляют гибкость и позволяют переопределять поведение свойств. Однако эта гибкость имеет цену в виде дополнительного оверхеда, поскольку делегаты могут выполнять дополнительные действия (например, логирование, валидацию, уведомление об изменениях).
import kotlin.properties.Delegates
class Example {
var value: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
Минимальный оверхед, высокая производительность.
Ограниченная функциональность, жестко заданное поведение.
Гибкость, возможность добавления дополнительной логики.
Больший оверхед из-за дополнительной логики и вызовов.
fun main() {
val exampleWithGetterSetter = ExampleGetterSetter()
val exampleWithDelegate = ExampleDelegate()
val iterations = 1_000_000
// Тест геттеров и сеттеров
var startTime = System.nanoTime()
for (i in 0 until iterations) {
exampleWithGetterSetter.value = "value"
val value = exampleWithGetterSetter.value
}
var endTime = System.nanoTime()
println("Getter/Setter time: ${(endTime - startTime) / 1_000_000} ms")
// Тест делегатов
startTime = System.nanoTime()
for (i in 0 until iterations) {
exampleWithDelegate.value = "value"
val value = exampleWithDelegate.value
}
endTime = System.nanoTime()
println("Delegate time: ${(endTime - startTime) / 1_000_000} ms")
}
class ExampleGetterSetter {
var value: String = ""
get() = field
set(value) {
field = value
}
}
class ExampleDelegate {
var value: String by Delegates.observable("<no name>") {
prop, old, new -> // Нет дополнительной логики для чистоты эксперимента
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM