Binding Android service - android

A common approach for checking if you have bounded to a service in Android is keeping a boolean parameter such as mBound in your binding steps as described in Android developers guide. I noticed a problem in this Android reference tutorial and I think this approach is somehow a bad practice. Here is the code from Bound Services:
Here is a code block with language code as hint:
public class ActivityMessenger extends Activity {
Messenger mService = null;
boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
Ok, Here is the problem. Let's assume that for any reason OnStop() is called immediately after OnStart(). You can create this situation by placing a breakpoint in OnCreate() and wait for your phone screen goes dark that forces an application recreation and then continue application run.
If this situation happens, the mBount variable will be still false when you run OnStop(), because service connection's onServiceConnected() is not called yet. So you will not call unbindService() and you will have a service that you have binded and never unbinded.
Any suggestions for a better approach for Binding services? I wonder if calling unbideService() in any situation is enough or not(for example calling even though service is not binded right now).
Best Regards

I haven't tried this in practice, but it seems reasonable to set mBound = true already when calling bindService instead of in the callback. Actually, the documentation says that you should call unbindService even if bindService returned false (meaning you would never get any onServiceConnected call and mBound would never be true):
Note: If the method returns false, your client does not have a valid
connection to the service. However, your client should still call
unbindService(); otherwise, your client will keep the service from
shutting down when it is idle.
With this in mind it's clear that only setting mBound = true in onServiceConnected is not sufficient (or at least recommended). I would suspect that calling unbindService when not previously bound is a no-op, but that might need some confirmation. If so, setting mBound = true when calling bindService seems like a good approach.

Related

What is the advantage of binding to a Service to get its variables, as opposed to using SharedPrefs, or getting the variables directly?

I've got a service meant to sometimes run in the background - started with startService().
What is the advantage of binding to this service so as to get/set its variables, instead of - controversial, I know, but still - just accessing its public variables directly (e.g. myVar = mainService.itsVar), or using SharedPrefs to set and get the values?
Especially, what is the fastest, in terms of performance, based on the fact that the get interval would be roughly 3 seconds?
Advantage - You can access variables and run methods directly.
Disadvantage - allot of cross thread headache, not to mention unresolved crashes on google developer console.
How to do it correctly, I will give example of a foreground service, if it can be background then change only how to start it:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mContext.startForegroundService(startIntent)
} else {
mContext.startService(startIntent);
}
In the service you need to implement all binding methods:
private IBinder mBinder = new MyBinder();
public MainService() {
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
public class MyBinder extends Binder {
public MainService getService() {
return com.xxx.xxx.MainService.this;
}
}
In the activity you need to listen to connection:
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mBoundService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "connecting");
MainService.MyBinder myBinder = (MainService.MyBinder) service;
mBoundService = myBinder.getService();
mShouldUnbind.set(true);
}
};
If other activity or alarm can start your service you will need to check every second or so if the service was started by other and bind to it.
Important - service run on the same thread like the main/GUI thread - so if you have time consumption actions like camera or uploading - you will need to start a background thread. back communication can be made by calling the GUI/main thread.
on foreground service like mine - you need also to manage notifications otherwise the o.s. will kill the service after 5 seconds.
Code example:
if (mShouldUnbind.get() && mBoundService != null)
val = mBoundService.getTimestamp();
Edit I:
advantage:
I have a camera foreground service - I can set zoom values directly from the GUI thread since I have a handle to the service. the camera is running on the background thread in the service, but, since the preview is being drawn 30 times pro seconds - the zoom is set without any lag. you will never manage to do it in any other way.
there is a difference between bounded service and Unbound service, bounded services are bounded to an activity which binds it and will work only till bounded activity is alive. while a unbounded service will work till the completion even after activity is destroyed, see the below link
Can anybody explain what is difference between unbound and bound service in android

How to catch bindService failure

I have some foregraund IntentService which could be ran or stopped. I need to reflect its progress(current state which is quite complex) on the Activity if it is visible.
I did not find the reliable and good way to find out whether my IntentService is running and ready to be bound to my Activity.
If I do bindService it always returns true. In case the IntentService is not ran the onServiceConnected(as well as any other callbacks) is never called and my activity remains in the preparation state for always.
The only working solution I found was the static variable in my IntentService class which indicates the service is running. But I believe that is bad practice to use such approach and many people warn it could not work or be unpredictable for some cases.
QUESTION: How do developers of Android expect to handle the failure of bindService call?
And particular case: what to do in case if we are trying to bind to the service which is not running now or in some intermediate state(e.g. shutting down etc).
private UploadService.Binder mServiceBinder;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createService();
}
private void createService() {
if (mServiceBinder == null)
bindService(new Intent(this, UploadService.class),
mConnection,
Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceBinder = ((UploadService.Binder) service);
((UploadService.Binder) service).getService().setCallback(DashFragment.getInstance());
}
#Override
public void onServiceDisconnected(ComponentName name) {
mServiceBinder = null;
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mServiceBinder != null) {
unbindService(mConnection);
mServiceBinder = null;
}
}
If the mServiceBinder is not null means your service is successfully bounded to the activity else you have to call create service method.

How to implement a service in Xamarin android

using Xamarin and Mqtt and my mqtt client m2mqtt is unable to maintain a connection throughout the android lifecycle.
Activity life cycle will cause problems if your process needs to stay alive after the activity has paused or stopped (phone sleeps, user switches apps then comes back)
The idea of using a service is that it will remain intact regardless of where the activity is in the life cycle.
Can anyone show me how to implement a service.
OK,
It's very easy and really kind of cool.
I got the info I needed to figure this out here c# corner
a lot of times when I need a layman's description csharp corner isn't bad I used their example pretty close to how they portrayed it I just want to help you to understand how to use it
You need 3 classes
1. the service itself (we'll call it service)
2. service binder (serviceBinder)
3. service connection (serviceConnection)
I'll show you mine next (this is working), I was designing a bound service hence you will see in the names instead of service it's called boundService. However, after doing this I found out that a start service works exactly the same way you just start it different the classes are the same.
You need android.app.
So first we need the service it extends :Service (important)
using Android.App
[Service]
class MqttBoundService : Service
{
private MqttBoundServiceBinder binder;
public override void OnCreate()
{
base.OnCreate();
}
public override IBinder OnBind(Intent intent)
{
binder = new MqttBoundServiceBinder(this);
Toast.MakeText(this, "OnBind() method start from BoundService", ToastLength.Long).Show();
Toast.MakeText(this, "Bound Service is started", ToastLength.Long).Show();
return binder;
}
public override bool OnUnbind(Intent intent)
{
Toast.MakeText(this, "OnUnBind() Method Called from BoundService.cs", ToastLength.Long).Show();
return base.OnUnbind(intent);
}
public override void OnDestroy()
{
Toast.MakeText(this, "Bound Service Destroyed", ToastLength.Long).Show();
base.OnDestroy();
}
The toast is there just so you know whats happening in case your service is getting destroyed when you didn't want it too.
Ok now we need the service binder (Extends Binder important)
class MqttBoundServiceBinder : Binder
{
MqttBoundService service;
public MqttBoundServiceBinder(MqttBoundService service)
{
this.service = service;
}
}
now we need the connection and this is where the heavy lifting takes place. I am still new to this but it looks like you want to put the methods here that need to outlive the activity life cycle.
class MqttBoundServiceConnection : Java.Lang.Object, IServiceConnection
{
public Connection connect { get; private set; }
public void OnServiceConnected(ComponentName name, IBinder service)
{
Console.WriteLine("OnServiceConnected() Method called");
}
public void OnServiceDisconnected(ComponentName name)
{
Console.WriteLine("OnServiceDisConnected() Method called");
}
public Connection Connect()
{
connect = new Connection(Utility.data, Utility.cdata);
return connect;
}
}
in the method Connect I instantiate my Connection object the parameters here are security certs but that's not important. I also have a property with get; set; that I set to my object so I can access the mqtt connection in my OnRestart() and OnResume() functions of my activity.
I was having problems with resources being diverted by android before I did this and that doesn't happen now.
Not sure if it matters but this is my OnRestart()
if (serviceConnection.connect == null)
{
DoBindService();
connect = serviceConnection.Connect();
}
else
{
connect = serviceConnection.connect;
}
Oh I forgot how to tell you how to start it OK for me I am doing a bound service this means that after all the activities (or clients) that are using it are destroyed the service is destroyed.
So to start a bound service first create a class level variable for the connection (in the activity -- it is referenced in my OnStart() above). Also once you do this you can call the methods from this class (really cool)
MqttBoundServiceConnection serviceConnection = new mqttBoundServiceConnection();
Then we need a method in the activity called DoBindService
private void DoBindService()
{
var BoundServiceIntent = new Intent(this, typeof(MqttBoundService));
BindService(BoundServiceIntent, serviceConnection, Bind.AutoCreate);
}
in the OnCreate() for the activity add
DoBindService();
I hope this help someone it helped me once I figured it out :)

How to make sure the service binding as quick as possible?

I have two application A and B. A is an application. B is an service. when A started, I will bind B service. and call a function in B, and I will start different UI according the remote call result. since bindService returned immediately, at that moment, binding is not finished.
Is there a grace way to handle this case.
I would recommend displaying a ProgressBar while the service binds and initialize the rest of the UI or functionality in the onServiceConnected callback since there is now guarantee how quick a service will bind depending on system performance.
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
mBound = true;
// do UI and functionality initialization
// OR simply service dependent functionality
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};

Can bindService() be made to block?

I have an Android application that uses a Remote Service and I bind to it with bindService(), which is asynchronous.
The app is useless until the service is bound, so I would like to simply wait until the binding is finished before any Activity is started. Is there a way to have the service bound before onCreate() or onResume() is called? I think there might be a way to do the binding in Application. Any ideas?
Edit:
if in onCreate() I do this.
bindService(service, mWebServiceConnection, BIND_AUTO_CREATE);
synchronized (mLock) { mLock.wait(40000); }
The ServiceConnection.onServiceConnected doesn't get called for 40 seconds. It's clear that I have to let onCreate() return if I want the service to bind.
So it appears there's no way to do what I want.
Edit 2:
Android how do I wait until a service is actually connected? has some good commentary about what is going on in Android when binding a service.
You cannot have bindService() block. However, your ServiceConnection (2nd parameter to bindService) has callbacks to tell you when the service is connected and disconnected, so you can have other code block until your onServiceConnected() method unblocks it.
When I need to wait a service to be bound before doing something else I play with locks. Precisely, the ServiceConnection owns a lock object and exposes a waitUntilConnected method that block on the lock until a wake up signal. That notification is located in the onServiceConnected callback.
public class MyServiceConnection implements ServiceConnection {
private volatile boolean connected = false;
private Object lock = new Object();
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
connected = true;
synchronized (lock) {
lock.notifyAll();
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
connected = false;
}
public void waitUntilConnected() throws InterruptedException {
if (!connected) {
synchronized (lock) {
lock.wait();
}
}
}
}
So, for example, if an activity has to wait a service to be bound, it calls simply the waitUntilConnected method.
protected void onStart() {
super.onStart();
bindService(myServiceIntent, myServiceConnection, Context.BIND_AUTO_CREATE);
try {
myServiceConnection.waitUntilConnected();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I placed the waitUntilConnected method in onStart just as an example, but it has to be called in a different thread. I'd like to hear a more elegant way! :)
It seems that there is a way to do this. KeyChain.java and several Google-written classes uses a LinkedBlockingQueue to allow synchronously bind to a service.
For example, see the method called bind on this: https://github.com/android/platform_frameworks_base/blob/master/keystore/java/android/security/KeyChain.java
It seems to return the service object synchronously due to the use of blocking queue.
Unfortunately, as stated on the Android docs https://developer.android.com/reference/android/security/KeyChain.html, some methods throws InterruptedException, due to the taking of element from the queue that may be interrupted when waiting.
Android 10 has introduced a new bindService method signature when binding to a service to provide an Executor (which can be created from the Executors).
/**
* Same as {#link #bindService(Intent, ServiceConnection, int)} with executor to control
* ServiceConnection callbacks.
* #param executor Callbacks on ServiceConnection will be called on executor. Must use same
* instance for the same instance of ServiceConnection.
*/
public boolean bindService(#RequiresPermission #NonNull Intent service,
#BindServiceFlags int flags, #NonNull #CallbackExecutor Executor executor,
#NonNull ServiceConnection conn) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
See this Answer
bindService() cannot be made to block. That kind of defeats the whole purpose of a Service. You said that you whole UI consists of results from the service. I think you need to rethink your UI and populate it with some kind of intermediate representation that shows the user that the app is gathering data.

Categories

Resources