Dismiss notification on low memory? - android

As you can see from the picture, this is what happens to my fancy media player notification when the system goes low on memory. This notification is issued from a service and when the system goes low on memory, the service is stopped, too, automatically by the system.
From the behavior, it seems that the Service's onDestroy() is not called because it is implemented to get rid of the notification. So, i decided to implement onTrimMemory() as follows:
#Override
public void onTrimMemory(int memory){
Log.d("TRIM", "Please, Trim Memory");
String stop = getResources().getString(R.string.stopping_playback);
if(MusicService.status == 1){
stopService(PodcastrApplication.newInstance().getMusicServiceIntent());
}
Toast.makeText(PodcastrApplication.this, stop, Toast.LENGTH_SHORT).show();
}
But that did not work. I didn't even see the Toast or the Log.
So, how do I remove the notification when the system goes low on memory?
PS: At the point when this happens, it is certain that the MediaPlayer has stopped playing (automatically). It is only a matter of removing the notification.

When the service terminates, it notifies all its clients about this event. Therefore, the corresponding ServiceConnection object in the client activity receives a onServiceDisconnected() callback. You can remove the notification from within this method.
EDIT:
private ServiceConnection connection = new ServiceConnection(){
public void onServiceDisconnected(ComponentName name){
ClientActivity.this.runOnUiThread(new Runnable({
public void run(){
getSystemService("notification").cancelAll();
}
}));
}
public void onServiceConnected(...){...}//do something if you need to
};
public void onPause(){
Intent i = getIntentForService();
bindService(i, connection, Context.BIND_AUTO_CREATE);
}
public void onStop(){//or onDestroy()
unBindService(connection);
}

Related

why android service is stopped by Android OS?

OK, here is my code, I'm trying to create a running service even when the app is closed.
In main activity, I have created a new button and call startMyService() to start the service as following:
public void startMyService(View view) {
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
}
the Service class is simple :
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("MyService", "service is running");
final Uri uri = Settings.System.DEFAULT_RINGTONE_URI;
final Context x =(Context) MyService.this;
new Thread(new Runnable() {
#Override
public void run() {
MediaPlayer player = MediaPlayer.create(x,uri);
player.setLooping(true);
player.start();
}
}).start();
Toast.makeText(getApplicationContext(), "Service is running", Toast.LENGTH_LONG).show();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
Log.e("MyService", "service done");
}
}
Of course, I have added the service to manifest
<service android:name=".MyService" android:exported="false" android:description="#string/service_description" />
Now after running, I pressed the button to start the service, and close the activity, I supposed the music will be playing in the background but it stopped just after closing the activity.
How to solve this issue? How to make the service still running, and how to make it running again after an android OS destroying it?
I know there are too many topics about android services and START_STICKY
However, as you see this is not working in code above, why?
Note: This is not about playing music in the background, I used playing music because it is the simplest way to know when service is stopped, this is about how to make service keeps running in the background as supposed to be, for example, to do some task like tracking data changes from the server.
It's normal behavior when your application target from android O, if you want to remain your Service you should use startForgroundService with Notification. Read here
While an app is in the foreground, it can create and run both foreground and background services freely. When an app goes into the background, it has a window of several minutes in which it is still allowed to create and use services. At the end of that window, the app is considered to be idle. At this time, the system stops the app's background services, just as if the app had called the services' Service.stopSelf() methods.

Android: How do I communicate to a STICKY Service if the app is alive or not? Is there an Android built-in way of doing that?

I created a service that get the user location even if the app is killed. The service is running perfectly and it get's the location correctly even if the app is killed. Which is the optimal/safer way to detect if the app is still alive to send the location either to main activity(If the app is alive or running on the background) or to a backend(if the app is killed)?. This is what I have so far(I'm checking the permissions on the main activity if someone wants to know, that's why I disabled them here):
#SuppressWarnings("MissingPermission")
public class GPSService extends Service {
private LocationListener listener;
private LocationManager locationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
listener= new LocationListener() {
#Override
public void onLocationChanged(Location location) {
//To transfer the data to the main activity I use broadcast receiver in the main activity, using an intent filter location_update
Intent intentSendLocationMainActivity = new Intent("location_update");
Log.d("Location-update",location.getLongitude()+" "+location.getLongitude());
intentSendLocationMainActivity.putExtra("coordinates",location.getLongitude()+" "+location.getLongitude());
//I need to differentiate here if the app is killed or not to send the location to main activity or to a server
sendBroadcast(intentSendLocationMainActivity);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
Intent activateGPSIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
activateGPSIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activateGPSIntent);
}
};
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
//noinspection MissingPermission, listen for updates every 3 seconds
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,3000,0,listener);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearFromRecentService", "Service Started");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed, Removing update location listener");
//unregistering the listener
/*if(locationManager != null){
locationManager.removeUpdates(listener);
}*/
}
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//here you can call a background network request to post you location to server when app is killed
Toast.makeText(getApplicationContext(), "Warning: App killed", Toast.LENGTH_LONG).show();
//stopSelf(); //call this method to stop the service
}
}
And in the manifest file I have
<service
android:name="applicatives.GPSService"
android:exported="false"/>
I came up with a possible solution, set an "isAlive" boolean on the onResume() and on the onStop() methods of the mainActivity and then use that to commnunicate to the service that the app is dead or alive.....
1) Is there a built-in way of checking that?
2) In case there isn't, will that boolean suffice?
Sorry, I still don't get your architecture (see discussion in comments). In any case, even if the app is "running", it could be in the background, which means that even if you sent data to the app, the user wouldn't necessarily see it. In my opinion your architecture is flawed.
I would use a bound Service. Have your Activity bind to the Service. If the Activity is no longer running (ie: it finished, or Android killed the process hosting the Activity), your Service will get a call to onUnbind(). In this case you know that the Activity is "dead".
From the question and comments I can digest the following requirements:
If MainActivity is in a "started" state (i.e. state between onStart() and onStop()), which means that it is visible to the user (but might be obscured by e.g. dialog), data need to be sent to that Activity.
If MainActivity is in "stopped" state (i.e. state before onStart() or after onStop()), which means that it is not visible, data need to be sent to backend (reason doesn't matter).
If the above requirements is what you need, then the scheme is as follows:
Implement bound service
Service has addListener(SomeListener listener) and removeListener(SomeListener listener) methods
MainActivity implements SomeListener
MainActivity (or any other Activity or Fragment or Service) binds the service in onStart() and registers itself as a listener when the service is connected (onServiceConnected() called)
MainActivity unregisters from listening to the service in onStop() and unbinds
Whenever new data available, Service checks whether there are any listeners registered and either notifies all the listeners, or sends data to backend if there are none
You can play with this scheme in order to tailor it to your exact needs.

Android Service with MediaPlayer gets recreated or destroyed

I'm using bound service so that I am able to communicate between an activity and a service.
I'm binding to a service in onStart:
#Override
protected void onStart() {
super.onStart();
Intent bindIntent = new Intent(this, MusicService.class);
bindService(bindIntent, this, BIND_AUTO_CREATE);
}
waiting for service to bind:
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMusicService = ((MusicService.LocalBinder) service).getService();
mMusicService.setCallback(this);
}
handling disconnect from service:
#Override
public void onServiceDisconnected(ComponentName name) {
mMusicService = null;
}
unbinding from service in onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
if (mMusicService != null && isFinishing()) {
mMusicService.setCallback(null);
unbindService(this);
}
}
My problem is that when app is minimized, onDestroy gets called immediately and then onUnbind in Service gets called and music is stopped.
Here is onUnbind method (where mPlayer is MediaPlayer):
#Override
public boolean onUnbind(Intent intent) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
return super.onUnbind(intent);
}
If I don't implement onUnbind music continues to play (sometimes and sometimes it stops after some time) and when I open the app again (from minimized applications) I am able to play another song and then those two song play at same time.
I've red couple articles about music players and services on android and I thought that this was correct approach (thinking that onDestroy will be called when OS is out of memory).
Any ideas how I can re-implement my app workflow so that I will work as expected?
EDIT
At first I thought that "Don't keep activities" under developer options is a problem, but problem is still there even if I uncheck it.
And if some code from my service is needed please say I will edit my question (there's a lot of code and I'm not sure which part is important for this issue)
SOLUTION:
startForeground(<notification id>, <notification>);
to run service even if app gets killed. And when user dismisses the notification:
stopForeground(true);
stopSelf();
More about startForeground here.
It's not obvious, but you should start a Thread that runs in background and use the service to control it's state.
A service isn't a thread that hold some run state like a thread. Unless it's an IntentService. (Correct me if I'm wrong)
While activity can (and probably will) get destroyed, your app will keep running.
Activity:
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, BackgroundService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
finishOnPause = true;
}
#Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
}
Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ensureServiceThread();
if (intent != null) {
}
return START_NOT_STICKY;
}
private void ensureServiceThread() {
if (service_thread == null) {
service_thread = new ServiceThread(this);
service_thread.start();
}
}
#Override
public void onDestroy() {
stopServiceThread();
super.onDestroy();
}
private void stopServiceThread() {
if (service_thread != null) {
service_thread.interrupt();
service_thread = null;
}
}
And you should do your work inside the Thread.
If you need context, it's your Service.
If you need to do something on Main thread - create a Handler in Service.OnCreate and do a handler.postRunnable inside a worker thread safely.
What I would do is:
- Create a service
- Create a thread
- Create a media player inside a thread (if possible, otherwise on Service creation and pass it to thread)
- Inside a thread - continuously poll media player state
- On song finished send an intent to service that change track
- stop thread/service if needed.

Service and MediaPlayer - Control screen rotation and App exit

SOLVED - See answer.
I'm dealing with an app that uses a service for playing music with the MediaPlayer class.
I'm having problem with screen rotation and when I leave the app. I lose the reference or state of the MediaPlayer object or the service itself. Been working so many hours with this that not really know what is wrong.
I start and bind to the service in Activity's onResume:
#Override
protected void onResume()
{
Intent intent=new Intent(getApplicationContext(), MusicService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
I get the reference to the service with a ServiceConnection:
private ServiceConnection serviceConnection=new ServiceConnection()
{
public void onServiceConnected(ComponentName className, IBinder binder)
{
service=((MusicService.MyBinder)binder).getService();
}
public void onServiceDisconnected(ComponentName className)
{
service=null;
}
};
At this point I can call any method from my service: service.playMusic(); and works fine.
I unbind in onDestroy:
#Override
protected void onDestroy()
{
unbindService(serviceConnection);
super.onDestroy();
}
And this is my Service class:
public class MusicService extends Service
{
private final IBinder binder=new MyBinder();
private MediaPlayer player;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return Service.START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent)
{
player=MediaPlayer.create(getApplicationContext(), R.raw.tumbler);
player.setLooping(true);
player.setVolume(80, 80);
return binder;
}
public class MyBinder extends Binder
{
public MusicService getService()
{
return MusicService.this;
}
}
public void play()
{
player.start();
Log.d("MUSIC SERVICE", "play!");
}
}
So, problems are:
When I exit, the service and music keeps playing. I want to stop it. If I enter the app again, same service is launched again and the music is playing twice. I want to stop it.
When I rotate the screen, the MediaPlayer keeps playing (as I want) but I can't call anymore pause(), start() and so on because it state has changed (I get the message ' MediaPlayer﹕ pause called in state 8' and `MediaPlayer﹕ start called in state 0').
Please, need some help with this. Thanks in advance!
#Override
public IBinder onBind(Intent intent) {
player=MediaPlayer.create(getApplicationContext(), R.raw.tumbler);
player.setLooping(true);
player.setVolume(80, 80);
return binder;
}
You're re-initializing the MediaPlayer every time you bind to the service and never stopping the previous instance. Don't start the media in onBind(), wait for a command to the service to start/stop the media player. Services outlive the Activity (that's what they're designed for) so it's no surprise that it keeps playing after the Activity is destroyed. Unless you specifically told it otherwise, that's the expected behavior. If you're wanting a media player that just keeps going while the Activity is open, you might want to look into using a headless Fragment to keep a retained Fragment that can manage the MediaPlayer for you in onCreate()/onDestroy().
Well I think I got it.
I had a mess in my mind (and code) but finally it's working.
If this is not the correct way to do this, please let me know.
The first of my problems was: When the App exits the Service wasn't destroyed and the MediaPlayer was playing until I force-close the App. If I didn't force-close, on App relaunch I had 2 MediaPlayers playing simultaneously and so on.
Cause: I was not terminating my Service manually.
Solution: On Activity's onDestroy(), call unbindService() and stopService():
#Override
protected void onDestroy()
{
super.onDestroy();
unbindService(serviceConnection);
stopService(new Intent(getApplicationContext(), MusicService.class));
}
The second problem was: When the screen was rotated I lost the MediaPlayer's object reference, and music was out of control because I couldn't access the object.
Cause: Bad design. I was setting up the MediaPlayer into Service's onBind() method. Thanks to #kcoppock. He noticed that. So, when the screen was rotated, Activity's onCreate() was called and I have bindService() on it, causing the creation of the new MediaPlayer object without even cleaning up the former instance.
Solution: Just move out that code to another part that is not called automatically, and call it manually once, when the App starts, and not every time that Activity is recreated by configChanges.
So, basically that is how I fixed it, hope it may help to other users.
Cheers.

Started and Bound service is mysteriously stopped and restarted

My activity attempts to start and then bind a service that's supposed to run independently of it. This service turns the GPS on.
onCreate calls getApplicationContext.StartService, and onResume calls getApplicationContext.BindService. OnPause calls getApplicationContext.unbindService, although it never appears to run properly (the service connection never logs an unbind, although binds are logged when I treat it similarly).
Unfortunately, when I open up my Recents list, and slide the activity away, the Service stops and then re-starts itself almost immediately, dropping the GPS connection. What would cause this sort of behavior?
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
// Start up the service
Intent intent = new Intent(getApplicationContext(), LockService.class);
getApplicationContext().bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
Log.i(tag, "onPause");
if (isBound) {
getApplicationContext().unbindService(myConnection);
}
}
...
// Bound service stuff
LockService myService;
boolean isBound = false;
private ServiceConnection myConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LockBinder binder = (LockBinder) service;
myService = binder.getService();
isBound = true;
final boolean status = myService.getStatus();
Log.i(tag, "service bound");
}
#Override
public void onServiceDisconnected(ComponentName className) {
isBound = false;
Log.i(tag, "service unbound");
}
};
Edit: I checked into this answer, but this just prevents the service from restarting immediately (it still gets hung up, and re-opening the activity re-initializes it):
Android onCreate Service called when Activity onDestroy called
Edit 2: I had gotten my hopes up a bit too far with this answer. It doesn't seem to fix anything either.
How to force the onServiceDisconnected() get called?
Edit 3: This might be a KitKat thing.
In android 4.4, swiping app out of recent tasks permanently kills application with its service . Any idea why?
Because swiping an app out of the Recents list kills it in Android version 4.4 (KK), I have opted to simply not show my app in the Recents list at all.
Oh well, it didn't really need to live there anyway. It exists quite happily inside the notification bar. I pity anyone who isn't so lucky, and needs to forcibly restart the service via a timer and some hackneyed code.

Categories

Resources