Broadcast Receiver in IntentService (Service has leaked IntentReceiver) - android

I try to implement an IntentService with a BroadcastReceiver that reacts on the SCAN_RESULTS_AVAILABLE_ACTION.
The IntentService is supposed to compare Lists whenever onReceive is called. I always get the
"Service has leaked IntentReceiver"
error even though I unregister the BroadcastReceiver in onDestroy().
Here is the code:
public class MyClass extends IntentService {
private HashMap<String, List<String>>;
private WifiManager mWifiManager;
private WifiReceiver mWifiReceiver;
public MyClass() {
super("MyClass");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mWifiReceiver = new WifiReceiver();
registerReceiver(mWifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY,"ScanLock");
mWifiManager.setWifiEnabled(true);
return START_NOT_STICKY;
}
#Override
public void onDestroy(){
unregisterReceiver(mWifiReceiver);
mWifiManager.setWifiEnabled(false);
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
}
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Here I do my stuff with the scan results
//should be called every 5 seconds
}
}
Where is the problem in the code?
Why do I keep getting this error?
I still have to learn a lot about Android, but I think the IntentService is the right way to go since I do not expect any result from this class. It should just stop when I send a call stopService(). This IntentService is called by another IntentService! Is that a problem?
Thanks for helping.

I try to implement an IntentService with a BroadcastReceiver that reacts on the SCAN_RESULTS_AVAILABLE_ACTION.
This is largely pointless. Your receiver will be registered for a few seconds at most, hopefully.
I still have to learn a lot about Android, but I think the IntentService is the right way to go since I do not expect any result from this class.
That makes no sense whatsoever. You use an IntentService when you have a short bit of work that needs to be performed in a background thread. For example, if you use AlarmManager to check for new email messages every 15 minutes, or you have an activity kick off a large file download, you would use IntentService.
It should just stop when I send a call stopService().
You never call stopService() on an IntentService. The IntentService stops itself once onHandleIntent() returns. This is why your BroadcastReceiver will be removed within seconds -- your onHandleIntent() should only be running for seconds.
This IntentService is called by another IntentService!
This is unlikely to be a good design.

Try registering BroadcastReceiver in OnCreate() instead of OnStartCommand(),
That should fix your problem.

Related

Android Activity communication with IntentService

I need to have a two way communication between my activity and a running IntentService.
The scenario is like this: the app can schedule alarms which on run, start an IntentService which fetches some data from web and process it. There are three possible situations when IntentService finishes:
The app is in focus, which means that when the IntentService will finish, the app needs to refresh its views with the new data.
The app is closed and when opened after IntentService has finished the work, so the app will have access to processed data
The app is opened while the IntentService is running, in which case I need to have a way from the activity to ask the IntentService if its doing something in the background.
For 1. I have already implemented a BroadcastReceiver in my activity which gets registered with the LocalBroadcastManager. When IntentService finishes the work, sends a broadcast and the activity reacts. This works fine
For 2. There is nothing needed to be done
For 3. I don't know what to do. So far I've tried this:
In Activity:
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_SEND_TO_SERVICE));
In IntentService
private LocalBroadcastManager localBroadcastManager;
private BroadcastReceiver broadcastReceiverService = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BROADCAST_SEND_TO_SERVICE)) {
//does not reach this place
//Send back a broadcast to activity telling that it is working
}
}
};
#Override
protected void onHandleIntent(Intent intent) {
localBroadcastManager = LocalBroadcastManager.getInstance(context);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_SEND_TO_SERVICE);
localBroadcastManager.registerReceiver(broadcastReceiverService, intentFilter);
.... //do things
}
The problem with my implementation is that n the IntentService the BroadcastReceiver does not fire onReceive. Any suggestions or maybe a simpler way for the Activity to ask the IntentService what it is doing?
LE:
Trying to get atomicboolean.
In Service:
public static AtomicBoolean isRunning = new AtomicBoolean(false);
#Override
protected void onHandleIntent(Intent intent) {
isRunning.set(true);
// do work
// Thread.sleep(30000)
isRunning.set(false);
}
In Activity, restarting the app while service is running:
Log(MyIntentService.isRunning.get());
//this returns always false, even if the intent service is running
On AndroidManifest
<service
android:name=".services.MyIntentService"
android:exported="false" />

Receiver, that was created from IntentService, doesn't work

I have Geofence IntentService. On GEOFENCE_ENTER I create broadcast receiver. But it doesn't get actions.
If I created it in MainActivity - all is good. But I don't need to.
I do not understand what happens when I create the receiver from the service.
public class GeofenceTransitionsIntentService extends IntentService {
...
#Override
protected void onHandleIntent(Intent intent) {
...
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
//Create WiFi scan receiver
WiFiScanReceiver wifiScan = new WiFiScanReceiver();
registerReceiver(wifiScan, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
//Scan
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
Boolean scanStarted = wm.startScan();
}
}
Calling registerReceiver() in onHandleIntent() will not work well. Your receiver will go away microseconds later, after onHandleIntent() returns and the service stops itself.
Either use a manifest-registered receiver, or use a regular Service instead of an IntentService, managing your own background thread and arranging to stopSelf() the service when you are done with it.
IntentService will be destroyed once onHandleIntent() finished.
You should use Service

onDestroy() callback of Service

In my Android project, I have a normal Service:
public class MyService extends Service{
#Override
public int onStartCommand(...){...}
...
#Override
public void onDestroy() {
super.onDestroy();
Log.d("MyApp","MyService onDestroy() is called!");
}
}
In my BroadcastReceiver class, I stop MyService & do another task :
public static class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context, MyService.class));
doAnotherTask();
}
}
According to my log, onDestroy() of MyService is executed after doAnotherTask() is done.
How can I guarantee that onDestory() of MyService is executed before doAnotherTask() get called?
P.S.: I thought I could do something like:
boolean isStopped = context.stopService(new Intent(context, MyService.class));
if(isStopped){
doAnotherTask();
}
But it could be possible that there is no service has been started, which means stopService(...) does nothing. So, I can't rely on my above code.
call startActivityForResult() .... and after you got the onActivityResult,.... call your method doAnotherTask()
i think that will do the job
How about sending a special intent to your broadcast receiver from the onDestroy() function? When your receiver gets it, then call doAnotherTask(). (I am assuming that you can't simply call doAnotherTask() from onDestroy() directly.)
send a broadcast in the service's onDestroy function and in it's observer do your after things
The call to stopService() is asynchronous. You are basically telling Android that you want it to stop the Service. You have no control over when this actually occurs.
If you need to trigger something AFTER the Service is destroyed, then you send a broadcast Intent in MyService.onDestroy() and use that to trigger whatever you want to happen when the Service is destroyed.

How to force an IntentService to stop immediately with a cancel button from an Activity?

I have an IntentService that is started from an Activity and I would like to be able to stop the service immediately from the activity with a "cancel" button in the activity. As soon as that "cancel" button is pressed, I want the service to stop executing lines of code.
I've found a number of questions similar to this (i.e. here, here, here, here), but no good answers. Activity.stopService() and Service.stopSelf() execute the Service.onDestroy() method immediately but then let the code in onHandleIntent() finish all the way through before destroying the service.
Since there is apparently no guaranteed way to terminate the service's thread immediately, the only recommended solution I can find (here) is to have a boolean member variable in the service that can be switched in the onDestroy() method, and then have just about every line of the code in onHandleIntent() wrapped in its own "if" clause looking at that variable. That's an awful way to write code.
Does anybody know of a better way to do this in an IntentService?
Here is the trick, make use of a volatile static variable and check continue condition in some of lines in your service that service continue should be checked:
class MyService extends IntentService {
public static volatile boolean shouldContinue = true;
public MyService() {
super("My Service");
}
#Override
protected void onHandleIntent(Intent intent) {
doStuff();
}
private void doStuff() {
// do something
// check the condition
if (shouldContinue == false) {
stopSelf();
return;
}
// continue doing something
// check the condition
if (shouldContinue == false) {
stopSelf();
return;
}
// put those checks wherever you need
}
}
and in your activity do this to stop your service,
MyService.shouldContinue = false;
Stopping a thread or a process immediately is often a dirty thing. However, it should be fine if your service is stateless.
Declare the service as a separate process in the manifest:
<service
android:process=":service"
...
And when you want to stop its execution, just kill that process:
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
Iterator<RunningAppProcessInfo> iter = runningAppProcesses.iterator();
while(iter.hasNext()){
RunningAppProcessInfo next = iter.next();
String pricessName = getPackageName() + ":service";
if(next.processName.equals(pricessName)){
Process.killProcess(next.pid);
break;
}
}
I've used a BroadcastReceiver inside the service that simply puts a stop boolean to true. Example:
private boolean stop=false;
public class StopReceiver extends BroadcastReceiver {
public static final String ACTION_STOP = "stop";
#Override
public void onReceive(Context context, Intent intent) {
stop = true;
}
}
#Override
protected void onHandleIntent(Intent intent) {
IntentFilter filter = new IntentFilter(StopReceiver.ACTION_STOP);
filter.addCategory(Intent.CATEGORY_DEFAULT);
StopReceiver receiver = new StopReceiver();
registerReceiver(receiver, filter);
// Do stuff ....
//In the work you are doing
if(stop==true){
unregisterReceiver(receiver);
stopSelf();
}
}
Then, from the activity call:
//STOP SERVICE
Intent sIntent = new Intent();
sIntent.setAction(StopReceiver.ACTION_STOP);
sendBroadcast(sIntent);
To stop the service.
PD: I use a boolean because In my case I stop the service while in a loop but you can probably call unregisterReceiver and stopSelf in onReceive.
PD2: Don't forget to call unregisterReceiver if the service finishes it's work normally or you'll get a leaked IntentReceiver error.
#Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
if (action.equals(Action_CANCEL)) {
stopSelf();
} else if (action.equals(Action_START)) {
//handle
}
}
Hope it works.
In case of IntentService it does not stop or takes any other request through some intent action until its onHandleIntent method completes the previous request.
If we try to start IntentService again with some other action, onHandleIntent will be called only when previous intent / task is finished.
Also stopService(intent); or stopSelf(); does not work until the onHandleIntent() method finishes its task.
So I think here better solution is to use normal Service here.
I hope it will help!
If using an IntentService, then I think you are stuck doing something like you describe, where the onHandleIntent() code has to poll for its "stop" signal.
If your background task is potentially long-running, and if you need to be able to stop it, I think you are better off using a plain Service instead. At a high level, write your Service to:
Expose a "start" Intent to start an AsyncTask to perform your background work, saving off a reference to that newly-created AsyncTask.
Expose a "cancel" Intent to invoke AsyncTask.cancel(true), or have onDestroy() invoke AsyncTask.cancel(true).
The Activity can then either send the "cancel" Intent or just call stopService().
In exchange for the ability to cancel the background work, the Service takes on the following responsibilities:
The AsyncTask doInBackground() will have to gracefully handle InterruptedException and/or periodically check for Thread.interrupted(), and return "early".
The Service will have to ensure that stopSelf() is called (maybe in AsyncTask onPostExecute/onCancelled).
As #budius already mentioned in his comment, you should set a boolean on the Service when you click that button:
// your Activity.java
public boolean onClick() {
//...
mService.performTasks = false;
mService.stopSelf();
}
And in your Intent handling, before you do the important task of committing/sending the intent information, just use that boolean:
// your Service.java
public boolean performTasks = true;
protected void onHandleIntent(Intent intent) {
Bundle intentInfo = intent.getBundle();
if (this.performTasks) {
// Then handle the intent...
}
}
Otherwise, the Service will do it's task of processing that Intent. That's how it was meant to be used,
because I can't quite see how you could solve it otherwise if you look at the core code.
Here is some sample code to start/stop Service
To start,
Intent GPSService = new Intent(context, TrackGPS.class);
context.startService(GPSService);
To stop,
context.stopService(GPSService);
context.stopService(GPSService);

How do I cancel all pending intents that are qued for intent Service

I have an intentservice that gets qued by the user and by my app automatically. I need to be able to kill all pending intents that are qued when the user logs out of my application, but I cannot seem to get that to work. I have tried stopService() and stopself(), but the intents continue to fire off the intentservice after the user has logged out. I would try to get the id of the intent but that is difficult as everytime the intentservice starts, the variable holding the intent id's is empty. Here is my intentservice code:
public class MainUploadIntentService extends IntentService {
private final String TAG = "MAINUPLOADINTSER";
private GMLHandsetApplication app = null;
private SimpleDateFormat sdf = null;
public boolean recStops = true;
public MainUploadIntentService() {
super("Main Upload Intent Service");
GMLHandsetApplication.writeToLogs(TAG,
"GMLMainUploadIntentService Constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Started");
if (app == null) {
app = (GMLHandsetApplication) getApplication();
}
uploadData(app);
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Finished");
}
#Override
public void onDestroy() {
GMLHandsetApplication.writeToLogs(TAG, "onDestroy Started");
app = null;
stopSelf();
GMLHandsetApplication.writeToLogs(TAG, "onDestroy completed");
}
public void uploadData(GMLHandsetApplication appl) {
//All of my code that needs to be ran
}
Unfortunately, I don't think it's possible to accomplish that with the standard IntentService methods since it doesn't offer a way to interrupt it while it's already going.
There are a few options I can think of that you can try to see if they fit your need.
Copy the IntentService code to make your own modifications to it that would allow you to remove pending messages. Looks like someone had some success with that here: Android: intentservice, how abort or skip a task in the handleintent queue
Instead of copying all the IntentService code, you might also be able to Bind to it like a normal Service (since IntentService extends Service) so you can write your own function to remove pending messages. This one is also mentioned in that link.
Rewrite the IntentService as a regular Service instead. With this option, you'd have more control over adding and removing messages.
I had what sounds like a similar situation where I was using an IntentService, and I eventually just converted it to a Service instead. That let me run the tasks concurrently and also cancel them when I needed to clear them.
Here
When should I free the native (Android NDK) handles? is the HangAroundIntentService class that has the method cancelQueue().
The class also has the method
public static Intent markedAsCancelIntent(Intent intent)
that converts an intent into a cancel intent, and
public static boolean isCancelIntent(Intent intent).
The class is based on the open-sourced Google's code.
Just a thought but inside of your onhandleintent can you have an argument that checks to see if app is running if not then don't run the code? example. In the start of your app you could have a static var
boolean appRunning;
Next in your onhandle of the intent, when you set the appRunning to false, after an onPause or onDestroy of activity, you could wrap the onhandleintent code in a boolean:
protected void onHandleIntent(final Intent intent) {
if(MainActivity.appRunning){
...
}
}
Just a thought

Categories

Resources