Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created November 5, 2025 11:30
Show Gist options
  • Save sunmeat/c76c0617d99a146df538eb47c4abff03 to your computer and use it in GitHub Desktop.
Save sunmeat/c76c0617d99a146df538eb47c4abff03 to your computer and use it in GitHub Desktop.
alarm example android
MainActivity.java:
package site.sunmeat.services;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.Manifest;
import com.permissionx.guolindev.PermissionX;
public class MainActivity extends AppCompatActivity {
private static final String PREFS_NAME = "alarm_prefs";
private static final String KEY_COUNTER = "counter";
private static final String KEY_ALARM_RUNNING = "alarm_running";
private static final String ALARM_ACTION = "site.sunmeat.services.ALARM_ACTION";
private AlarmManager alarmManager;
private PendingIntent pendingIntent;
private SharedPreferences prefs;
private BroadcastReceiver alarmReceiver; // для отримання статусу з receiver
// запит дозволу на показ сповіщень (з використанням бібліотеки permissionx)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkNotificationsPermission();
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
registerAlarmStatusReceiver(); // реєструємо receiver для статусу
updateCounterDisplay(); // оновлюємо показ лічильника (якщо є ui елемент)
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterAlarmStatusReceiver();
}
// реєстрація receiver для статусу alarm (broadcast від MyReceiver)
private void registerAlarmStatusReceiver() {
alarmReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ALARM_ACTION.equals(action)) {
int newCounter = intent.getIntExtra(KEY_COUNTER, 0);
prefs.edit().putInt(KEY_COUNTER, newCounter).apply();
updateCounterDisplay(); // оновлюємо ui з лічильником
}
}
};
registerReceiver(alarmReceiver, new IntentFilter(ALARM_ACTION));
}
// unregister receiver
private void unregisterAlarmStatusReceiver() {
if (alarmReceiver != null) {
unregisterReceiver(alarmReceiver);
}
}
// оновлення показу лічильника (приклад: toast, або setText для TextView з id counter_text)
private void updateCounterDisplay() {
int counter = prefs.getInt(KEY_COUNTER, 0);
showToast("лічильник: " + counter);
// якщо є TextView counterText = findViewById(R.id.counter_text); то counterText.setText(counter + "");
}
// перевірка та запит дозволу на показ сповіщень
private void checkNotificationsPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PermissionX.init(this)
.permissions(Manifest.permission.POST_NOTIFICATIONS)
.request((allGranted, grantedList, deniedList) -> {
if (allGranted) {
showToast("дозвіл на показ сповіщень отримано");
} else {
showToast("дозвіл на показ сповіщень не отримано");
}
});
}
}
// виведення тоста
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void start(View v) {
// перевірка, чи вже запущено alarm
if (isAlarmRunning()) {
showToast("alarm уже запущено");
return;
}
scheduleAlarm();
prefs.edit().putBoolean(KEY_ALARM_RUNNING, true).apply();
showToast("alarm запущено");
}
public void stop(View v) {
// перевірка, чи alarm запущено перед зупинкою
if (!isAlarmRunning()) {
showToast("alarm не запущено");
return;
}
cancelAlarm();
prefs.edit().putBoolean(KEY_ALARM_RUNNING, false).apply();
showToast("alarm зупинено");
}
// планування alarm кожну секунду (для тесту; в реальності обмежено doze mode)
private void scheduleAlarm() {
Intent intent = new Intent(this, MyReceiver.class);
intent.setAction(ALARM_ACTION);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 1000; // кожну секунду
long triggerTime = System.currentTimeMillis() + interval;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
}
// для повтору: alarmManager.setRepeating, але setExact з циклом в receiver краще для точності
}
// скасування alarm
private void cancelAlarm() {
if (pendingIntent != null) {
alarmManager.cancel(pendingIntent);
pendingIntent.cancel();
}
}
// перевірка, чи запущено alarm (через prefs, бо немає прямого api)
private boolean isAlarmRunning() {
return prefs.getBoolean(KEY_ALARM_RUNNING, false);
}
// перевірка, чи запущено сервіс (старий метод, лишаємо для сумісності, але не використовуємо)
private boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// для зворотної сумісності, хоч getRunningServices і deprecated, але для власного сервісу спрацює :)
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
=============================================================================================================================
MyReceiver.java: (це ресівер, не сервіс!)
package site.sunmeat.services;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.PowerManager;
public class MyReceiver extends BroadcastReceiver {
private static final String PREFS_NAME = "alarm_prefs";
private static final String KEY_COUNTER = "counter";
private static final String ALARM_ACTION = "site.sunmeat.services.ALARM_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || !ALARM_ACTION.equals(intent.getAction())) {
return;
}
// отримуємо wakelock для виконання роботи
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyReceiver::WakeLock");
wakeLock.acquire(10 * 60 * 1000L /*10 хвилин*/); // тримаємо 10 хв max
try {
// основна робота: інкремент лічильника
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
int counter = prefs.getInt(KEY_COUNTER, 0) + 1;
prefs.edit().putInt(KEY_COUNTER, counter).apply();
// надсилаємо broadcast в активність для оновлення ui
Intent updateIntent = new Intent(ALARM_ACTION);
updateIntent.putExtra(KEY_COUNTER, counter);
context.sendBroadcast(updateIntent);
// плануємо наступний alarm (для циклу)
scheduleNextAlarm(context);
} finally {
wakeLock.release(); // відпускаємо wakelock
}
}
// планування наступного alarm
private void scheduleNextAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyReceiver.class);
intent.setAction(ALARM_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 1000; // кожну секунду
long triggerTime = System.currentTimeMillis() + interval;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
}
}
}
=============================================================================================================================
в цій реалізації без сервісу та сповіщень AlarmManager планує пробудження пристрою кожну секунду,
BroadcastReceiver отримує сигнал, інкрементує лічильник у SharedPreferences, надсилає broadcast для оновлення UI
(через toast або TextView) і планує наступний alarm.
// https://developer.android.com/develop/background-work/services/alarms
// https://developer.android.com/develop/background-work/background-tasks/awake/wakelock
// https://developer.android.com/develop/background-work/background-tasks/broadcasts
// https://developer.android.com/training/data-storage/shared-preferences
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment