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).
Related
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.
I have background service which started on device boot . I want to get some data from that service in my activity .
I want the data, only when my activity start. so the basic requirement is that when my activity start it make a connection with the background service and get the data from this service and when activity stop then disconnect from the service.
You need to use bindService() to bind with running service and communicate with it.
Reference : http://developer.android.com/guide/components/bound-services.html
For example (from Android Docs),
public class BindingActivity extends Activity {
YourService 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 Your Service
Intent intent = new Intent(this, YourService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
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 your Service.
// 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 mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to the running Service, cast the IBinder and get instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
In your service,
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);
}
}
Basically, you should use bindService() in your activity onStart() and unbindService() in onStop()
I've read the documentation about Bound Services, where it is shown that you can easily communicate through Messages from an Activity to a remote (i.e. not in the same context) Service but is there any way to send messages from the Service to the bound Activity? For example, my activity bounds to a running background service of the same application, sends a message to it and upon the reception of this message the service replies with a message to the activity.. how do I implement this? Can you point me to some documentation that explains this topic?
NOTE: This is only for in-process services and activities, not remote like question asked.
Using a service to communicate with an activity involves making a listener that you can pass to the service from the activity.
You need to make a service that is bound to an activity.
The first step is making a service. In the service make sure you have a Binder object and the method to return a binder object. Below is an example that I used in my service to retrieve my binder. Also notice this binder has a method to set a listener, which will be saved in the service as a BoundServiceListener type field.
/**
* 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 DownloadBgBinder extends Binder {
public DownloadBgService getService() {
// Return this instance of LocalService so clients can call public methods
return DownloadBgService.this;
}
public void setListener(BoundServiceListener listener) {
mListener = listener;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
Now you need to create some kind of interface that you can pass to the binder object that your service can use to send updates to. Below is my BoundServiceListener.
public interface BoundServiceListener {
public void sendProgress(double progress);
public void finishedDownloading();
}
Now in your activity you need to create a ServiceConnection object that is used for binding to a service. SO add somethinglike this.
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
DownloadBgBinder binder = (DownloadBgBinder) service;
mService = binder.getService();
binder.setListener(new BoundServiceListener() {
#Override
public void sendProgress(double progress) {
// Use this method to update our download progress
}
#Override
public void finishedDownloading() {
}
});
mBound = true;
}
Now the key line to notice here is
binder.setListener(new BoundServiceListener() {
#Override
public void sendProgress(double progress) {
// Use this method to update our download progress
}
#Override
public void finishedDownloading() {
}
});
This part is where I am actually sending my BoundServiceListener interface to the service class. The service class then uses that listener object here
if (mListener!=null)
mListener.finishedDownloading();
if (mListener!=null)
mListener.sendProgress(percent);
Now you can put this anywhere you need to in your service class, and your activity will receive your progress update.
Also be sure to include following in your activity to actually bind and start the service.
Intent intent = new Intent(this, DownloadBgService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
Keep in mind that even though you bind to a service, it it not actually started until you call start service. Binding to the service just connects the service to an activity. the startService() method calls the services
onStartCommand(Intent intent, int flags, int startId)
Also declare your service in your manifest
<service android:name=".services.DownloadBgService" />
Also unbind the service when the activity leaves by
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
Hope this helps.
Found example in the reference documentation at Remote Messenger Service Sample.
In Short, the Answer is by assigning a Messenger with ResponseHandler to msg.replyTo(). Let's see in the below example how we do it.
Brief about what this Example Does:
In this Example, We have a button in MainActivity whose onClick() is linked to sendMessage(View view). Once the Button is Clicked, We Send a Custom Message to RemoteService. Once Custom Message is received in Remote Service, We append the CurrentTime to Custom Message and send it back to MainActivity.
MainActivity.java
public class MainActivity extends AppCompatActivity {
ServiceConnector serviceConnector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.serviceConnector = new ServiceConnector();
Intent intent = new Intent(this,RemoteService.class);
bindService(intent,serviceConnector, Context.BIND_AUTO_CREATE);
}
public void sendMessage(View view) {
Message msg = Message.obtain();
msg.replyTo = new Messenger(new ResponseHandler(this));
Bundle bundle = new Bundle();
bundle.putString("MyString", "Time");
msg.setData(bundle);
try {
this.serviceConnector.getMessenger().send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
ResponseHandler.java
public class ResponseHandler extends Handler {
MainActivity mainActivity;
public ResponseHandler(Context context){
this.mainActivity = (MainActivity) context;
}
#Override
public void handleMessage(#NonNull Message msg) {
Bundle data = msg.getData();
String dataString = data.getString("respData");
Toast.makeText(this.mainActivity,dataString,Toast.LENGTH_SHORT).show();
}
}
ServiceConnector.java
public class ServiceConnector implements ServiceConnection {
private Messenger messenger;
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
{
this.messenger = new Messenger(iBinder);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
this.messenger = null;
}
public Messenger getMessenger(){
return this.messenger;
}
}
RemoteService.java
public class RemoteService extends Service {
private final IBinder iBinder = new Messenger(new IncomingHandler(this)).getBinder();
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return iBinder;
}
}
IncomingHandler.java
public class IncomingHandler extends Handler {
private RemoteService remoteService;
public IncomingHandler(Context context)
{
this.remoteService = (RemoteService)context;
}
public RemoteService getService()
{
return this.remoteService;
}
#Override
public void handleMessage(#NonNull Message msg) {
try {
msg.replyTo.send(getCurrentTime(msg));
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Message getCurrentTime(Message msg){
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss MM/dd/yyyy", Locale.US);
Message resp = Message.obtain();
Bundle bResp = new Bundle();
bResp.putString("respData", msg.getData().getString("MyString") + " : " +(dateFormat.format(new Date())).toString());
resp.setData(bResp);
return resp;
}
}
1) implement transact/onTransact methods in own Binder.class and binder proxy implementing IInterface.class objects (anon or by extending a class direct) by use of passed in those methods Parcel.class object
2) attach local interface to own Binder object
3) create service and return a binder proxy implementation from onBind method
4) create bond with bindService(ServiceConnection)
5) this will result in returning proxy binder via created bound in interfece implementation
this is an android implementation of IPC with usage of kernel binder space
simplifying in code example :
class ServiceIPC extends Service {
#Override
public Binder onBind() {
return new IInterface() {
IInterface _local = this;
#Override
public IBinder asBinder() {
return new Binder()
{
//
// allow distinguish local/remote impl
// avoid overhead by ipc call
// see Binder.queryLocalInterface("descriptor");
//
attachLocalInterface(_local,"descriptor");
}
#Override
public boolean onTransact(int code,
Parcel in,
Parcel out,
int flags)
throws RemoteException {
//
// your talk between client & service goes here
//
return whatsoever // se super.onTransact();
}
}
}
}.asBinder();
}
}
*then you could use the IBinder on client and service side the transact method to talk with each other (4 example using odd/eve codes to disgusting local remote side as we use same onTransact method for booth sides)
should be able to do this using . a AIDL file like android billing api does. its a way to do RPC calls (communicate across remote processes). but you have to declare each method you want to use. sort of like the interface above already mentioned.
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;
}
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;
}
}