My app polls data from server after it was notified by cloud messaging.
The data is fetched in GcmListenerService. My current approach is to use an Intent to transfer the data to the relevant activity via a broadcast receiver. This only works if the activity is in foreground.
How to store the data fetched by GcmListenerService such that the activity will update itself according to the fetched data as soon as it is resumed?
You could use service binding.
Declare a Binder implementation as part of your service definition:
public class MyService extends Service {
private final IBinder mBinder = new MyBinder();
// Set this field whenever you receive data from the cloud.
private ArrayList<MyDataType> latestCloudData;
public class MyBinder extends Binder {
public ArrayList<MyDataType> getLatestCloudData() {
return MyService.this.latestCloudData;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Bind and unbind to your service in your activity's onStart and onStop, respectively, by providing an implementation of ServiceConnection:
public class MyActivity extends Activity {
// Provides an interface for communicating with the service.
MyBinder mMyBinder = null;
boolean mBound = false;
#Override
protected void onStart() {
super.onStart();
// Bind to service
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder binder) {
// Successfully bound, cast the IBinder to a MyBinder.
MyBinder myBinder = (MyBinder) binder;
// Can now use the MyBinder instance to communicate with the service.
MyActivity.this.mMyBinder = myBinder;
mBound = true;
// Use the MyBinder to get the latest cloud data from the service and update your view.
ArrayList<MyDataType> cloudData = MyActivity.this.mMyBinder.getLatestCloudData();
MyActivity.this.updateViewWithCloudData(cloudData);
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private void updateViewWithCloudData(ArrayList<MyDataType> data) {
// Use the data to update your view here...
}
}
See the Android Developer Guide for more information about Bound Services (the example above is a modified version of the example found there).
Also note that this will only help you when your activity moves from the background to the foreground (or is recreated). If you also want to receive updates while the activity is in the foreground, you should also perform a broadcast which your activity should listen for. However, you do not need to bundle the cloud data as part of this broadcast. The broadcast can simply be a simple notification prompting the activity to query the service for the new data using MyBinder.getLatestCloudData().
Related
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?
I've read the documentation about services and many examples on the web. However, most examples just include the same code and I still don't understand the life cyle of a background service completely.
Here is what I'm trying to do:
Start Activity
Activity starts a service, receiving location data
Exit Activity
Service keeps collecting
Start Activity
Bind to service and do some stuff, e.g. display some results
And this is what I've done to achive it:
Implemented a service (not an IntentService)
tried to start it in two ways:
Start the service by binding to it:
bindService(intent, myLocationService, Context.BIND_AUTO_CREATE);
This results in the service beeing created when the activity starts and beeing destroyed, when I call unbindService(...), e.g. in the onStop() method of the activity.
Start the service by creating it, explicitly:
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
This results in the service beeing created when the activity starts. When exiting the activity, the services crashes (although, I've implemented all cleaning up) and gets started again. When starting the activity again, the services starts once more, too.
So, how can I start, bind and unbind a serivce gracefully, without all that crashing and restarting behaviour?
These are the relevant code lines of the service:
public class MyLocationService extends Service {
final IBinder myServiceBinder = new MyServiceBinder();
public static class MyServiceConnection implements ServiceConnection {
MyLocationService service;
boolean bound = false;
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MyServiceBinder binder = (MyServiceBinder)iBinder;
service = binder.getService();
bound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
}
public MyLocationService getService() {
return service;
}
public boolean isBound() {
return bound;
}
}
/**
* Binder for GPSService
*/
public class MyServiceBinder extends Binder {
MyLocationService getService() {
return MyLocationService.this;
}
}
public MyLocationService() {
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return myServiceBinder;
}
}
These are the relevant code lines of the activity:
public class MyMainActivity extends AppCompatActivity {
MyLocationService.MyServiceConnection myServiceConnection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, MyLocationService.class);
bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
unbindService(myServiceConnection);
}
#Override
protected void onStop() {
super.onStop();
}
}
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Depending on what your requirements are, should be onPause() onResume() or onStart() and onStop().
If you want the service to run in the background you could use
/**
* Called when all clients have disconnected from a particular interface
* published by the service. The default implementation does nothing and
* returns false.
*
* #param intent The Intent that was used to bind to this service,
* as given to {#link android.content.Context#bindService
* Context.bindService}. Note that any extras that were included with
* the Intent at that point will <em>not</em> be seen here.
*
* #return Return true if you would like to have the service's
* {#link #onRebind} method later called when new clients bind to it.
*/ public boolean onUnbind(Intent intent) {
return false; }
onRebind() to do something.
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()
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().
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.