Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active November 20, 2025 10:54
Show Gist options
  • Select an option

  • Save sunmeat/c7e824f9c1e83c85e987c70e1ef8bb35 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/c7e824f9c1e83c85e987c70e1ef8bb35 to your computer and use it in GitHub Desktop.
foreground service android example
MainActivity.java:
package site.sunmeat.services;
import android.app.ActivityManager;
import android.content.*;
import android.os.Build;
import android.os.Bundle;
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 {
// запит дозволу на показ сповіщень (з використанням бібліотеки permissionx)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkNotificationsPermission();
}
// перевірка та запит дозволу на показ сповіщень
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) {
// перевірка, чи вже запущено сервіс
if (isServiceRunning(MyService.class)) {
showToast("сервіс уже запущено");
return;
}
var i = new Intent(this, MyService.class);
i.setAction("start");
ContextCompat.startForegroundService(this, i);
showToast("сервіс запущено");
}
public void stop(View v) {
// перевірка, чи сервіс запущено перед зупинкою
if (!isServiceRunning(MyService.class)) {
showToast("сервіс не запущено");
return;
}
var i = new Intent(this, MyService.class);
i.setAction("stop");
startService(i); // для зупинки можна залишити startService, він передасть intent в onStartCommand
showToast("сервіс зупинено");
}
// перевірка, чи запущено сервіс
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;
}
}
================================================================================================================
MyService.java:
package site.sunmeat.services;
import android.app.*;
import android.content.*;
import android.os.*;
import androidx.core.app.NotificationCompat;
public class MyService extends Service {
private Handler h;
private Runnable r;
int counter = 0;
@Override
public IBinder onBind(Intent intent) {
return null;
}
private Notification updateNotification() {
counter++;
var info = counter + "";
var manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Context context = getApplicationContext();
createNotificationChannel(manager);
var builder = new NotificationCompat.Builder(this, CHANNEL_ID);
PendingIntent action = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), PendingIntent.FLAG_IMMUTABLE);
// налаштування параметрів сповіщення
builder.setContentIntent(action)
.setContentTitle(info)
.setTicker(info)
.setContentText(info)
.setSmallIcon(R.drawable.barcode)
.setOngoing(true)
.build();
// повертаємо сповіщення без notify, щоб використовувати в startForeground
return builder.build();
}
// створення каналу для сповіщення
private static final String CHANNEL_ID = "alex_channel";
private void createNotificationChannel(NotificationManager manager) {
if (manager.getNotificationChannel(CHANNEL_ID) == null) {
var channel = new NotificationChannel(CHANNEL_ID, "AlexChannel", NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("My notification channel description");
manager.createNotificationChannel(channel);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getAction() != null && intent.getAction().contains("start")) {
// створюємо перше сповіщення та запускаємо foreground з ним
Notification initialNotification = updateNotification();
startForeground(101, initialNotification);
h = new Handler(Looper.getMainLooper());
r = new Runnable() {
@Override
public void run() {
// оновлюємо сповіщення через startForeground з тим же id
startForeground(101, updateNotification());
h.postDelayed(this, 1000);
}
};
h.post(r);
} else if (intent != null && intent.getAction() != null && intent.getAction().contains("stop")) {
if (h != null && r != null) {
h.removeCallbacks(r);
}
stopForeground(STOP_FOREGROUND_DETACH);
stopSelf();
}
return Service.START_STICKY;
}
}
==================================================================================================================
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="#00AA00"
android:onClick="start"
android:text="Запустити сервіс!"
android:textAllCaps="false" />
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="#AA0000"
android:onClick="stop"
android:text="Зупинити сервіс!"
android:textAllCaps="false" />
</LinearLayout>
===================================================================================================================
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
...>
<service
android:name=".MyService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location|dataSync|mediaPlayback"
tools:ignore="ForegroundServicePermission" />
<activity...
===================================================================================================================
build.gradle.kts:
dependencies {
...
implementation("com.guolindev.permissionx:permissionx:1.8.1")
===================================================================================================================
// alternative way: https://gist.github.com/sunmeat/c76c0617d99a146df538eb47c4abff03
public class MainActivity extends AppCompatActivity {
boolean runn = false;
// запит дозволу на показ сповіщень (з використанням бібліотеки permissionx)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
checkNotificationsPermission();
runn = isServiceRunning(MyService.class);
if (runn) {
stop(null);
}
else
start(null);
finish();
}
@dcarlson661
Copy link

Oleksandr,

Hi. This example is very clear and useful. Thank you for posting.

Dave Carlson
Valencia, Ca. USA

@prasath95
Copy link

I built this code as an app and run it, it stopped after 12 hours off works.

@bottookku
Copy link

if i press button named Clear all in recent app the service is die

@Rezashatery
Copy link

great sample
thanks

@nishit-pugmarker
Copy link

nishit-pugmarker commented Feb 25, 2022

You missed: AndroidManifest.xml:
<service android:enabled="true" android:name="com.alex.services.MyService"></service>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment