Created
November 5, 2025 11:30
-
-
Save sunmeat/c76c0617d99a146df538eb47c4abff03 to your computer and use it in GitHub Desktop.
alarm example android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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