Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active March 14, 2025 12:37
Show Gist options
  • Save sunmeat/c7e824f9c1e83c85e987c70e1ef8bb35 to your computer and use it in GitHub Desktop.
Save sunmeat/c7e824f9c1e83c85e987c70e1ef8bb35 to your computer and use it in GitHub Desktop.
foreground service android example
MainActivity.java:
package com.sunmeat.services;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_POST_NOTIFICATIONS_PERMISSION = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkNotificationsPermission();
}
// проверка и запрос разрешения на показ уведомлений
private void checkNotificationsPermission() {
if (isNotificationsPermissionGranted()) {
showToast("notification permission granted");
} else {
requestNotificationsPermission();
}
}
// проверка, предоставлено ли разрешение на показ уведомлений
private boolean isNotificationsPermissionGranted() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED;
}
// запрос разрешения на показ уведомлений
private void requestNotificationsPermission() {
ActivityCompat.requestPermissions(
this, new String[]{Manifest.permission.POST_NOTIFICATIONS},
REQUEST_POST_NOTIFICATIONS_PERMISSION);
}
// обработка результата запроса разрешения на показ уведомлений
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_POST_NOTIFICATIONS_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showToast("notification permission granted");
} else {
showToast("notification permission denied");
}
}
}
// вывод тоста
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void start(View v) {
if (isServiceRunning(MyService.class)) return;
Intent i = new Intent(this, MyService.class);
i.setAction("start");
ContextCompat.startForegroundService(this, i);
}
public void stop(View v) {
if (!isServiceRunning(MyService.class)) return;
Intent i = new Intent(this, MyService.class);
i.setAction("stop");
startService(i); // для остановки можно оставить startService, он передаст intent в onStartCommand
}
// проверка, запущен ли сервис
private boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
// альтернативный вариант, но не рабочий :)
public static boolean isServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null) {
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.processName.equals(context.getPackageName())) {
for (String service : processInfo.pkgList) {
if (service.equals(serviceClass.getName())) {
return true;
}
}
}
}
}
return false;
}
}
================================================================================================================
MyService.java:
package com.sunmeat.services;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
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++;
String info = counter + "";
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder;
Context context = getApplicationContext();
createNotificationChannel(manager);
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();
// создание и отправка уведомления
Notification notification = builder.build();
// long notificationCode = System.currentTimeMillis(); // будет генерироваться отдельная строка для каждого уведомления
long notificationCode = 2025; // решил обновлять одно и то же уведомление
manager.notify((int) notificationCode, notification);
return notification;
}
// cоздание канала уведомлений
private static final String CHANNEL_ID = "alex_channel";
private void createNotificationChannel(NotificationManager manager) {
if (manager.getNotificationChannel(CHANNEL_ID) == null) {
NotificationChannel 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")) {
startForeground(101, updateNotification());
h = new Handler();
r = new Runnable() {
@Override
public void run() {
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(true);
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
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App1"
tools:targetApi="31">
<service
android:name=".MyService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location|dataSync|mediaPlayback"
tools:ignore="ForegroundServicePermission" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@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