Android ClassCast exception when binding to service
Asked Answered
U

4

6

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!

Unavailing answered 7/11, 2011 at 17:31 Comment(0)
S
3

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).

Scarce answered 7/11, 2011 at 17:53 Comment(7)
Do you mean like: 'LocalBinder<GPSService> binder = (LocalBinder<GPSService>) service;' If so that still gives the same error!Unavailing
I was leaning towards taking out the generics entirely. I'll edit in a quick sample of use of a boundService I'm writing right now.Scarce
Will have a go myself without generics but if you could give an example that would be great! Thanks, JamesUnavailing
That should do it (unless you really need the generics in your IBinder, which would be a little weird)). You need not bind/unbind in onStart/onStop. onCreate/onDestroy works fine too, but you should likely avoid onResume/onPause, since these get called too much.Scarce
Still no luck. Do you have any other suggestions?Unavailing
Ok, I've got the basic template you gave running with no errors so I'll build it up from that. Thanks for all your help!Unavailing
Ok, turns out the only problem with my original code was that my manifest file specified service process as remote!Unavailing
S
32

Delete attribute process in your AndroidManifest.xml of your service.

Sheepshank answered 9/6, 2012 at 9:7 Comment(5)
Curious, any detailed reason?Gutta
@ThomasDecaux I believe it's because LocalBinder can only be used whenever the service runs in the same process (local) as the application. The process attribute creates a separate process for the service to run in, and thus, you must use IPC to communicate with it. During the bind process, Android provides you with a BinderProxy to act as the bridge to the service so that you can instantiate your IPC classes (written in AIDL). https://mcmap.net/q/752470/-android-service-android-os-binderproxy-errorGordie
It's an old thread, but this just saved my backside! Nice catch!Mako
The fact that you caught that without the OP posting his manifest - genius. Thanks!Lindesnes
Whats the right way to do this if I need to work on a separate process?Thirzi
P
4

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

Predictory answered 14/3, 2012 at 19:32 Comment(1)
Here's the new valid link developer.android.com/guide/components/aidl.htmlIntrude
B
4

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)

Bandit answered 21/9, 2012 at 3:59 Comment(0)
S
3

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).

Scarce answered 7/11, 2011 at 17:53 Comment(7)
Do you mean like: 'LocalBinder<GPSService> binder = (LocalBinder<GPSService>) service;' If so that still gives the same error!Unavailing
I was leaning towards taking out the generics entirely. I'll edit in a quick sample of use of a boundService I'm writing right now.Scarce
Will have a go myself without generics but if you could give an example that would be great! Thanks, JamesUnavailing
That should do it (unless you really need the generics in your IBinder, which would be a little weird)). You need not bind/unbind in onStart/onStop. onCreate/onDestroy works fine too, but you should likely avoid onResume/onPause, since these get called too much.Scarce
Still no luck. Do you have any other suggestions?Unavailing
Ok, I've got the basic template you gave running with no errors so I'll build it up from that. Thanks for all your help!Unavailing
Ok, turns out the only problem with my original code was that my manifest file specified service process as remote!Unavailing

© 2022 - 2024 — McMap. All rights reserved.