I want to make an app with a Broadcast Receiver. I read a lot in this topic and I understood that a Foreground service is the best for the task. It is REALLY important for the Broadcast Receiver to be active always and forever. The problem I am facing with is that the Foreground service works well until 24 hours from the moment it has been started, and after 24 hours the foreground is getting killed. The Foreground is also getting killed when my phone is under load, like when playing a heavy 3D game.
I have tried a lot (and I mean, a lot) of ways to overcome this obstacle, and some of them made things better, but not perfectly and not the way I want them to work.
My test phone is the Xiaomi Redmi Note 10, with android 11. From what I read, if I'll be able to find the solution for Xiaomi phones, I'll find the solution for all of the phones (I read that Xiaomi are the worst with all of that battery optimization stuff).
How can I keep my Foreground service alive even when my phone is under pressure?
Is there a better way to keep a Broadcast Receiver working and "listening"?
The way I start the Service in my Activity:
Intent intent = new Intent(this, MyService.class);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
startForegroundService(intent);
}else{
startService(intent);
}
My Service code:
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannle();
Intent intent1 = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent1, 0);
Notification notification = new NotificationCompat.Builder(this, "ChannelID1")
.setContentTitle("No More Spam Calls!")
.setContentText("We Are Protecting You.")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_STICKY;
}
private void createNotificationChannle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(
"ChannelID1", "Foreground notification", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
stopForeground(true);
stopSelf();
Intent intent = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
#Override
public void onLowMemory() {
onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
onDestroy();
}
}
Thank you very much!
Related
I am trying to write a music player for android and am using foreground service to run the music player. I am sending a pendingIntent from the UI controls to the service to play songs.
After receiving the intent, onStartCommand and onDestroy are called immediately. I am not sure how to stop the onDestroy the call.
I tried changing the pending intent to startService/ContextCompat.startForegroundService but the issue still persists.
Service:
import android.app.Service;
public class PlayerService extends Service{
private TransportControls transportControls;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mediaSessionManager == null) {
initMediaSession();
}
handleIntent(intent);
return START_STICKY;
}
private initMediaSession(){
...
transportControls = mediaSession.getController().getTransportControls();
mediaSession.setCallback(new MediaSessionCompat.Callback() {
// Implement callbacks
#Override
public void onPlay() {
play();
}
...
}
handleIntent(Intent intent){
// Initial checks
if(/* action in intent is play*/) transportControls.play();
}
private void play(SongInfo songInfo){
...
buildNotificaiton(//play action);
}
private void buildNotification(){
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
this, "default").setContentIntent(intent)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setStyle(new MediaStyle()
.setMediaSession(mediaSession.getSessionToken())
.setShowActionsInCompactView(0, 1, 2))
.setContentText(StringUtils.parseArtists(songInfo.artists()))
.setContentTitle(songInfo.displayName())
.setContentInfo(songInfo.album())
.setSound(null);
NotificationManager notificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("default",
"Bhatki media notification",
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(
"Notification displayed when music is being played. This notification is "
+ "required for the music to play.");
notificationManager.createNotificationChannel(channel);
}
// Execution is reaching this line.
startForeground(NOTIFICATION_ID, notificationBuilder.build());
}
}
In activity:
Intent intent = new Intent();
intent.setClass(context, PlayerService.class);
intent.setAction(action);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// one approach
ContextCompat.startForegroundService(context, intent);
// Different approach
// PendingIntent pendingIntent = PendingIntent
// .getService(serviceContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// try {
// pendingIntent.send(serviceContext, 0, intent);
// } catch (CanceledException e) {
// Log.d(TAG, "sendIntent: failed");
I also added that android foreground service permission in the manifest:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
I am expecting the notification and service to persist. Can't figure out why OnDestroy is called.
Once the service has been created, the service must call its startForeground() method within five seconds.
Apps that target Android 9 (API level 28) or higher and use foreground services must request the FOREGROUND_SERVICE permission
foreground service
Edit: I can't tell the problem from your code but from my experience, the notification block should be in override fun onCreate()
I want to take signal measurements. My app activity takes masurements (and provide charts) when is in foreground and at the onPause event I call and bind a service to take measurements (and store them in the database) in order to replace the activity.
However if the phone is unpluged the app stop taking measurements. I have study a lot of other posts and I have implemented in foreground with notification.
Here is a code sample from the Service
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,START_STICKY,startId);
goForeground();
return Service.START_STICKY; }
private void goForeground() {
Log.i(TAG ,"goForeground");
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification n = new Notification.Builder(this)
.setContentTitle("Measurements Service")
.setContentText("App still collects measurements.")
.setSmallIcon(R.drawable.myApp)
.setContentIntent(pendingIntent)
.setPriority(Notification.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.build();
NotificationManager notificationManger =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManger.notify(01, n);
startForeground(FOREGROUND_NOTIFICATION_ID, n);
}
and the MainActivity
Intent serviceIntent = new Intent(MainActivity.this, SnoopService.class);
if (SnoopService.isRunning()) {
Log.d(TAG, "Service is running");
doBindService(intent);
} else {
Log.d(TAG, "Service will run");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else
startService(intent);
doBindService(intent);
}
isBound = true;
}
Does anyone knows if I am doing something wrong?
I have test it with Samsung Galaxy S8.
In my app I use a foreground service that must run constantly. Sometimes the foreground service is stopped.
Under what circumstances can the OS kill my service (it happen even if there is enough memory, battery is full, phone is charging)?
This is what my code looks like until now:
public class ServiceTest extends Service {
public static Thread serverThread = null;
public Context context = this;
public ServiceTest(Context context) {
super();
this.context = context;
}
public ServiceTest() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (this.serverThread == null) {
this.serverThread = new Thread(new ThreadTest());
this.serverThread.start();
}
return START_STICKY;
}
private class ThreadTest implements Runnable {
#Override
public void run() {
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification notification = new NotificationCompat.Builder(context)
.setContentTitle("Notification title")
.setContentText("Notification text")
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.setSmallIcon(R.drawable.android)
.setOngoing(true).build();
startForeground(101, notification);
while(true){
//work to do
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
There is not a single... Many problems in your code... You may be getting it "0 Errors" as it is syntactically correct but it is androidicaly wrong, your basics are poor, reading of android documentation and implementation is very poor. Android never runs very poor things...
Problem : 1
Do you know for a service conventionally you should override onCreate, onStartCommand, onBind, onDestroy methods....?
I don't see onDestroy there....!!
Problem : 2
Do you know how to notify...? Your onStartCommand implementation is again making no sense.
KEEP IT EMPTY JUST RETURN START_STICKY
Problem : 3
How do you expect to run this under background execution limits...? Notify android first by making notification in oncreate only and with startforeground if needed...
I don't see it there.... you trying to do it in onstartcommand and again it is very poorly...
Well... take a look at working code below :
public class RunnerService extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "1";
public RunnerService() { }
#Override
public void onCreate()
{
super.onCreate();
Log.d("RUNNER : ", "OnCreate... \n");
Bitmap IconLg = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this, null);
mBuilder.setContentTitle("My App")
.setContentText("Always running...")
.setTicker("Always running...")
.setSmallIcon(R.drawable.ic_menu_slideshow)
.setLargeIcon(IconLg)
.setPriority(Notification.PRIORITY_HIGH)
.setVibrate(new long[] {1000})
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setOngoing(true)
.setAutoCancel(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{1000});
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(notificationChannel);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(1, mBuilder.build());
}
else
{
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
mNotifyManager.notify(1, mBuilder.build());
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d("RUNNER : ", "\nPERFORMING....");
return START_STICKY;
}
#Override
public void onDestroy()
{
Log.d("RUNNER : ", "\nDestroyed....");
Log.d("RUNNER : ", "\nWill be created again automaticcaly....");
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("NOT_YET_IMPLEMENTED");
}
}
How to check....???
Remove the app from recents list and you should see in your logs the "Performing " message in logcat...
In what conditions it stops...?
It never stops ( until next boot..!! )... Yes it stops when user force stops application. And rarely if system finds it is having very low resources .... which is a very rare condition seems to occur as android has improved a lot over the time....
How to start it....?????
Wherever it may be from mainactivity or from receiver or from any class :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, RunnerService.class));
}
else
{
context.startService(new Intent(context, RunnerService.class));
}
How to check is service started or not....?
Simply Don't..... Even if you starts service how many times you wants.... If it is already running... then it won't be start again.... If not running then... will start it...!!
The criticism made in the chosen answer is not reasonable if the service needs an intent to work.
On higher version of Android, System will pause any foreground service while the device is locked, to minimize the power consumption even if it returns START_STICKY. So, to make a foreground task constantly, a wakelock is required.
Here's what android documentation describes wakeLock:
To avoid draining the battery, an Android device that is left idle quickly falls asleep. However, there are times when an application needs to wake up the screen or the CPU and keep it awake to complete some work.
To make a foreground service running constantly, acquire a wakeLock from inside the onCreate().
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyApp::MyWakelockTag");
wakeLock.acquire();
For further detail, have a look at official Android Documentation.
I am new to android. I have already developed android application which starts service when application starts.
What I have :
Currently when application starts, it creates sticky notification in notification area as shown in screenshot.
Source code for service :
public class InfiniteService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Intent notificationIntent = new Intent();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(getString(R.string.app_name))
.setContentText("Welcome To TestApp")
.setContentIntent(pendingIntent).build();
startForeground(1337, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Log.e("InfiniteService", "Service started");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
//Log.e("InfiniteService", "onDestroy");
sendBroadcast(new Intent("InfiniteService"));
}
What I want :
I want to get rid of this sticky notification in notification area. Instead I want service that runs in background continuously.
What I have tried :
I tried 'return START_NON_STICKY' in onStartCommand with initial impression that it will remove sticky notification but later learned from here that START_STICKY tells the OS to recreate the service after it has enough memory
I was suspecting PendingIntent is making this but again after reading explanation, come to know that it is not what I am looking for.
Then I searched how to create sticky notification in android hoping to get some clue. After reading this article, got to know flags makes it sticky. I searched "Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT" in my entire code but did not find anything.
What am I missing? Any pointers/help would be great. Thanks.
This is onStartCommand()
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Notification creation code
telMgr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
telMgr.listen(new PSL(), PhoneStateListener.LISTEN_CALL_STATE);
return super.onStartCommand(intent, flags, startId);
}
and PhoneStateListener class (under service class)
public class PSL extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNum) {
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
case TelephonyManager.CALL_STATE_OFFHOOK:
//Work1
break;
case TelephonyManager.CALL_STATE_RINGING:
//Work2
break;
}
}
}
both of them are in same .java file
I have these code on one service class.
when I call startService() from main activity, it works well.
but when my app is killed by Task Manager, Killer or automatically by shortage of memory on device, Service does restarts but not working.
when i go to Setting - Application - Running, it shows process 1 and service 1, before/after killed.but after killed, memory share goes 1/10. I have tried startForeground() not to be killed easily with my notification - it didn't work. (doesn't show any notification)
and also tried return of onStartCommand(): START_STICKY, START_REDELIVER_INTENT - shows same result is there any way I can restart completely or make it not killed?
After Spending few Hours I fount that , For Android 2.0 or later you can use the startForeground() method to start your Service in the foreground.
Documentation provided by Android
A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)
Ther are very rare chances of Foreground Service to kill by OS.But It works fine.
public class ServicePhoneState extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Music Player")
.setTicker("Google Music Player")
.setContentText("My Music")
.setSmallIcon(R.drawable.ic_bg_icon)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setOngoing(false)
.build();
startForeground(10, notification);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I solved it by startForeground()
It didn't work because I had not used Notification.FLAG_FOREGROUND_SERVICE on Notification
After Spending few Hours I fount that , For Android 2.0 or later you can use the startForeground() method to start your Service in the foreground.
Documentation provided by Android
A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.) Ther are very rare chances of Foreground Service to kill by OS.But It works fine.
public class ServicePhoneState extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyPhoneStateListener phoneListener = new
MyPhoneStateListener(ServicePhoneState.this);
TelephonyManager telephony = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Music Player")
.setTicker("Google Music Player")
.setContentText("My Music")
.setSmallIcon(R.drawable.ic_bg_icon)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setOngoing(false)
.build();
startForeground(10, notification);
return START_NOT_STICKY;}
#Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;}}