I'm trying to bind a service, but onBind() always returns false.
This is the code for the ServiceConnection-
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with our service has been established,
// giving us the service object we can use to interact with our service.
mBoundService = ((ScheduleService.ServiceBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mBoundService = null;
}
};
This is call to bindService() -
boolean test = getApplicationContext().bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
This is the declaration of the service in the Manifest -
<service android:name=".Notifications.ScheduleService" android:enabled="true"/>
I've read previous questions regarding the subject and couldn't find an answer(tried switching the Activity context with the Application context, but it didn't help).
I'm using using Frgaments and ActionBarSherlock, and my Activity extends SlidingFragmentActivity (That's why i'm using the application context, which doesn't help).
Edit - This is the code of the service i'm trying to start -
public class ScheduleService extends Service {
/**
* Class for clients to access
*/
public class ServiceBinder extends Binder {
public ScheduleService getService() {
return ScheduleService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("ScheduleService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly stopped, so return sticky.
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
private final IBinder mBinder = new ServiceBinder();
/**
* Show an alarm for a certain date when the alarm is called it will pop up a notification
*/
public void setAlarm(Calendar c) {
// This starts a new thread to set the alarm
// You want to push off your tasks onto a new thread to free up the UI to carry on responding
new AlarmTask(this, c).run();
}
}
Any help will be appreciated . Thanks.
What is the fully qualified class-name of ScheduleService (i.e. including the full package-name)?
I'm asking this, because in your AndroidManifest.xml file, your service's name is .Notifications.ScheduleService, which seems a bit odd:
This tells me that either
The (last part of the) package-name contains a upper-case
character... not so good.
I would expect .notifications.ScheduleService instead, if this is the case.
The ScheduleService is defined within a file called Notifications.java.
I would expect .Notifications$ScheduleService instead, if this is the case (dollar sign instead of period).
Do you mean bindService() returns false? onBind() returns IBinder type.
Keep in mind that service binding takes some time. If you want to perform some action after binding is done you can perform it in the onServiceConnected() method.
public void onServiceConnected(ComponentName className, IBinder service) {
mBoundService = ((ScheduleService.ServiceBinder) service).getService();
Calendar c = new Calendar();
mBoundService.setAlarm(c);
}
If you need more guidance on this you need to show us your Activity code.
Why do you use the application context to bind the service?
The method bindService is called through the ContextWrapper. It might not be the issue but I'd share contexts across the place where you bind the service and where you have the connection.
In your case instead of
boolean test = getApplicationContext().bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
I'd do the following
boolean test = bindService(new Intent(this, ScheduleService.class), mConnection, Context.BIND_AUTO_CREATE);
Or if you want to keep a global context within the application, move everything to your Application file and call it similarly the same way suggested above.
The issue can also be on the package name of your app and the declaration of your service in your manifest. If you are unsure make sure to give the global route to your service in the manifest.
Related
What I want to do is send a logout request to my backend server when the user quits my app (can happen during any Activity, there are many).
It seems like I cannot use OnStop() as I have a gallery chooser and camera Intent and when these are started, OnPause and OnStop are called. I need a way to definitively know that the app has closed.
I've read up about using a Service/BroadcastReceiver or even a LocalBroadcastManager, or possibly tying the request to when the home button has been clicked.
I can't check if the application has been sent to background as this would be true for a camera/gallery Intent starting as well as the app being sent to background. I've also tried checking the package name of the activity being started but this may be variable on other devices (e.g. gallery might have a different package name).
Any advice/direction is much appreciated.
Edit: I've discovered there isn't really a way to intercept a home button press. Still looking for a solution!
I had a similar problem like this,I used a Service to solve my problem.This is what I have done
In main activity
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder binder) {
((KillingNotificationBar.KillBinder) binder).service.startService(new Intent(
Main.this, KillingNotificationBar.class));
}
public void onServiceDisconnected(ComponentName className) {
}
};
bindService(new Intent(Main.this,
KillingNotificationBar.class), mConnection,
Context.BIND_AUTO_CREATE);
KillingNotificationBar class
public class KillingNotificationBar extends Service {
private final IBinder mBinder = new KillBinder(this);
public class KillBinder extends Binder {
public final Service service;
public KillBinder(Service service) {
this.service = service;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onCreate() {
you will know if the activity is destroyed
}
}
add this in your manifest
<service android:name=".services.KillingNotificationBar"/>
Note
It will take 1-5 seconds to get executed.
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
I have a background Service that need to be running even if the application gets killed by Android. This is currently working perfectly.
My problem is that when I restart the application (with the background service still running), I want my Activity to bind to the service to have access to some of its methods. When I try to bind with a ServiceConnection, the onServiceConnected is never called.
final private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected"); //this is never called
MyBackgroundService.ServiceBinder binder = (MyBackgroundService.ServiceBinder) service;
backgroundService = binder.getService();
}
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected");
backgroundService = null;
}
};
private void bindBackgroundService(){
this.bindService(new Intent(this, MyBackgroundService.class), serviceConnection, Context.BIND_AUTO_CREATE);
}
Am I doing this the wrong way? Is it better to stop the Service and restart it?
Since the class that binded the background service is a singleton and my alarm broadcast receiver that is making sure the background service is always running instantiates this singleton, I had access to this singleton and I was trying to bind to the service that was already binded.
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
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.