Memory leak - Service + thread - android

I would like to know if I am doing the right thing. I am clearly getting memory leaks, but I can not pin down where - I have submitted a simplified version of where I think the problem lies . . . is there a potential for leak in the following code?
public class MainActivity extends Activity {
filterService mServer;
private void startService() {
Intent mIntent = new Intent(getApplicationContext(), filterService.class);
startService(mIntent);
bindService(mIntent, mConnection, BIND_AUTO_CREATE);
}
private void stopService() {
stopService(new Intent(getApplicationContext(), filterService.class));
unbindService(mConnection);
mConnection = null;
}
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
mServer = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
LocalBinder mLocalBinder = (LocalBinder)service;
mServer = mLocalBinder.getServerInstance();
}
};
public void onDestroy() {
super.onDestroy();
stopService();
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService();
}
}
Any comments would be most valuable - thank you.

Better unbind from your service in 'onStop()', because 'onDestroy()' may not be called.
If you use 'startService()' to make your service do whatever it is supposed to do and return with 'START_STICKY' from the 'onStartCommand()' method of the service, then it will not be destroyed.
See the documentation about bound services (The Basics):
When the last client unbinds from the service, the system destroys the service (unless the service was also started by startService()).
This way, you can keep your service alive even though the activity is stopped/ destroyed. As soon as it is finished, it can call 'stopSelf()'.
Another source for memory leaks could be a Handler used for communication with the bound service, but I can't judge that from your code.

Related

How to test the process that a client of an Android bound service bind to the service?

I have an service App that defined a bound service, and another client App that one of its activity binds to the bound service. How can I write test case to test the bind service process?
The code of the client App binding to the service is similar to what the Android official doc has:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent();
intent.setComponent(new ComponentName(SERVICE_APP_PACKAGE_NAME,
SERVICE_NAME));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(connection);
mBound = false;
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
What kind of test case can test the setIntent() & bindService() or unbindService() method in the activity's onStart() and onStop() method?
You don't want to test onBind. You know that works, that's tested as part of the Google framework. What you want to test is two things:
1)That your ServiceConnection functions properly set mBound and mService.
2)That your onStart calls onBind to bind it.
The best way to do this is actually a refactor. This code isn't as testable as it could be. Bring mService and mBound into the ServiceConnection class, and make it a full class (rather than an anonymous class). Then you can easily test (1) using mocks for the input. To test (2) I would actually subclass the Activity, override bindService to just set a variable to true, and ensure after calling onStart the variable was set to true.

Activity with long-lasting service in the background that will not be killed

On Android, I have an Activity called FirstActivity which starts a Service named MyService to do networking stuff in the background. The Activity and the Service communicate with each other all the time by calling methods.
Now when the user navigates from FirstActivity to SecondActivity, the background service should not be killed or re-created, but kept alive and passed to SecondActivity which will now be the one communicating with the service.
In other words, the Service shall be running as long as one of the two Activitys is running, and it should not stop while the user navigates between the two Activitys.
One of the Activitys will always be in the foreground and during this time, the service should (optimally) never get killed. I think this should not be a problem because one of those two Activitys is always active and thus Android knows the service is important and not something that must be killed.
(If there was no way to prevent Android from killing and re-creating the service from time to time, I would need a way to restore the full state of the service gracefully.)
To sum up, the Service should have the same lifespan as the two Activitys "combined". It should start with the first of them and stop not before both of them have been destroyed.
So is the following code correct for that setup and goals?
public class MyService extends Service {
public class LocalBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
...
}
public class FirstActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(FirstActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
...
startService(new Intent(FirstActivity.this, MyService.class));
}
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
if (mMyService != null) {
mMyService.setCallback(null);
}
if (!isUserMovingToSecondActivity) {
stopService(new Intent(FirstActivity.this, MyService.class));
}
}
#Override
public void onBackPressed() {
stopService(new Intent(FirstActivity.this, MyService.class));
super.onBackPressed();
}
...
}
public class SecondActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(SecondActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
}
#Override
protected void onDestroy() {
...
stopService(new Intent(SecondActivity.this, MyService.class));
}
...
}
Is this the best way to guarantee a long-lasting service in the background of the Activitys that will not be killed or re-created?
What about Context.BIND_AUTO_CREATE? Is it correct to have this flag set here? What about Context.BIND_ADJUST_WITH_ACTIVITY and Context.BIND_WAIVE_PRIORITY -- do I need these?
(Many thanks to #corsair992 for his useful pointers!)
If the activities are always called in that order (i.e. FirstActivity starts SecondActivity, and never the other way around, then you should, basically, attempt to "tie" the Service's life-cycle to FirstActivity's lifecycle.
In general (see caveats later), this means:
Call startService() in FirstActivity.onCreate().
Call stopService() in FirstActivity.onDestroy().
Call bindService()/unbindService() in the onStart()/onStop() methods of both Activities (to get access to the Binder object, and be able to call methods on it).
A service started this way will be alive until stopService() is called and every client unbinds, see Managing the Lifecycle of a Service:
These two paths are not entirely separate. That is, you can bind to a
service that was already started with startService(). (...) In cases like this, stopService() or
stopSelf() does not actually stop the service until all clients
unbind.
and:
When the last client unbinds from the service, the system destroys the
service (unless the service was also started by startService()).
With this basic strategy, the Service will live as long as FirstActivity is around (i.e. it is not destroyed). However, an important point still remains: in the event of a configuration change (e.g. a screen rotation) that is not handled explicitly will cause the activity to restart itself, and the service will be destroyed (since we're calling stopService() in onDestroy()).
To prevent this, you can check isChangingConfigurations() before actually stopping the service (since an onDestroy() callback occurring due to this reason means that although this particular instance of the Activity is being destroyed, it will be recreated afterwards.
Hence, the full solution would be something like:
public class FirstActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
}
private ServiceConnection mServiceConnection = new ServiceConnection() { ... }
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
unbindService(mServiceConnection);
super.onStop();
}
#Override
protected void onDestroy() {
if (!isChangingConfigurations())
stopService(new Intent(this, MyService.class));
super.onDestroy();
}
While SecondActivity would only implement the onStart()/onStop() methods (in the same way).
A couple of notes about your particular implementation:
It's not necessary to override onBackPressed(), since if the activity is destroyed the necessary lifecycle methods will be called (plus, it could be finished without pressing the back button, for example if calling finish() on it).
Stopping the service in onDestroy() instead of onPause() saves you from having to check for isUserMovingToSecondActivity.

bindService() gives NullPointerException and onServiceConnected() never called

Let me start by saying I've been searching for a long time, found a lot of similar questions (on SO) but I can't find anything to solve this yet:
I have a Service (jobcrawler) that is started by calling startservice().
Within this service, I am starting a long-running thread, which at some point is calling a class (webservice) whose init looks like this:
public webservice(Context context) {
this.context = context;
this.db = new DatabaseHandler(this.context);
this.access_token = db.getAuthKey();
}
After some network calls, the class(webservice) receives data in a method called recieveData().
Inside recieveData I am attempting to bind to the service as follows:
if (!isBound) {
// not bound yet, then bind to the service.
Intent intent = new Intent(this, jobcrawler.class);
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
Now, I'm getting nullpointerexemption on the line where I call bindservice. Note, that I'm not actually attempting to do anything with the service yet. I'm just trying to bind to it.
any help would be appreciated... if I had hair I'd be pulling it out! lol
Here's some additional code that I think is relevant.
myConnection:
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder service) {
Log.e("webservice", "service is connected");
MyLocalBinder binder = (MyLocalBinder) service;
myService = binder.getService();
isBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
Log.e("webservice", "service is disconnected");
isBound = false;
}
};
binder from service called MyLocalBinder:
public class MyLocalBinder extends Binder {
public jobcrawler getService() {
Log.e("Job Crawler", "returning self");
return jobcrawler.this;
}
}
service's onbind method:
private final IBinder myBinder = new MyLocalBinder();
#Override
public IBinder onBind(Intent arg0) {
Log.d("JobCrawler Service", "Service is bound");
return myBinder;
}
oh and this is where I load the class from the thread inside the service, just in case I should be using a different context or something:
private webservice ws= new webservice(getBaseContext());
I know it's a bit late, but I ran upon the same problem and maybe some googlers will be happy :)
So for me the following worked:
Call the bindService method in reference to your context:
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

Binding a service before calling a method on it

i have a problem with binding a service to an activity in Android. The problem occurs in the activity:
public class ServiceTestActivity extends Activity {
private static final String TAG = "ServiceTestAct";
boolean isBound = false;
TestService mService;
public void onStopButtonClick(View v) {
if (isBound) {
mService.stopPlaying();
}
}
public void onPlayButtonClick(View v) throws IllegalArgumentException, IllegalStateException, IOException, InterruptedException {
if (isBound) {
Log.d(TAG, "onButtonClick");
mService.playPause();
} else {
Log.d(TAG, "unbound else");
Intent intent = new Intent(this, TestService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
isBound = true;
}
};
}
isBound tells if the service (called TestService) is already bound to the activity.
mService is the reference to the service.
Now if i call "onPlayButton(..)" the first time, with the service not beeing bound, bindService(..) is called and isBound switches from false to true. Then if i call "onPlayButton(..)" again, it calls "playPause()" on the service object. To here everything works fine.
But i want "playPause()" to be called right after the service has been bound, so i changed my code to this:
public void onPlayButtonClick(View v) throws IllegalArgumentException, IllegalStateException, IOException, InterruptedException {
if (isBound) {
Log.d(TAG, "onButtonClick");
mService.playPause();
} else {
Log.d(TAG, "unbound else");
Intent intent = new Intent(this, TestService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mService.playPause();
}
}
From this point on I get a NullPointerException, because mService doesn't have a reference to the bound service, it's still null. I checked that by logging the value of mService at different positions in the code.
Any tips on what I am doing wrong here? I am pretty new to programming (especially binding) services in android, but I still don't see where the major differences between my to versions are.
One solution is to call playPause() in onServiceConnected(). Another solution is to call startService() with a custom intent that will tell the service to play. I think you might want to think about a redesign. I would try to design the service so that you can start and bind to the service when the activity starts and stop the service when the activity stops. If you need a service that will stay active past the lifetime of the activity, extend the Application class and you can start the service in the onCreate() method.
The binding of the service occurs asynchronously, i.e. the service may not be bound if bindService() returns but when onServiceConnected() has completed. Because of that mService is still null and the exception is thrown.
One solution would be to disable the button by default (in XML or onCreate()) and enable the button in onServiceConnected().

Good practices for services on Android

I am currently using 2 services in my app:
1: LocationService, basically trying to localize the user, and aims to stay alive only when the app is on foreground.
2: XmppService, which init the connection with the xmpp server, receive messages, send it, logout ... and aims to stay alive until the user logout.
I've been reading quite a lot of documentation, but I just can't make it clear.
I'm having Leaks when I try to store reference of LocationServiceBinder, which is used to call my service functions (using AIDL interfaces). Same for Xmpp. When I unbind, I get sometimes ANR (which look like to be linked with the fact that my bind/unbind are weirdly done, onResume, onRestart ...).
All the system is working, but I'm sure it is not the right way to do it, and please I would love to follow experienced people to come back in the right side of the force ! :)
Cheers
UPDATE
My Location Service is bind at the app launch to get as fast as possible the user's position :
if(callConnectService == null) {
callConnectService = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
locationServiceBinder = LocationServiceBinder.Stub.asInterface(binder);
try {
global.setLocationBinder(locationServiceBinder);
global.getLocationBinder().startLocationListener();
} catch (Exception e){
Log.e(TAG, "Service binder ERROR");
}
}
public void onServiceDisconnected(ComponentName name) {
locationServiceBinder = null;
}
};
}
/* Launch Service */
aimConServ = new Intent(this, LocationService.class);
boolean bound = bindService(aimConServ,callConnectService,BIND_AUTO_CREATE);
My Xmpp Service is launched when the user log in :
callConnectService = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
try {
Log.d(TAG, "[XMPP_INIT] Complete.");
global.setServiceBinder(ConnectionServiceBinder.Stub.asInterface(binder));
//Connect to XMPP chat
global.getServiceBinder().connect();
} catch (Exception e){
Log.e(TAG, "Service binder ERROR ");
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "Service binder disconnection ");
}
};
/* Launch Service */
Intent aimConServ = new Intent(MMWelcomeProfile.this, XmppService.class);
bound = bindService(aimConServ,callConnectService,Context.BIND_AUTO_CREATE);
and unbind on each Activity :
if (callConnectService != null){
unbindService(callConnectService);
callConnectService = null;
}
It hasn't been well-documented in Google's official dev guide, Context.bindService() is actually an asynchronous call. This is the reason why ServiceConnection.onServiceConnected() is used as a callback method, means not happened immediately.
public class MyActivity extends Activity {
private MyServiceBinder myServiceBinder;
protected ServiceConnection myServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
myServiceBinder = (MyServiceBinderImpl) service;
}
... ...
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// bindService() is an asynchronous call. myServiceBinder is resoloved in onServiceConnected()
bindService(new Intent(this, MyService.class),myServiceConnection, Context.BIND_AUTO_CREATE);
// You will get a null point reference here, if you try to use MyServiceBinder immediately.
MyServiceBinder.doSomething(); // <-- not yet resolved so Null point reference here
}
}
A workaround is call MyServiceBinder.doSomething() in myServiceConnection.onServiceConnected(), or perform MyServiceBinder.doSomething() by some user interaction (e.g. button click), as the lag after you call bindService() and before system get a reference of myServiceBinder is quite soon. as long as you are not using it immediately, you should be just fine.
Check out this SO question CommonsWare's answer for more details.
this thread is quite old, but I just discovered it.
Actually there is only one way for your service to go on living if it is bound : it has to be also started. The documentation is not quite clear about that but a service can be both started and bound.
In that case, the service will not get destroyed when you unbind from it, it will get destroyed when :
you stop it and there is no one bound to it
you unbind from it and it has been stopped before.
I made a small Service Lifecycle demo app on GitHub and it's also available on Google Play.
Hope that helps ;)
if you bind to a service in an Activity, you need to unbind it too:
#Override
protected void onResume() {
Log.d("activity", "onResume");
if (locationServiceBinder == null) {
doBindLocationService();
}
super.onResume();
}
#Override
protected void onPause() {
Log.d("activity", "onPause");
if (locationServiceBinder != null) {
unbindService(callConnectService);
locationServiceBinder = null;
}
super.onPause();
}
where doBindLocationService():
public void doBindLocationService() {
Log.d("doBindService","called");
aimConServ = new Intent(this, LocationService.class);
// Create a new Messenger for the communication back
// From the Service to the Activity
bindService(aimConServ, callConnectService, Context.BIND_AUTO_CREATE);
}
You need to do this practise for your XmppService as well

Categories

Resources