Android Binder Leaks - android

I just read http://www.ozdroid.com/#!BLOG/2010/12/19/How_to_make_a_local_Service_and_bind_to_it_in_Android about how there can be memory leaks when binding to a local service...
I am currently implementing binding to a local service using the following code.
In the service I have:
private final Binder binder=new LocalBinder();
public class LocalBinder extends Binder implements IStreamCommander {
public void registerPlayer(IStreamListener callback) {
theUI=callback;
}
public void removePlayer(IStreamListener callback) {
theUI=null;
}
public void play(Station NowP) {
playURL(NowP);
}
public void stop() {
stopIt();
}
}
Where IStreamCommander is defined:
public interface IStreamCommander {
void registerPlayer(IStreamListener callback);
void removePlayer(IStreamListener callback);
void play(Station SongID);
void stop();
}
and IStreamListener is defined:
public interface IStreamListener {
void updateUI(String msg, int buttons);
}
I then have this in the activity:
this.bindService(startedSvc, svcConn, 0);
and
private ServiceConnection svcConn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
service = (IStreamCommander) binder;
}
public void onServiceDisconnected(ComponentName className) {
service = null;
}
};
So am I leaking memory, or is this okay?

If you are going to stick with the binding pattern, I would:
Move your Binder to a standalone public class, not an inner class
Bind using getApplicationContext(), rather than this
Make sure you use onRetainNonConfigurationInstance() properly to pass your binding between instances of your activity when the configuration changes (e.g., screen rotation)
Here is a sample project demonstrating this.

Related

Android : how to update a interface method implemented by a class A and update from Class B?

I've declare an interface named
public interface listener {
public void onError();
}
Implemented this interface in a class A.
I've another service B which I open via startService(new Intent(this, B.class));
Now desire is, using that interface when I receive any error in class B, can be notify to class A without using Broadcast?
If your ActivityA and ServiceB are in the same process, you can use bindService(Intent intent, ServiceConnection conn, int flags) instead of startService to initiate the service. And the conn will be a inner class just like:
private ServiceConnection conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyService = ((ServiceB.MyBinder) service).getService();
mMyService.setListener(new Listener() {
#Override
public void onError() {
// ...
}
});
}
#Override
public void onServiceDisconnected(ComponentName name) {
mMyService = null;
}
};
mMyService is the instance of your ServiceB.
In ServiceB, just override onBind:
public IBinder onBind(Intent intent) {
return new MyBinder();
}
and add the following class in ServiceB:
public class MyBinder extends Binder {
public ServiceB getService() {
return ServiceB.this;
}
}
in addition, add a public method:
public void setListener(Listener listener) {
this.mListener = listener;
}
so you can notify ActivityA in ServiceB like:
someMethod(){
// ...
mListener.onError();
}
ps: bindService will be like this:
this.bindService(intent, conn, Context.BIND_AUTO_CREATE);
and do not forget
protected void onDestroy() {
this.unbindService(conn);
super.onDestroy();
}
Hope it helps.

How to access 'Activity' from a Service class via Intent?

I am new to Android programming - so I do not have very clear understanding of the 'Context' and the 'Intent'.
I want to know is there a way to access Activity from a Service class?
i.e. Let's say I have 2 classes - one extends from "Activity" and other extends from "Service" and I have created an intent in my Activity class to initiate the service.
Or, how to access the 'Service' class instance from my 'Activity' class - because in such workflow Service class is not directly instantiated by my Activity-code.
public class MainActivity extends Activity {
.
.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, CommunicationService.class));
.
.
}
public class CommunicationService extends Service implements ..... {
.
.
#Override
public int onStartCommand(final Intent intent, int flags, final int startId) {
super.onStartCommand(intent, flags, startId);
....
}
}
You can use bindService(Intent intent, ServiceConnection conn, int flags) instead of startService to initiate the service. And the conn will be a inner class just like:
private ServiceConnection conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyService = ((CommunicationService.MyBinder) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
mMyService is the instance of your CommunicationService.
In your CommunicationService, just override:
public IBinder onBind(Intent intent) {
return new MyBinder();
}
and the following class in your CommunicationService:
public class MyBinder extends Binder {
public CommunicationService getService() {
return CommunicationService.this;
}
}
So you can use mMyService to access any public methods and fields in your activity.
In addition, you can use callback interface to access activity in your service.
First write a interface like:
public interface OnChangeListener {
public void onChanged(int progress);
}
and in your service, please add a public method:
public void setOnChangeListener(OnChangeListener onChangeListener) {
this.mOnChangeListener = onChangeListener;
}
you can use the onChanged in your service anywhere, and the implement just in your activity:
public void onServiceConnected(ComponentName name, IBinder service) {
mMyService = ((CommunicationService.MyBinder) service).getService();
mMyService.setOnChangeListener(new OnChangeListener() {
#Override
public void onChanged(int progress) {
// anything you want to do, for example update the progressBar
// mProgressBar.setProgress(progress);
}
});
}
ps: bindService will be like this:
this.bindService(intent, conn, Context.BIND_AUTO_CREATE);
and do not forget
protected void onDestroy() {
this.unbindService(conn);
super.onDestroy();
}
Hope it helps.

ClassCastException: android.os.Handler$MessengerImpl cannot be cast to My

I have declared an handler in my service:
final Messenger messenger = new Messenger(new MusicAPIControlHandler());
public class MusicPlayerBinder extends Binder {
public MusicPlayerService getService() {
return MusicPlayerService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
public class MusicAPIControlHandler extends Handler {
#Override
public void handleMessage(Message msg) {
//
}
}
In my class which implements the ServiceConnection and which binds the service, I do the following:
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
handler = (MusicPlayerService.MusicAPIControlHandler) iBinder;
}
My problem is, I get the a ClassCastException when I try to cast the binder to my handler, which actually should be an instance of it because it´s set to the binder of the messenger.
Where am i going wrong?
I have NOT declared my service as an process

android bind service to activity

I have seen several similar examples here but can't seem to get my service to bind with activity.
I am getting the error
"android.os.binderproxy cannot be cast to IC_CommissaryService".
My service looks like this:
public class IC_CommissaryService extends Service
{
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
IC_CommissaryService getService()
{
return IC_CommissaryService.this;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
}
private boolean SendOrderToServer(int orderID)
{
/* do stuff*/
}
}
and my activity looks like this:
public class SubmitOrders extends Activity
{
IC_CommissaryService ICservice;
#Override
public void onCreate(Bundle savedInstanceState)
{
Intent serviceintent = new Intent(this, IC_CommissaryService.class);
serviceintent.putExtra("binded", true);
bindService(serviceintent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName className, IBinder service)
{
Log.e("TEST", "SERVICE CONNECTED");
try
{
ICservice =(IC_CommissaryService.LocalBinder)service).getService();
for(int i = 0; i < Submitorders.size(); i++)
{
ICservice.SendOrderToServer(Submitorders.get(i).intValue());
}
}
catch(Exception ex)
{
Log.e("Error", "Error connecting service: " + ex.getMessage());
}
}
#Override
public void onServiceDisconnected(ComponentName className)
{
}
};
}
I am getting the error in my activity on the line ICservice =(IC_CommissaryService.LocalBinder)service).getService();
I think I have done the same as people already suggested in other posts so any help please?
thanks
I had the same kind of problem. I just figured it out today. Please look at the parts annotated with <<===== below. I hope it helps.
public class PracticeServiceBindingActivity extends ListActivity {
private MyService.MyBinder service; <<====
....
private ServiceConnection connection = new ServiceConnection( ){
public void onServiceConnected (ComponentName name, IBinder service) {
setService(MyService.MyBinder) service; <<====
....
}
}
public void onCreate(....) {
...
public MyService.MyBinder getService(){ <<=====
return service;
}
public void setService(MyService.MyBinder service) { <<=====
this.service = service;
}
}
}
Quote from the Bound Services documentation:
If your service is private to your own application and runs in the
same process as the client (which is common), you should create your
interface by extending the Binder class and returning an instance of
it from onBind().
Remove the android:process attribute in in AndroidManifest.ml to make the service run in the same process. I had the same problem today it did the trick.
These are the abstract classes that I use to solve this problem:
https://gist.github.com/frenchie4111/6086c6e4327d7936364a
Just extend both these classes with your service and activity (You can change the fragment from a fragment to an activity with ease). And make sure that in your service/fragment onCreate you set the serviceClass like so:
public void onCreate( Bundle savedInstance ) {
super.onCreate( savedInstance );
this.serviceClass = IC_CommissaryService.class;
}

Android ClassCast exception when binding to service

Ok, I'm new to android development and am trying to bind to a service so that I can call methods on the service once it's been started. The Activity and Service described below are both part of the same application so there shouldn't be any problems there, but everytime I run my app I get the following error:
java.lang.ClassCastException: android.os.BinderProxy
The line this happens on is:
LocalBinder binder = (LocalBinder) service;
My Activity code (simplified is):
public class Main extends Activity {
boolean gpsBound = false;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/** Called whenever the activity is started. */
#Override
protected void onStart() {
super.onStart();
// Bind to GPSService
Intent i = new Intent(this, GPSService.class);
startService(i);
bindService(i, connection, Context.BIND_AUTO_CREATE);
}
/** service binding */
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// After binding to GPSService get the instance of it returned by IBinder
LocalBinder binder = (LocalBinder) service;
gpsBound = true;
}
public void onServiceDisconnected(ComponentName className) {
gpsBound = false;
}
};
}
Service:
public class GPSService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent i) {
// TODO Auto-generated method stub
return new LocalBinder<GPSService>(this);
}
/**
* Our implementation of LocationListener that handles updates given to us
* by the LocationManager.
*/
public class CustomLocationListener implements LocationListener {
DBHelper db;
CustomLocationListener() {
super();
}
// Overridden methods here...
}
}
And finally my LocalBinder:
/**
* A generic implementation of Binder to be used for local services
* #author Geoff Bruckner 12th December 2009
*
* #param <S> The type of the service being bound
*/
public class LocalBinder<S> extends Binder {
private String TAG = "LocalGPSBinder";
private WeakReference<S> mService;
public LocalBinder(S service){
mService = new WeakReference<S>(service);
}
public S getService() {
return mService.get();
}
}
I understand the meaning of the ClassCast Exception but cannot understand what to do! I've followed the example in the google documentation but it's still not working. Can anyone shed any light on what might be causing this?
Thanks in advance!
Delete attribute process in your AndroidManifest.xml of your service.
Had same error. I had added the android:process=":process_description" attribute in the manifest. When you add it, your service is created as separate process and hence you get instance of binderProxy (Hence the class cast exception)
If you are trying to bind to a local service than yes, you can just cast it. However if you are trying to bind to a remote (separate process) service you must use the AIDL method as prescribed in this article.
http://developer.android.com/guide/components/aidl.html
the LocalBinder passed in onServiceConnected has a generic type argument, while your local variable LocalBinder binder does not have one.
Resolve this one way or another, either by removing the generic type from the definition of LocalBinder, or by adding one to your declaration of your local variable binder in onServiceConnected
class MyBoundService extends Service{
private final IBinder mBinder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class MyBinder extends Binder{
public void doStuff(){
//Stuff
}
//More Binder Methods
}
}
class MyActivity extends Activity{
private MyBinder mBinder;
#Override
protected void onStart(){
Intent intent = new Intent(this, MyBoundService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop(){
unbindService(mConnection);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (TaskBinder) service;
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private void doStuff(){
if (mBound)
mBinder.doStuff();
}
}
No real need to fiddle around with weak references and whatnot. just be sure to unbind (I didn't in the sample)
If you want to invoke service methods ASAP, just put calls in onServiceConnected, after you set mBinder. otherwise, just invoke from other callbacks (onClick events and whatnot).

Categories

Resources