In the Android official developer guide, the chapter of Bound Service, there is an example about local binder which extends the Binder, the code is like this:
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 especially for this is the method for client to call:
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
I feel a bit confused about for such a simple action, why we need to put it to Service? What's further more is this Service is just a local service, can be only used in current process of application, but not other applications. Of course it is obviously this method can be put into any class as a public method, which is enough for the above case.
I can understand the purpose of to put such as file downloading code into a service (or maybe IntentService is a bit more suitable), which is suitable to the Service's feature: run long time processing code.
But what I cannot understand is why we need a Local Service like this?
Local bind service is needed to handle client-server case
Suppose you have a case when you need to do some long operations many times and also need result of it and do some on result in your Activity.
In that case you bind service and use its method and also can use callback to get results and do operations in activity.
Related
Maybe I'm just tired. However I am struggling to understand just how the IBinder interface works.
A service has a method call onBind(Intent intent) and this passes some form of the IBinder to a binding activity.
Where I am struggling is how to visualize this. Is an IBinder object a pipe between the service and activity, so that an activity can interact directly with the service? Or is an IBinder an object that is passed to the activity that has information from the Service in it (similar to an intent)?
Edit: The idea is for me to be able to call the getSomeData function from the binding activity. Or rather, the idea is to get the mSomeData obect to the activity.
I have the following classes that I am using, and Im trying to figure out the best way to have the activity request data (serializable if needed though I'd rather not) from the service.
public class MainService extends Service{
private CustomBinder mIBinder;
private SomeDataObject mSomeData;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mIBinder = new CustomBinder();
return super.onStartCommand(intent, flags, startId);
}
#Override
public CustomBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// This is auto generated and I havent changed it yet. I am not
// Experiencing errors as this is not designed to run yet.
throw new UnsupportedOperationException("Not yet implemented");
}
public final SomeDataObject getSomeData(){
return mSomeData;
}
class SomeBinder extends MainEngineBinder{
// All the Auto Generated Stuff
}
}
You don't need to know how IBinder works. Just subclass Binder (Not IBinder) and add some methods to the Binder subclass. These methods can be called from the activity bound to your service.
The easiest way to do this in your case is to have an inner class extend Binder, and that inner class will then be able to access methods/fields from your service and return them to the activity. You look like you're working from a similar template as the Android documentation on bound services, so here's the example from the docs - all the binder does in this case is expose a getter for getting the service itself, and a caller can simply call methods directly on the 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);
}
}
The caller calling bind() will need to cast the IBinder to LocalService.LocalBinder. In this case, the IBinder is neither a pipe nor an intent-like object; it is a normal Java-style interface.
Note that all of this holds true if you are not doing IPC. The documentation states that in this case (same application, same process) the call is a normal Java call - you can pass objects, references, whatever you like, because everything is in the same process.
Note that IPC, pipes, marshalling, serialisation and the like are all unnecessary (unless you want your service to be called from other apps or if you use multi-process apps). An Android service is not even run in a separate thread unless you start a new thread explicitly, and does not have it's "own" thread or process - all calls made to binders or the service object itself are made in the same thread as the caller.
In your case, the exact example from the Android documentation seems like the best approach.
I can either set an variable int[] by using the binder object or using the service class object itself. Code is in public class MyActivity extends Activity. Which one is the way to go, both works:
private BackgroundService.BackgroundBinder mBoundBinder; // to get methods of nested binder class inside BackgroundService
private BackgroundService mBoundService; //service class object
public void onServiceConnected(ComponentName className, IBinder binder) {
mBoundService = ((BackgroundService.BackgroundBinder) binder).getService(); //google version
mBoundBinder = ((BackgroundService.BackgroundBinder) binder);
}
Service int[] array can be set in two ways:
1. mBoundService.setListeners = genArr(); //genArr() returns int[]
2. mBoundBinder.setListeners(genArr());
Google android.developer version uses following inside Service class. This shrinks the necessary nested Binder class down to one method returning the service object itself, thus its methods can directly be accessed. Nice.
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
So, from what I understand, I should use intents to update the UI of an activity from a service.
But, I am a bit concerned about the efficiency of what I want to do.
Theoretically, it shouldn't really pose a serious problem, but I don't want to have a situation in which I wake up my phone, and have 20-30 onTick-s running at once, each time generating a new intent.
What I want (if possible) is for each onTick's intent to overwrite the previous one, so that the receiver only has process one at a time.
I know pending intents have FLAG_UPDATE_CURRENT, but doing things the right way means (if I understand correctly) using a local broadcast, which doesn't work with pending intents.
This turned out to be surprisingly simple.
A pskink suggested in his comment, all you need is a binder:
Create a class that extends the Binder class. Keep in mind that the Binder class implements IBinder, which is why you can pass an instance of your new class as an instance of IBinder (which is something you will be doing later on)
In this class, implement various functions you want your main activity to be able to use. If you want your service to be able to run things from the activity as well, you can pass an instance of an inner class of the activity, which has the relevant functions in it, or even pass a pointer to the activity (this).
Keep in mind: Do these things at your own risk. If, for example, your activity is destroyed - for example, due a change of orientation, the pointers might become worthless and errors will be thrown
I recommend implementing an invalidate inside your new class to help you handle these situations, but perhaps there are better practices I am not personally familiar with.
Now, you have a two-way communication channel with your activity.
Below is some sample code. Note that if you don't want your service to be destroyed when the activity is, you need (as far as I understand it) to also start it using startService (in addition to using bindService)
public class MyService extends Service {
private IBinder yourBinder; //A class that will connect your service and your activity
#Override
public void onCreate() {
super.onCreate();
yourBinder = new YourBinderClass();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return yourBinder;
}
Your binder class:
public class YourBinderClass extends Binder {
private Service thisService;
public YourBinderClass(Service myService)
{
thisService = myService;
}
public Service getService()
{
return thisService;
}
And finally, inside your main activity:
private YourBinderClass yourBinderClass;
private Service yourService;
private ServiceConnection sc = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
yourBinderClass = (YourBinderClass) service;
yourService = yourBinderClass.getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
And to launch the service:
getBaseContext().bindService(new Intent(getBaseContext(),MyService.class), sc, 0)
Note that the service won't be ready immediately, so if your activity is dependent on it, you might want to signal that you are ready to do stuff in onServiceConnected
I have a custom implementation of VpnService, which needs to perform some additional cleanup on disconnection. Everything works fine when I am stoping VpnService from my application using service bindings, but I need to perform that cleanup when client is disconnecting from the Vpn using system dialog.
So, how can I catch the disconnection and add some handlings to that?
Get VPN Connection status on Android - this could be the solution, but it's not working on android 4+.
From logs point of view there are only two entries:
03-20 03:27:09.478: INFO/Vpn(504): Switched from org.my.package to [Legacy VPN]
03-20 03:27:09.478: DEBUG/Vpn(504): setting state=IDLE, reason=prepare
I just ran into the same issue. VpnService.onRevoke() is not called.
It turns out this happens because I use a custom IBinder defined via AIDL wich I return from onBind(). VpnService implements onBind() too and returns an instance of VpnService.Callback.
Which is implemented this way:
private class Callback extends Binder {
#Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
if (code == IBinder.LAST_CALL_TRANSACTION) {
onRevoke();
return true;
}
return false;
}
}
VpnService.Callback does not use AIDL and just checks if the function code IBinder.LAST_CALL_TRANSACTION was sent. If so it executes onRevoke().
I integrated this code fragment in my custom IBinder implementation and now I receive the onRevoke() message. See the following example:
private final IBinder mBinder = new ServiceBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public final class ServiceBinder extends ICustomVpnService.Stub
{
... implement methods defined in ICustomVpnService.Stub ....
/**
* Intercept remote method calls and check for "onRevoke" code which
* is represented by IBinder.LAST_CALL_TRANSACTION. If onRevoke message
* was received, call onRevoke() otherwise delegate to super implementation.
*/
#Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException
{
// see Implementation of android.net.VpnService.Callback.onTransact()
if ( code == IBinder.LAST_CALL_TRANSACTION )
{
onRevoke();
return true;
}
return super.onTransact( code, data, reply, flags );
}
private void onRevoke()
{
// shutdown VpnService, e.g. call VpnService.stopSelf()
}
}
How did I figure it out? I searched the android source code for where onRevoke() is actually invoked. For that I find grepcode (android) pretty helpful. I often read the android source to understand how things work.
This happens if we use a custom IBinder defined via AIDL, which I return from onBind().
VpnService implements onBind() too and returns an instance of VpnService.Callback private-class.
Solution:
#Override
public IBinder onBind(Intent intent) {
String action = intent != null ? intent.getAction() : null;
if (action != null && action.equals(VpnService.SERVICE_INTERFACE)) {
return super.onBind(intent);
}
return yourBinder;
}
Note that above works as long as you never set VpnService.SERVICE_INTERFACE as action manually.
That action should only be set by Android's internal-logic (automatically, never by our code manually).
So far I've a class for a simple local service. What I want to do is to send requests to this local service from different activies in my application. Depending on the parameters of these requests the service will connect via HttpClient to a webserver and receive a JSONObject and return it to the activity. All the HTTP communication is already working within my activity, but I'd like it to run in a separate thread in my local service now.
The source code of my very simple local service looks like this so far:
// BackgroundService.java
package com.test.localservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class BackgroundService extends Service
{
#Override
public void onCreate() {
super.onCreate();
Log.i("BackgroundService", "onCreate()");
Thread thr = new Thread(null, new RunThread(), "BackgroundService");
thr.start();
}
class RunThread implements Runnable
{
public void run() {
Log.i("BackgroundService", "run()");
/* Here the HTTP JSON communication is going to happen */
//BackgroundService.this.stopSelf();
}
}
#Override
public void onDestroy()
{
Log.i("BackgroundService", "onDestroy()");
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
Log.i("BackgroundService", "onStart()");
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
Log.i("BackgroundService", "onBind()");
return null;
}
}
The problem I'm facing now (due to a lack of knowledge) is the communication part Activities <--> Local Service. I found some communication and object exchange examples for a Remote Service using AIDL (Android Interface Definiton Language). However, I'm not sure if I've to go this path. I would simply like to exchange either my own defined objects or if that is not possible then just a JSONObject or if it makes things much easier even just simple String Arrays would work (for now).
Can someone please point me in the right direction and if possible give a simple example of an Object exchange (both ways) with a local service.
Thanks in advance.
I'd suggest using an AsyncTask instead of your own thread in this scenario.
To give an activity a reference to your BackgroundService object, your options are:
Use a singleton (i.e., make your BackgroundService object available from a static context, such as a public static data member, nulling out that static reference in onDestroy()).
Follow the LocalServiceBinding API sample to return an IBinder that simply gives access to the BackgroundService object.
From there, everything is in the same JVM, so you can just pass objects around. Be careful, though, not to hold onto anything past the component lifetime (e.g., Activity should not cache the singleton).