"MainActivity has leaked ServiceConnection org.altbeacon.beacon.BeaconManager that was originally bound here" - this is the problem, I'm facing. I'm using AltBeacon from a class in a library. The class is getting passed a Context element at instantiation. The class implements BeaconConsumer and the overrides are acting like shown below:
#Override
public Context getApplicationContext() {
return mContext.getApplicationContext();
}
#Override
public boolean bindService(Intent intent, ServiceConnection connection, int mode) {
return mContext.bindService(intent, connection, mode);
}
#Override
public void unbindService(ServiceConnection connection) {
mContext.unbindService(connection);
}
#Override
public void onBeaconServiceConnect() {
// do things
}
The bindService works, but because there is no onDestroy in the library (and finalize() is never called either), unbindService will never be called. This leads to the leak mentioned above.
How can I recover from that? I don't want to expose a special cleanup() function, the library shall be able to detect the shutdown moment and should cleanup internally. Is that possible?
Any pointer welcome.
Regards
OK, added a cleanup function.
Solved
Related
I have a service in Android that encapsulates a framework that has a start method. The service boils down to something like this, many things omitted:
public class MyService extends Service {
private IBinder thisBinder;
public MyService(){
thisBinder = new LocalBinder();
}
#Override
public IBinder onBind(Intent intent) {
return thisBinder;
}
public void start(Map<String, Object> options)
{
getDriverManager().start(options);
}
}
I also have a bridging class that makes calls to the service:
public class MyServiceBridge implements ServiceConnection {
private boolean started = false;
private boolean bound = false;
private MyService myService;
public MyServiceBridge(Context context){
this.context = context;
}
public void bindService(){
Intent intent = new Intent(getContext(), MyService.class);
getContext().bindService(intent, this, getContext().BIND_AUTO_CREATE);
getContext().startService(intent);
}
// Here's a sample call, and the one that is relevant
public void start(Map<String, Object> options){
setOptions(options);
if(bound == true){
getMyService().start(options);
}
else{
started = true;
}
}
}
I call the bridge's start method in order to run the service. This works fine, except in this particular situation (so far). The MyApplication class calls the bridge's start method on onCreate:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
getServiceBridge().start(null);
}
}
This, according to the docs is "Called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created.". Indeed it appears to be so, because the service does not start, and instead starts when I close the app (odd, at least). This works if I move the call to an activity's onCreate method, but that's not ideal because I can also stop the service, and I want the service to run for the lifetime of the app. That is, the service should start when the app starts and stop when the app terminates. Does this make sense? Is there another way to do this?
In my opinion, you did a good job when you decided to run service in Application onCreate. but it is strange to hear that service is started when you close the app.
I have done this several times, and service starts in application onCreate which must be called.
Have you checked if your application is alive and run in background? Please make sure that you killed you application before testing. Use Task Killer or something like that, to be sure that application is always freshly started.
Sadly, Android does not have appropriate mechanism to notify you when application is exited, because it is still alive until system decides to kill it and free resources.
So, from what I understand, I should use intents to update the UI of an activity from a service.
But, I am a bit concerned about the efficiency of what I want to do.
Theoretically, it shouldn't really pose a serious problem, but I don't want to have a situation in which I wake up my phone, and have 20-30 onTick-s running at once, each time generating a new intent.
What I want (if possible) is for each onTick's intent to overwrite the previous one, so that the receiver only has process one at a time.
I know pending intents have FLAG_UPDATE_CURRENT, but doing things the right way means (if I understand correctly) using a local broadcast, which doesn't work with pending intents.
This turned out to be surprisingly simple.
A pskink suggested in his comment, all you need is a binder:
Create a class that extends the Binder class. Keep in mind that the Binder class implements IBinder, which is why you can pass an instance of your new class as an instance of IBinder (which is something you will be doing later on)
In this class, implement various functions you want your main activity to be able to use. If you want your service to be able to run things from the activity as well, you can pass an instance of an inner class of the activity, which has the relevant functions in it, or even pass a pointer to the activity (this).
Keep in mind: Do these things at your own risk. If, for example, your activity is destroyed - for example, due a change of orientation, the pointers might become worthless and errors will be thrown
I recommend implementing an invalidate inside your new class to help you handle these situations, but perhaps there are better practices I am not personally familiar with.
Now, you have a two-way communication channel with your activity.
Below is some sample code. Note that if you don't want your service to be destroyed when the activity is, you need (as far as I understand it) to also start it using startService (in addition to using bindService)
public class MyService extends Service {
private IBinder yourBinder; //A class that will connect your service and your activity
#Override
public void onCreate() {
super.onCreate();
yourBinder = new YourBinderClass();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return yourBinder;
}
Your binder class:
public class YourBinderClass extends Binder {
private Service thisService;
public YourBinderClass(Service myService)
{
thisService = myService;
}
public Service getService()
{
return thisService;
}
And finally, inside your main activity:
private YourBinderClass yourBinderClass;
private Service yourService;
private ServiceConnection sc = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
yourBinderClass = (YourBinderClass) service;
yourService = yourBinderClass.getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
And to launch the service:
getBaseContext().bindService(new Intent(getBaseContext(),MyService.class), sc, 0)
Note that the service won't be ready immediately, so if your activity is dependent on it, you might want to signal that you are ready to do stuff in onServiceConnected
I know there have been others that have asked similar questions, but I haven't been able to get an answer on how to get past a false when calling bindService.
Basically I've written an app which is connected to 2 services. They're both in my manifest:
<service android:name="com.test.LService"></service>
<service android:name="com.test.CService"></service>
The CService works as expected and always has. I added the LService yesterday and it won't start at all. Note that originally I had my LocationListener imeplement in MainActivity, but I wanted to collect GPS coords independent of the Activity being in focus. To save space, I'll just paste in LService related code since it's the service I'm having trouble with. In my MainActivity:
#Override
protected void onStart() {
super.onStart();
// bindService for LService always returns FALSE?!?!
Log.d(TAG, "LService: " + this.context.bindService(new Intent(this, LService.class), mLConnection, Context.BIND_AUTO_CREATE));
Log.d(TAG, "CService: " + this.context.bindService(new Intent(this, CService.class), mCConnection, Context.BIND_AUTO_CREATE));
}
private ServiceConnection mLocConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocBinder binder = (LocBinder) service;
mLocService = binder.getService();
mLocBound = true;
}
#Override
public void onServiceDisconnected(ComponentName className) {
mLocBound = false;
}
};
In the LService class, all the binding stuff you'd expect:
private final IBinder mBinder = new LBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LBinder extends Binder {
public LService getService() {
return LService.this;
}
}
The only thing that could be an issue is that my LService implements LocationListener:
public class LocService extends Service implements LocationListener {
...
CService doesn't implement anything, it just extends Service like it should. Do you think removing LocationListener as an implement, and re-implementing it in a sub-class would do the trick? Could it be some Android reflection seeing the implement and determining that it's not suitable as a Service? Why no exception though? Any ideas? Thanks!
UPDATE: Well it's not because of the LocationListener implementation. I removed the implement from LService and it's still returning FALSE. Sigh...
UPDATE2: Trying to debug the situation is rough, but the CService drops into ClassLoader while the LService doesn't at all. Is it possible there's an incorrect some class identifier somewhere?!? Everything looks ok to me.
UPDATE3: I gave up and just merged the LocationListener into my CService service... I'll close this question when I can.
I can't close this question so I'll just answer with what I ended up doing.
I gave up and just merged the LocationListener into my CService service and gave up on trying to figure out why my 2nd service wouldn't work. Still interested in opinions though!
According to RoboSpice documentation https://github.com/octo-online/robospice/wiki/Design-of-RoboSpice , i can use it in any Context.
Can't find an example of using Robospice in service context.
I did some attempts but nothing happened, requests just not executes, no exceptions (Maybe some log leaking, what i need to do to enable robospice log on device?)
Where to start/stop it? (spiceManager.start(this) / spiceManager.shouldStop())
Where to create SpiceManager instance? (My service starts in application.onCreate() method, maybe i have to wait for some SpiceService initialization?)
some code
public abstract class SpicyService extends Service {
private SpiceManager spiceManager = new SpiceManager(SpiceService.class);
#Override
public void onCreate() {
super.onCreate();
spiceManager.start(this);
}
#Override
public void onDestroy() {
spiceManager.shouldStop();
super.onDestroy();
}
}
Shame on me ...
After drinking some coffee i spotted that child Service, dont have super.onCreate() call to start spiceManager.
It's working perfectly fine now!
Sorry for your time.
This is my second post today as fixing the 1st post lead to another problem which I am really struggling on. I want to use Broadcasts and intent to send data packets back from a service to the UI. I managed to bind and start the service successfully
see my other post if you want history and code
The null pointer exception comes on the sendBroadcast() inside the service. The service class does have its constructor re-called after binding the UI to the service. This happens from another class so Context can not be easily used. So I guess the sendBroadcast has no-where to go :(
I suspect this is my problem...the re-calling of the Service constructor after the initial binding. I have onDestroy, onPause and onResume covered with binding an unbinding.
Any ideas or suggestion would be great or maybe I am just going about this wrong is there another way?
EDIT
The previous problem of binding is now solved and due to me being new to the forums a slight delay in accepting the answer...sorry.
The class diagram is like this (it is ported C#code)
Activity doBind (on Curve.class)---> Activity starts a worker class (not treated as a service) Comm.class with a Handler for some comms--> the Comm starts another worker class --> previous worker class finally calls new Curve.class.
It is this last stage Curve.class where the sendBroadcastReceiver() then throws a nullpointer ref becasue the binder is lost.
I tested the broadcastreceiver just with a simple timer cutting out the worker classes in between and it works fine. Problems start when the Curve.class is recalled later further down the hierarchy and the binder gets nulled or "lost".
I removed all references of the binder from Curve except in onBind(). This might not be a good idea. Again the code below does work if used with a simple timer started directly from the UI (no other worker classes).
Some more code here:
The service
public class Curve extends Service
{
private NewCurvePointEventArgs newpoint = null;
private static final String TAG = "Curve";
Intent intent;
int counter = 0;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
IBinder mBinder = new LocalBinder<Curve>(this);
return mBinder;
}
#Override
public void onStart(Intent intent, int startId) {
}
#Override
public void onDestroy() {
}
public Curve(){
}
private void refreshintent(NewCurvePointEventArgs tmp)
{
ArrayList<String> thepoint = new ArrayList<String>();
thepoint.add()//various things added here
Bundle bundle = new Bundle();
// add data to bundle
bundle.putStringArrayList("New_Point", thepoint);
intent = new Intent();
intent.setAction(NEWCURVE_POINT);
intent.putExtra("NEW_POINT", bundle
sendBroadcast(intent);
}
The activity has this code now. the doBind() is called after the onCreate of the activity.
private BroadcastReceiver CurveReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Curve.NEWCURVE_POINT)) {
displaynewpoint(intent);
}
}
};
private ServiceConnection CurveServiceConncetion = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
CurveService = ((LocalBinder<Curve>) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
CurveService = null;
}
};
#Override
public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +");
}
#Override
public synchronized void onPause() {
super.onPause();
if(D) Log.e(TAG, "- ON PAUSE -");
}
#Override
public void onStop() {
super.onStop();
if(D) Log.e(TAG, "-- ON STOP --");
}
#Override
public void onDestroy() {
super.onDestroy();
if(D) Log.e(TAG, "--- ON DESTROY ---");
unregisterReceiver(CurveReceiver);
unbindService(CurveServiceConncetion);
}
public void doBind(){
Boolean tmp;
tmp = bindService(new Intent(this, Curve.class), CurveServiceConncetion, Context.BIND_AUTO_CREATE);//Context.BIND_AUTO_CREATE
IntentFilter filter = new IntentFilter(Curve.NEWCURVE_POINT);
registerReceiver(CurveReceiver, filter);
}
This problem to me is because the Curve.class has its constructor called again after the initial doBind().
Surely there must be a way around this otherwise I have to load my worker classes closer in hierarchy to the UI with the code from Curve.class???
EDIT
Curve is an object that processes data, constants etc sent from an external machine and contains the processed data in arrays.
The logCat did of course exist I just wasn't looking in the right place here it is
ARN/System.err(10505): java.lang.NullPointerException
WARN/System.err(10505): at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:271)
WARN/System.err(10505): at pi.droid.Core.Curve.refreshintent(Curve.java:206)
WARN/System.err(10505): at pi.droid.Core.Curve.AddPoint(Curve.java:400)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.AddPoint(CommMeasure.java:363)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$GenericCommMeasurement.TryProcessData(CommMeasure.java:168)
WARN/System.err(10505): at pi.droid.Core.Comm.CommMeasure$CommMeasurement.ProcessData(CommMeasure.java:302)
WARN/System.err(10505):at pi.droid.Core.Comm.ClientConnection$timer_tick.run(ClientConnection.java:164)
WARN/System.err(10505): at java.util.Timer$TimerImpl.run(Timer.java:289)
You can also see the chain of the 2 other worker classes I use. The constructor of Curve is called after the bind from CommMeasure.
So this is my problem.
Do I need to totally change how my program is set up or is there another way around this?
FINAL EDIT
This code is brought from c# and Curve used eventhandlers to pass data around. I got rid of all them(java listeners) and used android Handler and broadcastreceiver.
It has been suggested that I should pass the CurveService around but this will be problematic as Curve has multiple constructors. The no parameter 1 for the service and then 1 like this
public Curve(Unit XUnit, Unit YUnit)
{ this.Title = "Curve";
this.finished = false;
this.XUnit = XUnit;
this.YUnit = YUnit;
this.YDirection = CurveDirection.Unspecified;
}
so surely instantiating that would be a problem with CurveService, which has to be like this: public Curve(){} ??
Anyway many thanks for all your help and advice.
Final Edit +1..lol
The UI creates a new instance of ClientConnection, that in turns creates a new instance of CommMeasure and finally CommMeasure creates a new instance of Curve to access Curve.addpoint.
I think this thread and the other linked 1 goes beyond a simple android problem and highlights the difficulties of code porting. Any .Net developer for example reading this will learn some peculiarities of android a lot quicker than I did. There is also good working code in there.
Thanks to all who helped especially Justin Breitfeller
The best thing for you to do is follow the example from the Android APIDemos.
A service to be used like you want to use it:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html
Look at the Binding class inside of this file to see a class that does binding like you should:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html
Finally, if your constructor is being called twice on your service, you aren't binding to your service properly, or perhaps you are unbinding from it and binding to it again unexpectedly.
EDIT
From the stack trace, it appears that CommMeasure needs to have a reference to the instance of Curve that you receive in onServiceConnected.
EDIT 2
If you really want to make your life simple, pass getApplicationContext() to your CommMeasure class and just appContext.sendBroadcast() from that class to send out your point. This will prevent you from requiring a reference to the service in your long-running task.