Binding to a service which implements an interface - android

EDIT: The code below has been edited to show the correct solution to the problem.
I have an app which uses a foreground service to perform network operations.
Currently, the foreground service uses a bluetooth connection to perform the operations. I'm trying to implement a new version of the service which uses wifi instead, and allow the user to decide whether to use bluetooth or wifi through shared preferences.
I've implemented the wifi service, and now I need to bind to it. I created an interface, MyService, which defines all of the methods that both versions of the service require. However, when I try to bind to the service in my activity, I get a ClassCastException error.
Here are the relevant parts of my service interface:
MyService.java:
public interface MyService {
// constants
...
// method declarations
...
public interface LocalBinder {
MyService getService(Handler handler);
}
}
And here are the relevant methods which are present in both versions of the service:
MyBluetoothService.java:
public class MyBluetoothService extends Service implements MyService {
private final IBinder mBinder = new LocalBinder();
...
public class LocalBinder extends Binder implements MyService.LocalBinder {
MyService getService(Handler handler) {
mHandler = handler;
// Return this instance of MyService so clients can call public methods
return MyBluetoothService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.w(TAG, "MyBluetoothService bound");
return mBinder;
}
}
MyWifiService.java: Exactly the same as MyBluetoothService.java except with class names changed as necessary.
And here is where I bind to the service in my activity:
MyService mChatService = null;
...
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to MyService, cast the IBinder and get MyService instance
LocalBinder binder = (LocalBinder)service; <------- ClassCastException
mChatService = binder.getService(mHandler);
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName argo) {
mBound = false;
}
};
The ClassCastException occurs on the line indicated above.
Now that all of that is out of the way... is it possible to bind to a service in this way? Alernatively, I could always check shared preferences every time I call a method from the service but I'd rather not.

I'm assuming that the code where it is throwing is a MyService.LocalBinder class and not a MyBluetoothService.LocalBinder class?
What I think you meant to do is define the MyBluetoothService.LocalBinder class to extend from the MyService.LocalBinder class?
e.g.
public class MyBluetoothService extends Service implements MyService {
private final IBinder mBinder = new LocalBinder();
...
public class LocalBinder extends MyService.LocalBinder {
MyService getService(Handler handler) {
mHandler = handler;
// Return this instance of MyService so clients can call public methods
return MyBluetoothService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
Log.w(TAG, "MyBluetoothService bound");
return mBinder;
}
}

Related

How to send an initial message using ServiceConnection?

I have an android service, which is connected to a service connection. Upon initialization, I'd like to send a single String, for example "test message" to the Service connection. How would I do this?
This is my Service class:
public class ExampleService extends Service {
private final IBinder iBinder = new Messenger(new IncomingHandler(this)).getBinder();
#Override
public IBinder onBind(Intent intent) {
return iBinder;
}
}
This is my ServiceConnection implementation:
private ServiceConnection myService = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i("exampleService", "Binding Connect");
messenger = new Messenger(iBinder);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
messenger = null;
}
};
The ServiceConnection monitors the state of the connection to the service, as opposed to communicating information to the service. To communicate with the service, you need to use the binder that is passed as an argument to the onServiceConnected(ComponentName name, IBinder binder) callback.
In your code sample, you are using a Messenger to perform communication instead of directly interacting with the binder. A Messenger is:
a simple wrapper around a Binder that is used to perform the communication
Sample code that does what you are asking:
public class MyService extends Service {
// if there are a lot of options, use an enum; its not 2012 anymore and devices have 4GB+ of memory
public static final int MSG_HELLO = 1;
private class IncomingHandler extends Handler {
#Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_HELLO:
final String stringMessage = (String) message.obj;
Toast.makeText(MyService.this.getApplicationContext(), "MyService: " + stringMessage, Toast.LENGTH_SHORT).show();
default:
return; // message not understood, ignore
}
}
}
final private Messenger messenger = new Messenger(new IncomingHandler());
#Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
public class MainActivity extends Activity {
private static final String HELLO_MESSAGE = "hello originating from MyActivity";
private Messenger messenger = null;
private final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
messenger = new Messenger(iBinder);
// send a HELLO message immediately when connected
final Message message = Message.obtain(null, MyService.MSG_HELLO, HELLO_MESSAGE);
try {
messenger.send(message);
} catch (RemoteException e) {
messenger = null;
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
messenger = null;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
// rest of implentation...
}
For a more detailed example of how to work with Messenger, see the Remote Messenger Service Sample.
A couple notes:
If you are communicating with a local service (i.e. the service is in the same process as the activity), I recommend not using messenger as this will make things more complicated than necessary. Instead, you should create a subclass of Binder that has a method which returns the instance of the service. See the Local Service Sample for an example.
Make sure every bindService(...) has a corresponding unbindService(...) and vice versa. For example, if you call bindService(...) in onCreate(), then call unbindService(...) in onDestroy().
Regardless of whether the service is local or remote, be aware of memory leaks. IBinder instances may stay in memory beyond the lifecycle of the component that is containing it, potentially until the process is destroyed; this can cause a severe memory leak. If you subclass Binder inside of an Activity or Service class, then use a static inner class, as opposed to a non-static inner class which will have an implicit reference to the Service or Activity. If you need a reference to a context or lifecycle aware component, then use a WeakReference. The proper way to deal with this is outside the scope of this question. For related posts, see:
Memory leaks found when Local Binder has a reference to Service
LocalService and LocalBinder leak memory in Android 10
Android service-binder leaks?

Does Binder have to be an inner class?

I am reading upon Android Bound service, http://developer.android.com/guide/components/bound-services.html
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
And all the tutorial, android developer guide and books suggest to have Binder as inner class of service. Is it really have to be only inner class ?
It is an inner class so you can return the outer Service instance easily. You could als make it an external class:
public class LocalBinder extends Binder {
private final LocalService mLocalService;
public LocalBinder(final LocalService service) {
mLocalService = service;
}
LocalService getService() {
return mLocalService;
}
}
Using an inner class saves you from the trouble of creating a field and a constructor.

To communicate with Service, what is the different between bindService() and create a instance of service?

To communicate with Service, what is the different between bindService() and create a instance of service? Why should need to use bindService() to communicate with service? I was confused by it.
(1)
public class BLEService extends Service {
private static BLEService sService;
#Override
public void onCreate() {
super.onCreate();
sService = this;
}
public static BLEService getInstance() {
return sService;
}
}
public class HeartRateActivity extends Activity {
private BLEService mBLEService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBLEService = BLEService.getInstance();
}
}
(2)
public class BLEService extends Service {
private final IBinder mBinder = new LocalBinder();
private BLEService mBLEService;
public class LocalBinder extends Binder {
public MyleService getServerInstance() {
return MyleService.this;
}
}
}
public class HeartRateActivity extends Activity {
private BLEService mBLEService;
private boolean mBounded;
#Override
protected void onStart() {
super.onStart();
Intent mIntent = new Intent(this, BLEService.class);
bindService(mIntent, mConnection, BIND_AUTO_CREATE);
}
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
mBounded = false;
mBLEService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
mBounded = true;
LocalBinder mLocalBinder = (LocalBinder)service;
mBLEService = mLocalBinder.getServerInstance();
}
};
}
Thanks
Edit: Remove new operator in onCreate() of service
You would not instantiate a Service object via its constructor using the new keyword. A service is intended to be a long-running process that is not necessarily tied to the lifetime of the Activity that wants access to it. As such, services are something that you use an Intent to signal to Android that you wish to run them in the same way that you use Intent objects to signal that you wish to start a new Activity.
Using .bindService() you can signal to Android that you want to attach to a running service (and to implicitly start that service if it isn't running already). Once bound, you can communicate with the service via whichever interfaces it has available.

Get the object of a running service

Background: I'm running a background service (independent of the app opened or not) to maintain connection with Tizen-based app on Gear2 (not Android, hence the manual maintenance).
Whenever my phone apps (multiple apps) have data to send to send to the service, I need to get the 'connection' object inside the service and call 'send'.
So my question is: how can I get running service object?
If I can get that service, my code will be like this:
MyConnection connection = runningService.getConnection()
connect.send(message);
Thanks.
If it's only a single object (say connection) you need to periodically access, I would probably make it to be a singleton, which is created by the services and available to the other components of your app:
class MyConnection {
private static MyConnection inst;
public static void set(........) { <-------- set by service
}
public static getInstance() { return inst; } <------- and accessible to other components
}
But, if you need a more elaborate and continuous interaction with your service, you should probably set it to
be a bound service, and hand craft the interface you would like it to implement:
Create a Bound Service:
class MyConnectionService extends Service {
private final IBinder myBinder = new MyLocalBinder();
#Override
public IBinder onBind(Intent arg0) {
return myBinder;
}
public ConnectionRecord getConnection() {
return myConnection;
}
public class MyLocalBinder extends Binder {
MyConnectionService getService() {
return MyConnectionService.this;
}
}
}
And bind to it from another component, e.g. an Activity:
public class MyActivity extends Activity {
MyConnectionService serviceConnector;
boolean isBound = false;
private ServiceConnection serviceConnector = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
MyLocalBinder binder = (MyLocalBinder) service;
serviceConnector = binder.getService(); //<--------- from here on can access service!
isBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
serviceConnector = null;
isBound = false;
}
};
.
.
.
}
Note that after onServiceConnected() is completed you will have a serviceConnector object you can use to communicate
with the service, which is what we aimed for.
you cannot have multiple instance of a service. so you just need to send commands to it, via startService().

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