I add a local service to my MainActivity, in the onResume, I did this
#Override
public void onResume() {
super.onResume()
boolean is_start = isMyServiceRunning(MyService.class)
if (is_start) {
bindMyService()
} else {
startMyService()
bindMyService()
}
}
In onPause I just simply do the "unBindMyService" operation.
Also, I add the Context.BIND_AUTO_CREATE flag to bind the service, the result is very strange.
I can see MyService's "onCreate" and "onBind" with logcat, this goes smoothly
When I switch to another activity or app, The "Unbind" is called, which is correct!
When I "force stop" the service in settings, the "onDestroy" of the Service is called in response, that is OK.
When I remove the app from the "Recent List" of the apps, there are no "onDestroy" of the Service is called, I can explain it as that the service is not terminated. also OK.
What I can't explain is that after 4, I launched my app again, I've noticed that the "onCreate" and "onBind" of the service is called, but without a single "onDestroy" of the Service. Even when "is_start" is true, the Service is created again without an "onDestroy" called.
So what happened between 4 and 5? The service is still alive or is dead?
you need to stop service to call onDestroy.
Use this:
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
stopService(new Intent(this,MyService.class));
}
Related
When my service is running and I can see my app on the screen, everything works fine. My service sends broadcast messages and I can see them in my "MainActivity". But as soon as I push the home button and my app is no longer in the foreground, it stops. The service is still running but the broadcasts don't arrive at my "dead" or "pausing" app. This is my code:
Main Activity:
onCreate:
Intent intent = new Intent(getApplicationContext(), MainGate_Service.class);
startService(intent);
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
private BroadcastReceiver ServiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("ServiceMessage");
if(message != null) {
if(alarmActive && message.equals("ring"))
new soundPlayer().execute();
}
setNoti(message);
}
}
Service:
#Override
protected void onHandleIntent(Intent intent) {
ring = rcv_ring();
Intent ServiceIntent = new Intent("MainGate_ring");
ServiceIntent.putExtra("ServiceMessage", ring);
LocalBroadcastManager.getInstance(this).sendBroadcast(ServiceIntent);
}
whenever you press home button onStop gets call and there you are unregister receiver so there is not broadcast receiver who can receive broadcast.
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
Remove above line from onStop() or unregister it whenever your service stop.
onStop#LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
literally unregister receiver. So there is no receiver to receive broadcasted message from service.
The problem is here
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
Activity.onStart() :
Called after onCreate(Bundle) — or after onRestart() when the activity had been stopped, but is now again being displayed to the user. It will be followed by onResume().
Activity.onStop()
Called when you are no longer visible to the user. You will next receive either onRestart(), onDestroy(), or nothing, depending on later user activity.
So as soon as you leave your activity onStop() is called and you unregister your receiver.
Don't put your unregister code in onDestroy() either, because it may or may not be called and the Activity might be killed without calling it.
As suggested by #Naveen Dissanayake you might want to reconsider your logic.
You register your BroadcastReceiver inside activity, so, it will depend on Activity's lifecycle. When you press back button, activity goes into 'stopped' state - methods onPause and onStop is called. Android can also destroy this activity in it is low on resources.
Service, on the other hand, i smeant to be running inndefinetely, even when ui is not ready. If you wanat to receive notifications from Activity, there is two
possible solutions:
- Create, store and manage BroadcastReceiver in Application instance - Application class is still running until your app is destroyed. It seems like you want to play sound when service notify you about some action.
Register BroadcastReceiver in onCreate and unregister in onDestroy in notifications.
- Another solution - use another Service if you want to trigger some action or IntentService, reacting to that broadcast.
I woud consider solution - create some ResponseService, start it along with your MainGate_Service (from Application) and stop it from application too. In that Service register BroadcastReceiver or, add IntentFilter into manifest if you want it to start even when app is not running. In your Activity, bind to that service. Serive will know if some UI is attached (if Activity is bound), and, if it is, will notify activity. If it don't - will do some other things (perhaps, show notification)
If you want to receive Broadcast even if your Activity is in
Background then,
Register in onCreate(...)
#Override
protected void onCreate(Bundle sis) {
super.onCreate(sis);
setContentView(...);
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
Unregister in onDestroy()
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
I have an activity with two tabs. Clicking on two tabs will change the the fragments below the tabs. While that activity is in front I give out a notification, After that I minimize the app and kill that activity(not force stopping).
My problem is that am not getting call back in onDestroy while the activity is been killed by the user. Now if I click the notification the app will force close and thats because the activity for pending intent is been missing. Why am not getting the call back in onDestroy?
I found solution of that:
Create service:
public class MyService extends Service {
#Override
public final int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public final IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "APP KILLED", Toast.LENGTH_LONG).show(); // here your app is killed by user
try {
stopService(new Intent(this, this.getClass()));
} catch (Exception e) {
e.printStackTrace();
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
super.onTaskRemoved(rootIntent);
} else{}
}
}
and then start your service when app start:
startService(new Intent(this, MyService.class));
make sure you register service in your AndroidManifest.xml
<service
android:enabled="true"
android:name="yourPackageName.MyService"
android:stopWithTask="false" />
onDestroy is guaranteed to be called when you explicitly call finish().
On the contrary, when you are minimizing your app by pressing Home key onDestroy may well not be called right now. If your app stays in the background for a long time then onDestroy will be called.
For debugging purposes you can enable Settings|Developer Options|Don't save Activities. This way onDestroy will be called immediately when your app goes to background.
It is not sure to get callback in fragment's onDestroy(). When we kill the app Activity's onDestroy() will get the callback and the activity will be killed and fragment may not get callback.
As stated jn the documentation, onDestroy() can't be depended on, it will be called when the OS wants to kill the app, say in low memory conditions. Thus when the user hits the back button or home, onPause() or onStop() are called in place of it. Try implementing your callback in thr onPause() or onStop() method.
If my app is running and I press home button, the app goes in background. Now if I long press the home button and kill the app by swiping it from the recent app list, none of the events like onPause(), onStop() or onDestroy() gets called rather the process is terminated.
So if i want my services to stop, kill notifications and unregister listeners, how can I do that?
I just resolved a similar kind of issue.
Here is what you can do if its just about stopping service when application is killed by swiping from Recent app list.
Inside your Manifest file, keep flag stopWithTask as true for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="true" />
But as you say you want to unregister listeners and stop notification etc, I would suggest this approach:
Inside your Manifest file, keep flag stopWithTask as false for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="false" />
Now in your MyService service, override method onTaskRemoved. (This will be fired only if stopWithTask is set to false).
public void onTaskRemoved(Intent rootIntent) {
//unregister listeners
//do any other cleanup if required
//stop service
stopSelf();
}
Refer my question for more details, which contains other part of code, too.
We need to create a service that would clear the application from recent service
public class ClearService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearService", "Service Started");
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearService", "Service Destroyed");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearService", "END");
//Code here
stopSelf();
}
}
register this service in manifest.xml
<service android:name="com.package.ClearService" android:stopWithTask="false" />
Then start this service on your splash activity
startService(new Intent(getBaseContext(), ClearService.class));
And now whenever you will clear your app from android recent Then this method onTaskRemoved() will execute.
I resolved similar issue. If you want after swiping from recent task and on next launch it to behave properly then follow below steps:-
1) Save process ID in shared preference:
SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());
2) When application is launched from launcher after clear from recent task then do:
int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);
int currentProcessID = android.os.Process.myPid();
if ((previousProcessID == currentProcessID)) {
// This ensures application not killed yet either by clearing recent or anyway
} else {
// This ensures application killed either by clearing recent or by anyother means
}
When you press home - onPause and onStop of your Activity is being called, so at this time you have to do all savings and cleanup, because Android platform doesn't further guarantee that onDestroy or any other lifecycle method would be invoked, so the process could be killed without any notification.
ViewModel.onCleared() can be useful, if the goal is to release some resource (perhaps a system running somewhere else on the network) when the user executes a surprise exit by swiping, rather than by pressing the "stop" or button. [This is how I originally arrived at this question].
Application doesn't get a notification, and Activity.onDestroy() gets called for configuration changes such as changes in orientation, so the answer isn't there. But ViewModel.onCleared gets called when the Application is swiped away (as well as when the user backs out of the activity). If the resource you want to use is associated with more than one activity in the stack, you can add reference counts or some other mechanism to decide if ViewModel.onClear should release the resource.
This is yet another of many good reasons to use ViewModel pattern
I don't really know why the above approaches are not working on my case even I set android:stopWithTask="false" that onTaskRemoved() not called.
Another good approach would be using AndroidViewModel. This one even works on the case when user exits the applcation on pressing back button.
Just bound ViewModel class to your MainActivity then do your task onCleared() callback.
Example:
public class MainViewModel extends AndroidViewModel {
public MainViewModel(#NonNull Application application) {
super(application);
}
#Override
protected void onCleared() {
// Do your task here
Log.e("MainViewModel", "OnCleared mainViewModel");
super.onCleared();
}
}
then bound it to your MainActivity:
MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
~ Voila!
As Bob Cram mentioned in his answer, View Model's onCleared() method is the answer.
It works in both cases :
When the user removes the app by swiping the app from background.
When the user clear all the app using the clear list button.
Service's onTaskRemoved() will work when the user swipes the app from the background, but will not work when the apps are cleared using the kill all button.
But the viewModel's onCleared() method works in both cases. You can use if to stop any ongoing process or clearing any task in the remote server.
override fun onCleared() {
super.onCleared()
Log.d(TAG , "App Killed")
}
You need to save your data when on onPause() is called.
Look at this life cycle diagram:
Android Developer
You can see that an app can be killed after onPause() or onStop().
Handle your data there and recover it in onRestart() \ onCreate().
good luck!
This worked for me on android 6,7,8,9.
Make one service like this:
public class OnClearFromRecentService extends Service {
#Override public IBinder onBind(Intent intent) {
return null; }
#Override public int onStartCommand(Intent intent, int flags, int
startId) {
Log.d("ClearFromRecentService", "Service Started");
return START_NOT_STICKY; }
#Override public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed"); }
#Override public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//Code here
stopSelf(); } }
2) Register this service in manifest.xml:
<service android:name="com.example.OnClearFromRecentService"
android:stopWithTask="false" />
3) Then start this service on your splash activity
startService(new Intent(getBaseContext(),
OnClearFromRecentService.class));
I have a service that look gps coordinate. I want that when the user click on the home button the gps stop to work (battery saving).
The strategy i am using is this:
#Override
public void onPause() {
super.onPause();
doUnbindService();
}
#Override
public void onResume() {
super.onResume();
doBindService();
}
on each Activity that use the service. The problem is that each time i switch activity it stop and resume. Is there a strategy that let me have the service always up until my app is in foregroung?
I have an app with 3 activities that use a service to deal with the GPS. In each activity's onStart() I have code of the form
bindService(new Intent(..., ..., Context.BIND_AUTO_CREATE);
and in each onStop() I have
unbindService(..);
You should find that activity 2's onStart() will execute before activity 1's onStop(). Thus only when no activities are running will the service stop.
I'm trying to revamp my multi-activity app to use just once instance of a LocationListener which I intend to implement in a service. Prior to doing this I've been experimenting with a stub activity and a stub service to see what happens under error conditions.
I want to see what happens if I attempt to unbind from a service which has already been unbound and avoid any errors if this should happen. The activity has two buttons to bind/unbind. If I deliberately hit the unbind twice in succession I do get a runtime error.
What condition can I test for at the point marked '<<<<<' in the code below to skip calling unbind again?
My activity code is
public void myClickHandler(View target) {
switch (target.getId()) {
case R.id.bind:
Log.d("STAG", "Activity One pressed BIND button");
mServiceConnected = bindService(new Intent(
"com.nbt.servicetest.LOCATIONSERVICE"), mServconn,
Context.BIND_AUTO_CREATE);
break;
case R.id.unbind:
Log.d("STAG", "Activity One pressed UNBIND button");
try{
if (mServconn != null) // <<<< What to put here if already unbound?
unbindService(mServconn);}
catch(Exception e){
Log.d("STAG", "Exception " + e.getMessage());
}
break;
}
}
ServiceConnection mServconn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("STAG", "Activity One service connected");
mIbinder = service;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("STAG", "Activity One service disconnected");
}
};
The service is starting/stopping OK. I've put log lines in the service code with the same tag on all the pertinent lines. The output is :
STAG(2945): Activity One onCreate
STAG(2945): Activity One onStart
STAG(2945): Activity One onResume
STAG(2945): Activity One pressed BIND button
STAG(2945): Loc service ONCREATE
STAG(2945): Loc service ONBIND
STAG(2945): Activity One service connected
STAG(2945): Activity One pressed UNBIND button
STAG(2945): Loc service ONUNBIND
STAG(2945): Loc service ONDESTROY
STAG(2945): Activity One pressed UNBIND button
STAG(2945): Exception Service not registered: com.nbt.servicetest.ServiceTesterActivityOne$1#43b8b290
I note that the activity's onServiceDisconnected() never gets called, is this normal?
The simplest thing to do would be to introduce another variable, say, isServConnBound, and add checks on both bind and unbind actions. Of course, remember to update the variable after you call bindService and unbindService.
I agree with vhallac - just use boolean flags. What are your concerns with this approach? As for me there's nothing to be afraid of.
As to why "the activity's onServiceDisconnected() never gets called" - yes, this is normal. Look what API says on this callback:
Called when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed.
Your process has neither crashed nor been killed, so this is an expected behavior. Even more, since you have your service in the same process, you'll never get this called. This is important when you bind to a service that runs in another process (inter process communication).