Android service running after pressing Home key - android

I have an Android service, created in OnCreate of first Activity of the application using StartService(). I need this service to be running throughout the life span of the application ie, all the activities in the application. But the service should not consume the resources after user pressed Home key or Back button. Is there any elegant way to do that other than stopping the service in onPause() method of all the activities?

Instead of using StartService, you can call bindService in onResume and unbindService in onPause. Your service will stop when there are no open bindings.
You'll need to create a ServiceConnection to get access to the service. For instance, here's a class nested inside MyService:
class MyService {
public static class MyServiceConnection implements ServiceConnection {
private MyService mMyService = null;
public MyService getMyService() {
return mMyService;
}
public void onServiceConnected(ComponentName className, IBinder binder) {
mMyService = ((MyServiceBinder)binder).getMyService();
}
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
}
// Helper class to bridge the Service and the ServiceConnection.
private class MyServiceBinder extends Binder {
MyService getMyService() {
return MyService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return new MyServiceBinder();
}
#Override
public boolean onUnbind(Intent intent) {
return false; // do full binding to reconnect, not Rebind
}
// Normal MyService code goes here.
}
One can use this helper class to get access to the service via:
MyServiceConnection mMSC = new MyService.MyServiceConnection();
// Inside onResume:
bindService(new Intent(this, MyService.class), mMSC, Context.BIND_AUTO_CREATE);
// Inside onPause:
unbindService(mMSC);
// To get access to the service:
MyService myService = mMSC.getMyService();

You could do what Darrell suggests but put that code in a new class that extends Activity and then extend that on all your normal Activities.
I don't know any other more elegant way of achieving your goals.

Related

Return a different Binder from already bound service

I have a Service which is already bound by an external App via AIDL.
However, there are some service requests which require to start an Activity.
Since I cannot call startActivityForResult from within a Service I decided to Bind my local Activities to the service as well.
(PseudoCode) looks like this:
class MyService extends Service{
public IBinder onBind(Intent intent){
if (intent.hasExtra("LocalBindingRequest")){
return getLocalBinder();
else {
return getAidlBinder();
}
}
}
class ExternalApp extends Activity{
void someFunc(){
Intent i = new Intent(new ComponentName("com.my.pkg", "com.my.pkg.MyService");
bindService(i, myServiceConnection, Context.BIND_AUTO_CREATE);
}
}
class InternalApp extends Activity{
MyService mService;
void someFunc(){
Intent i = new Intent(new ComponentName("com.my.pkg", "com.my.pkg.MyService")
.putExtra("LocalBindingRequest", true);
bindService(i, myServiceConnection, Context.BIND_AUTO_CREATE);
}
public void onServiceConnected(ComponentName cn, IBinder service){
InternalBinder ib = (LocalBinder)service;
mService = ib.getService();
}
}
Flow is like this:
ExternalApp binds to AidlBinder
ExternalApp calls Function which requires Service to start an Activity
Service starts Activity
Internal Activity tries to Bind
I get an Exception (appearantly without hitting a breakpoint in onBind or onServiceConnected)
java.lan.ClassCastException: AidlService cannot be cast to InternalBinder
Isn't it possible for a Service to return a different Binder?
If not, what can I do, to propagate a Result back to MyService which is already bound?
Ok I should have read the docs stating in onBind(Intent)
Intent: The Intent that was used to bind to this service, as given to
Context.bindService. Note that any extras that were included with the
Intent at that point will not be seen here.
Thats why I was given the Aidl Service. The fix would be:
class InternalApp extends Activity{
MyService mService;
void someFunc(){
Intent i = new Intent(new ComponentName("com.my.pkg", "com.my.pkg.MyService");
i.setAction("LocalBindingRequest");
bindService(i, myServiceConnection, Context.BIND_AUTO_CREATE);
}
public void onServiceConnected(ComponentName cn, IBinder service){
InternalBinder ib = (LocalBinder)service;
mService = ib.getService();
}
}
class MyService extends Service{
public IBinder onBind(Intent intent){
if ("LocalBindingRequest".equals(intent.getAction()){
return getLocalBinder();
else {
return getAidlBinder();
}
}
}
And we can have separate Binders for each binding request

Why a Null Pointer when trying to use Android Local Bound Service?

In the Service class:
class AeroBluetoothService extends Service { ...
private final IBinder asBleBinder = new LocalBinder();
public class LocalBinder extends Binder {
AeroBluetoothService getService() {
return AeroBluetoothService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return asBleBinder;
}
In the client Activity:
Intent bindAsBleIntent;
#Override
public void onServiceConnected( ComponentName className, IBinder service ) {
AeroBluetoothService.LocalBinder asBleBinder = (AeroBluetoothService.LocalBinder) service;
asBleServiceRef = asBleBinder.getService();
}
In Client's onCreate():
bindAsBleIntent = new Intent( getApplicationContext(), AeroBluetoothService.class );
bindService( bindAsBleIntent, /*ServiceConnection*/ this, Context.BIND_AUTO_CREATE );
The problem is that when I try to call a Service method from the Client:
asBleServiceRef.scanForAero();
the reference to the Service instance, asBleServiceRef, is null. It is as though the onServiceConnected() callback is not being called (or is passing a null argument).
I copied this code quite carefully from an Android example. I just noticed that the example calls bindService() from its onStart() method, whereas I'm calling from onCreate(). Could that make any difference? What's the problem?
As #avinash correctly saw, the problem was simply neglecting to declare the Service in the Manifest.

Interaction between IntentService and Activity - Android

In my application I am using an IntentService to download a file from a cloud. And showing the progress in NotificationManager. I need to show the status (Downloading/Completed or Failed) in the Activity which stared the IntentService too.
My problem is once I closed the app and open it back, I want to get the status of downloading from IntentService.
Which is the best way to do this?
You can let your Activity bind to your Service, by calling bindService() in your Activity. As per the documentation:
A service is "bound" when an application component binds to it by
calling bindService(). A bound service offers a client-server
interface that allows components to interact with the service, send
requests, get results, and even do so across processes with
interprocess communication (IPC). A bound service runs only as long as
another application component is bound to it. Multiple components can
bind to the service at once, but when all of them unbind, the service
is destroyed.
Also:
You should create a bound service when you want to interact with the
service from activities and other components in your application or to
expose some of your application's functionality to other applications,
through interprocess communication (IPC).
The documentation provides a fully functional example of this. Below is taken from the provided link.
Service class:
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);
}
}
Activity class:
public class BindingActivity extends Activity {
LocalService 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 LocalService
Intent intent = new Intent(this, LocalService.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 the LocalService.
// 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 LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
In your Service, you can define public methods that your Activity can call, such as polling for your download progress. Please refer to the documentation for explanation in detail.
There are couple of ways to have communication connection between Service and Activity. I would suggest these 2
First, you can use the great library Otto. With Otto, you can also have #Produce annotated method. With this method you will return the latest information about the download. When you #Subscribe in your Activity you will get the latest info immediately. https://github.com/square/otto
If you are using Android built-in DownloadManager it returns the updates and results with a Broadcast, you can register to that Broadcast both in your Service and Activity. This way you will be able to update both of them. I suggest you to use DownloadManager, it is awesome.
http://developer.android.com/reference/android/app/DownloadManager.html

How to make a started service continue after the last bound services un-binds

I would like to have a service (doing occasional monitoring) be active continuously. I plan to start it by listening to a BOOT_COMPLETE, which I believe makes it a "Started Service". I want a UI application to bound to it, which is working and documented. However, after the binding activity is destroyed, the Service dies because it's "unbound from all clients".
Is there a way to have a started service allow binding and still continue after the last bound services un-binds?
Returning true from onUnbind() wouldn't help, as the service should continue to be active even if no additional binder exist.
In Android, services are started in one of two ways - through the startService(Intent i) method, or the bindService(Intent i). The method used to start the service determines whether it is started or bound. A service can be started, then bound to a client - or bound and then have calls to start sent to it (it doesn't restart if already started).
As you mention listening for BOOT_COMPLETE, I assume this is an action for an Intent, which is sent via a Broadcast object. This means that you can create an IntentFilter object with the BOOT_COMPLETE action added to it via the addAction(String action) method. Then a BroadcastReceiver object can be created, which upon receiving an intent with an action of BOOT_COMPLETE can then call the startService(Intent i) method (this is done by overriding the onReceive(Context context, Intent intent) method).
If you call startService(Intent i) when the Intent is received, then the service will be a started service. This means that it will only stop when a call to stopService(Intent i) is made by the app, or if the service calls the stopSelf() method. It can be bound and unbound from by multiple activities during the time it is running, and it will not stop (as it is started, not bound).
Here is an example of this, using two Activity objects and a Service:
Activity 1 (first activity of your app):
public class ServiceActivity extends Activity {
private IntentFilter filter = new IntentFilter();
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action.equals(BOOT_COMPLETE) {
startService(new Intent(ServiceActivity.this, MyService.class));
}
}
};
#Override
protected void onStart() {
super.onStart();
filter.addAction(BOOT_COMPLETE);
registerReceiver(receiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(receiver);
}
//Some other code
}
Activity 2 (used at some point after activity 1):
public class AnotherActivity extends Activity {
private MyService service;
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
service = ((MyService.MyBinder)service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), connection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
//Some other code
}
Service:
public class MyService extends Service {
private MyBinder binder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
//Some other code
final class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
Final notes
To be able to use the service as bound, you need to override the onBind(Intent intent) method, and return an instance of binder MyBinder. Not doing so will result in not being able to bind (the binding sets the service variable by using the getService() method defined in MyBinder).
The BroadcastReceiver must alwasy be unregistered when the Activity it's in closes, as it would be leaked otherwise. That is why in the example, I have registered and unregistered in the onStart() and onStop() methods respectively. Using onDestroy() to unregister is not recommended as it is not always called.
The MyService object that is used when binding must also be unbound when it's Activity closes, as it too can be leaked. It is set to null when onServiceDisconnected(ComponentName name) is called for garbage collecting.
Sources for further reading
https://developer.android.com/reference/android/content/BroadcastReceiver.html
https://developer.android.com/reference/android/app/Activity.html
https://developer.android.com/reference/android/app/Service.html
https://developer.android.com/reference/android/content/ServiceConnection.html

Android: Getting a variable from my running service

My activity starts a Service, and when I close my app, the service continues to run.
OK, that's right. But when I open my application again, in the activity, I need to know the value of a public variable defined on the running Service(class) that I've started previously.
How can I do that?
Thanks
If you are binding your Activity to the Service, you should have an implementation of the Binder interface in your Service, e.g.
public class ServiceBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
In your Activity, create a new ServiceConnection class which will be used to give you access to your Service:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mMyService = ((MyService.ServiceBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
Here the member variable mMyService will give you access to all public members of your Service class.
To create the connection, implement doBindService and doUnbindService in your Activity:
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
}
void doUnbindService() {
// Detach our existing connection.
unbindService(mConnection);
}
Hope this helps!
If you don't call unbindService, your activity still have connection to service and you can simply check the variable through the service's method.
You could use messenger.
As per android website
A messenger is reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by creating a Messenger pointing to a Handler in one process, and handing that Messenger to another process.

Categories

Resources