My app uses a Service for background tasks. I want the Service to keep running when user kills the app(swipes it away). There are two scenario's in which the user kan kill the app:
Scenario 1: When in app:
1 User presses **backbutton**, apps goes to background and user is on the homescreen.
2 User presses the multitasking button and swips the app away.
3 The Service should restart because its sticky.
Scenario 2: When in app:
1 User presses **homebutton**, apps goes to background and user is on the homescreen.
2 User presses the multitasking button and swips the app away.
3 The Service should restart because its sticky.
Scenario 1 works exactly as expected. The Service restarts after the app is swiped away in a matter of seconds.
Scenario 2 does not work as expected. The service does get restarted but after more than 5 minutes.
I don't understand why it takes so long to restart the service in scenario 2. Is there a known solution to this?
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent test = new Intent(this, TestService.class);
startService(test);
}
}
public class TestService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("Service", "Service is restarted!");//In Scenario 2, it takes more than 5 minutes to print this.
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
When you press the back button, you are essentially destroying the activity which means the onDestroy() is called where you stop() the STICKY_SERVICE. Hence the STICKY_SERVICE boots up again immediately.
When you press the home button, you are pausing the activity (onPause()), essentially putting it in background. The activity is only destroyed when the OS decides to GC it. It is only at that point of time that the onDestroy() is called at which you stop() the service and STICKY_SERVICE boots up again.
Hope this helps.
Related
I have an always-running background service, started on my MainActivity. The problem is, when MainActivity is destroyed, my service gets restarted. This scenario is the only one in which the service stops, so the "always-running" thing is working well.
I would like to know why this is happening, and what I could do to prevent it.
My service is not bound to my activity, because I don't need it.
The service:
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
...
public LocationService() {
}
#Override
public void onCreate() {
// Some initialization logic here
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
...
}
MainActivity:
public class MainActivity extends BaseActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Start location service in background
Intent intent = new Intent(this, LocationService.class);
startService(intent);
}
}
Thanks in advance! :D
I'm guessing because I don't have enough data. When your MainActivity is destroyed, the OS process hosting your application has no active activities in it, just your Service. Android recognizes that the process isn't hosting any live activities, so it kills the process to free resources. Since your Service returned START_STICKY, Android then schedules your Service for a restart. The Service gets reinstantiated in a new OS process.
You could run your Service in a separate OS process to test this theory. Just add
android:process=":remote"
to the manifest entry for the <service>. This may have other implications on your application, so be sure you understand that.
In any case, Android will randomly kill OS processes that host only services, even if they returned START_STICKY. There isn't any way to prevent that, especially on low-end devices with limited resources. It is Android's way of cleaning house ;-)
Here is the use case:
1. User logs into the app and presses the hardware home button and app is sent to background
2. I run a handler in the background to check if the inactivity time out is 5 mins. Then I need to call the logout API and start the loginactivity, Without launching or bringing the app to foreground
Here is what I have tried
if (!mIsAppInForeground) {
Log.d("App in background", "App in background and timing out");
activity.startService(new Intent(activity,LogOutBackGroundService.class).addFlags( Intent.FLAG_ACTIVITY_MULTIPLE_TASK ));
}
public class LogOutBackGroundService extends Service {
public static final String HAS_SIGNED_OUT = "hasSignedOut";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
intent.putExtra(HAS_SIGNED_OUT, true);
startActivity(new Intent(this, LoginActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
The app is timing out properly and login activity is being launched, but the app is being brought to foreground (i.e app is being launched). I want this to happen in the background only. Only when the user relaunches the app, he should see the login screen again
startactivity(intent) pops up the backstack activity or creates a new one if it doesn't exist.. so your solution is there is onResume() and onPause().. onPause() is called when you the activity is gone, onResume() is called when you see the activity, so my advice to you is create a boolean, it can be in a singleton class
public class MySingletonClass {
public static boolean startloginpage; // by default its false
}
then you in mainactivity or the activity that the user will launch or come back to, put the code in its onresume
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
if(MySingletonClass.startloginpage){ //check if your boolean is true
// if it checks out then call do what you want to do when the user times run out
}else{
// if it doesn't check out continue without telling user to login,
}
}
in your service remove the intent code and put this
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
intent.putExtra(HAS_SIGNED_OUT, true);
MySingletonClass.startloginpage = true;
return START_NOT_STICKY;
}
In a typical logout scenario , clicking on logout button should just start the login activity clearing the login details. In your case after 5 mins you should try to clear the stored token for login ( assuming you store the login details for the user to login automatically when required). When the user launches the app next time your LAUNCHER activity would check for the stored token to launch the required activity.
I am starting and stopping a service from an activity calling startSertice()/stopService() (when user select/deselect a check box and service is not bounded). Every thing is working fine even though the activity that starts the service is closed. In "Running apps" I'm able to see 1 processes, 1 service running. But when I kill the application, using Task manager kind of application, the process is getting killed and service is not working though the running apps showing 0 processes, 1 service. How to make the service working in such situations? I observed the same in some other security applications like Avast with 0 processes, 1 service, while service working properly. Please help me out on this.
Following is the activity on click method
#Override
public void onClick(View arg0) {
boolean value = checkBox.isChecked();
if(value){
// start the service
startService(new Intent(this, MyService.class));
Toast.makeText(this, "Background service started", Toast.LENGTH_SHORT).show();
} else {
stopService(new Intent(this, MyService.class));
Toast.makeText(this, "Background service stopped", Toast.LENGTH_SHORT).show();
}
}
Following is the service class:
public class MyService extends Service{
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
super.onCreate();
Log.d("######Service","Service created successfully");
}
#Override
public int onStartCommand(Intent intent, int flags, int stardId){
Log.d("######Service","Service started successfully");
IntentFilter powerButtonIntentFilter = new IntentFilter();
powerButtonIntentFilter.addAction("android.intent.action.SCREEN_ON");
this.registerReceiver(pbReceiver, powerButtonIntentFilter);
Log.d("#######","Power button register registered");
return START_STICKY;
}
#Override
public void onDestroy(){
Log.d("######Service","Service destroyed successfully");
this.unregisterReceiver(pbReceiver);
Log.d("#######","Power button register un-registered");
super.onDestroy();
}
}
Everything is working fine in ideal case. SCREEN ON action is being listened by the broadcast receiver properly even when the activity that starts the service is closed. I am able to see the app running in settings. But when I force kill the process using Task Manager kind of applications, processes is getting killed and in running apps I am able to see 0 process, 1 service running. Though the service is running after force killing the app from Task manager, broadcast receiver is not listening to the SCREEN ON action. Please help me out on this.
Thanks, JK
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));