Stop a service after MainActivity is closed (EDITED) - android

I think im not clear at all, i do want the service to persist even if the main activity is destroyed via user action or android system does it, it does it well, but when the app is reopened at certain point i will want to check if a bg activity exists and stop it using a action button, THX in advance.
I launch a background service, in my MainActivity I can stop it and rerun it, the service persists when the app is closed from the running apps list, the problem is when I relaunch the closed app an try to stop the service with a button I have the app crashes cause it obviously tries to stop a service from which it no longer has a reference.
private void startBg(){
if (!hasPermissions() || mScanning) {
return;
}
clearLogs();
BgServiceIntent = new Intent(MainActivity.this, BgScanService.class);
startService(BgServiceIntent);
}
private void stopBg(){
stopService(BgServiceIntent);
}
Calling stopBg() after reopening the app fails, because BgServiceIntent no longer points to this service and thus I get this:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: mobile.link.imbera.apsys.imberalink, PID: 20104
java.lang.NullPointerException
at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1568)
at android.app.ContextImpl.stopServiceCommon(ContextImpl.java:1628)
at android.app.ContextImpl.stopService(ContextImpl.java:1589)
at android.content.ContextWrapper.stopService(ContextWrapper.java:499)
at mobile.link.imbera.apsys.imberalink.MainActivity.stopBg(MainActivity.java:180)
at mobile.link.imbera.apsys.imberalink.MainActivity.lambda$onCreate$1$MainActivity(MainActivity.java:124)
at mobile.link.imbera.apsys.imberalink.MainActivity$$Lambda$1.onClick(Unknown Source)
at android.view.View.performClick(View.java:4463)
at android.view.View$PerformClick.run(View.java:18770)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5333)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
at dalvik.system.NativeStart.main(Native Method)

If the service already running in background then you need to call stopSelf() on same instance of that service .
Now as per service life Cycle onCreate() only call once in the lifetime of service .
WhereAs onStartCommand get called each time you call startService() with new intent. So what you can do is to pass a flag in intent to stop it .
Intent BgServiceIntent = new Intent(MainActivity.this, BgScanService.class);
BgServiceIntent.putExtra("close",true);
startService(BgServiceIntent);
And in Service .
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean shouldClose=intent.getBooleanExtra("close",false);
if(shouldClose){
stopSelf();
} else {
// Continue to action here
}
return START_STICKY;
}

Maybe all you need to do is check to see if it's null before attempting to stop it:
private void stopBg(){
if(BgServiceIntent != null)
stopService(BgServiceIntent);
}

First of all start a service in START_NOT_STICKY.
Override this method in your Service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
and add this in your MainActivity.class
#override
private onDestroy() {
stopService(YourActiveService);
finishAffinity();
super.onDestroy()
}

Related

Intent is Null all the time in service

I have two Application A and B.In app B I have a service that I can run it from app A. I want to send data to app B with intent but always my intent is null!
I run app B's service from app A with this Code:
try {
String packageName = "app_B_package";
String appService = packageName + ".activity.InternetService";
Intent start = new Intent();
start.setComponent(new ComponentName(packageName, appService));
start.putExtra("LAUNCHER_COMMAND_CLOSE" , true);
G.context.startService(start);
} catch (Exception e) {
e.printStackTrace();
}
But when service of app B will run the intent is null. This is onStart of the service in app B:
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.i("LOGO_OFFICE_IN", "onStart");
if (intent != null) {
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
}
}
Why my intent is null all the time? I can't find it out.
Thank you for your help.
It looks like your service is type fire-and-forget - it does one quick thing and should quit immediately because it's done. Correct?
1. Don't leave your idle service running
Documentation says
If a component starts the service by calling startService() (which results in a call to onStartCommand()), the service continues to run until it stops itself with stopSelf() or another component stops it by calling stopService().
so after your workload is done call stopSelf().
When your service is not running there's nothing to restart.
2. Use correct start mode
Unless you stop it, your service is by default automatically restarted after it's killed by system (because system needed resources). The default mode is called START_STICKY and does this:
This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.
Since your service is a quick one-time job, it makes no sense for it do be restarted later at an arbitrary time.
To let Android know, you should return START_NOT_STICKY from onStartCommand.
3. Use current API
Don't use onStart, it was deprecated 9 years ago. It doesn't support start modes mentioned above. Implement onStartCommand instead. Your service would look like this:
#Override
public void onStartCommand(Intent intent, int flags, int startId) {
// No super call.
Log.i("LOGO_OFFICE_IN", "onStart");
// Intent cannot be null.
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
stopSelf(); // Work is done, stop service.
return START_NOT_STICKY; // Don't restart if killed.
}
Now that I think of it, only step 1 is absolutely necessary. Anyway, get into habit of using current APIs and finding out how things work.

How to stop a service only when the entire app is paused and closed and not use a base activity

I am looking for a solution to kill a service only when my app itself is paused or closed. Please note, I am not referring to one activity, I am looking for when any activity within the app is in the foreground while it is paused or killed. I have a lot of activities, so I don't want to extend them all from a BaseActivity. Is there any other way to do this ?
Set an uncaught exception handler for your main thread in onCreate of your Application
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
// Kill your service here
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}
});
START_STICKY
the system will try to re-create your service after our App/service is
killed
START_NOT_STICKY
the system will not try to re-create your service after our App/service is killed
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want to stop this service, when our Application is
// not running (foreground/background), so return non-sticky.
return START_NOT_STICKY;
}
Hope this would help you!

Application starts new service when app is closed

I have an android app with service that has to track user's location even with closed application only if user set flag in app. So I start service in code after user clicks button and stop when he clicks it again with following code
alarm.changeAlarmStatus();
if(alarm.getAlarmStatus()) {
SharedPreferences.Editor ed = alarmPreferences.edit();
ed.putFloat("MARKER_LATITUDE", (float) theMarker.getPosition().latitude);
ed.putFloat("MARKER_LONGITUDE", (float) theMarker.getPosition().longitude);
ed.commit();
startService(myIntent);
}
else{
stopService(myIntent);
}
It seems to work fine. Service works and does what it should. But problem is if I close application through the task manager I see in logcat that Service starts again(but only if service is already working) and it causes nullPointerException because intent is null. You can see in this code why it happens
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Bundle bundle = intent.getExtras();
alarm = (Alarm) bundle.getSerializable("alarm");
Log.d("service","On Start Command is called ");
serviceIntent = intent;
return START_STICKY;
}
However I need to start my service only by pressing button and no other way. Does anyone know how to fix that?
Return START_NOT_STICKY in onStartCommand instead of START_STICKY.
If START_STICKY is returned, system will try to re-create service after it is killed.
If START_NOT_STICKY is returned, system will not try to re-create service after it is killed.

NullPointerException while retrieving Extras from Intent

I'm working on building a Location_Service for my Android App.I have a Service say , T_Service that starts on phone boot and checks for few conditions in its onStartCommand() to start my Location_Service. I want to send these conditions as an intent from a broadcast receiver associated to with the T_Service.
I do the following:
In the Broadcast Receiver
public class T_BroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context,T_Service.class);
i.putExtra("PARENT_ACTIVITY_NAME","com.example.helloworld");
context.startService(i);
}
}
In the T_Service class:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("APP","The intent has parent activity : "+intent.getExtras().getString("PARENT_ACTIVITY_NAME"));
}
I get a NullPointerException when my code reaches the above point that tries to print the value associated with the key that was set in the Intent.
Kindly advise me on how to go about the case of adding extras to an intent of a Service like T_Service that starts on phone boot.
The Stack trace for the Exception :
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start service org.abc.def.services.TService#41593b58 with null: java.lang.NullPointerException
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2387)
at android.app.ActivityThread.access$1900(ActivityThread.java:127)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1221)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4511)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at org.abc.def.services.TrackerService.onStartCommand(TrackerService.java:140)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2370)
            at android.app.ActivityThread.access$1900(ActivityThread.java:127)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1221)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4511)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
            at dalvik.system.NativeStart.main(Native Method)
From the documentation (onStartCommand):
Parameter "intent": The Intent supplied to startService(Intent), as given.
This may be null if the service is being restarted after its process has gone
away, and it had previously returned anything except
START_STICKY_COMPATIBILITY.
If you're running this on phone boot is likely that you will get null on the intent because the process that started it has gone away.
To receive the the same intent you would need to return START_REDELIVER_INTENT in the onStartCommand method, as follows:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("APP","The intent has parent activity : "+intent.getExtras().getString("PARENT_ACTIVITY_NAME"));
return Service.START_REDELIVER_INTENT;
}
Returning START_REDELIVER_INTENT will make the intent parameter to be passed again to the onStartCommand method whenever the service get restarted (e.g on phone boot).
For more details check the documentation.
START_REDELIVER_INTENT
Constant to return from onStartCommand(Intent, int, int): if this
service's process is killed while it is started (after returning from
onStartCommand(Intent, int, int)), then it will be scheduled for a
restart and the last delivered Intent re-delivered to it again via
onStartCommand(Intent, int, int)
Please read the javadoc for onStartService. It says that the intent parameter can be null, so you should always check it for null before calling methods on it.
My experience is that even when you think that an IntentSevice will never receive a null there, it is actually still possible in extremely rare circumstances. So you should still check it for null even if you don't think it will happen.
replace this
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("APP","The intent has parent activity : "+intent.getExtras().getString("PARENT_ACTIVITY_NAME"));
}
with this
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Bundle b = getIntent().getExtras();
if (b != null){
Log.d("APP","The intent has parent activity :"+b.getString("PARENT_ACTIVITY_NAME")); }
Per the Intent API documentation, you must include a package name in the extra's name.
public Intent putExtra (String name, String value)
Added in API level 1
Add extended data to the intent. The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll".
Parameters
name The name of the extra data, with package prefix.
value The String data value.
Returns
Returns the same Intent object, for chaining multiple calls into a single statement.
See Also
putExtras(Intent)
removeExtra(String)
getStringExtra(String)
Please check your intent and extra string before accessing. May be it is null
if(intent.getExtras()!=null && intent.hasExtra("PARENT_ACTIVITY_NAME")){
Log.d("APP","The intent has parent activity : "+intent.getExtras().getString("PARENT_ACTIVITY_NAME"));
}
To pass an intent, please refer older thread.
Pass data from Activity to Service using an Intent

onDestroy() not getting called while the activity is getting killed by the user?

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.

Categories

Resources