I have a DialogFragment that can be launched from anywhere, let’s call it UploadDialogFragment. This fragment allows the user to accomplish two related tasks:
Upload an image (can take up to 1min)
Upload a JSON object with some text and a reference to the saved image
This two tasks need to be accomplished in sequence - you can’t do 2. without having completed 1.. So what really happens is:
I start uploading the image (1.)
Meanwhile, the user writes the text and adds other info
When all is ready, dismiss the dialog and start the second task (2.).
I used to do this with background tasks, but now I’d like to switch to a Service: the whole operation should be completed even if, after dismissing, I force quit the app.
Current design
In my experience I have always used IntentService, so I am a complete newbie. The current flawed design I am moving forward is something like:
public class UploadService extends Service {
private final Binder binder = new Binder();
public class Binder extends android.os.Binder {
UploadService getService() {
return UploadService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public void completeFirstTask() {
...
}
public void completeSecondTask() {
// wait for first task to complete if necessary...
...
stopSelf();
}
}
And here’s my UploadDialogFragment:
public class UploadDialogFragment extends AppCompatDialogFragment implements
ServiceConnection {
private UploadService uploadService;
private boolean boundService;
#Override
public void onServiceDisconnected(ComponentName name) {
uploadService = null;
boundService = false;
}
private void bindService() {
Intent i = new Intent(getActivity().getApplicationContext(), UploadService.class);
getActivity().getApplicationContext().bindService(i, this, Context.BIND_AUTO_CREATE);
boundService = true;
}
private void unbindService() {
if (boundService) {
getActivity().unbindService(this);
boundService = false;
}
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
uploadService = ((UploadService.Binder) service).getService();
uploadService.completeFirstTask();
}
// THEN, LATER:
// OnClick of a button, I call uploadService.completeSecondTask();
// and this.dismiss();
}
This is deeply flawed right now.
I need to reliably unbind() when the dialog fragment is closed/dismissed/recreating itself, otherwise I am going to leak it because of the ServiceConnection (right?). I don’t know when to do it?. onDismiss, onDestroyView, onSaveInstanceState ... I have tried many options but I often end up with a IllegalArgumentException saying that the service connection is not registered.
The service might never reach the completeSecondTask() part, and so no one is going to stop it, leaking it for no reason. I should probably call stopService() somewhere, but where? These are different scenarios:
I force-quit the app / recreate the fragment after a completeSecondTask() call: the Service should keep going until it ends.
I recreate the fragment without having called completeSecondTask() : the Service should keep going until it ends. (There’s proper logic inside my fragment to handle this)
I force-quit the app without having called completeSecondTask() : the Service should stop.
Questions
Now, you might see this as two questions: how to handle unbind(), and how to handle stopService().
However, because I am finding so hard to set up this little task, I am thinking that this is deeply flawed and I should use a totally different approach. I hope you can shed some light on this for me.
I have a Service like this (this is not the actual Service, it's just for describing my problem).
public class UploadService {
private BlockingQueue<UploadData> queue = null;
private UploadInfoReceiver receiver = null;
public void onStart(...) {
queue = new LinkedBlockingQueue<UploadData>();
(new Processor()).start();
// creating and reigtering receiver
}
public void onDestroy() {
queue.add(new ServiceDestroyedData());
// unregistering the receiver
}
private class Processor extends Thread() {
public void run() {
while (true) {
UploadData data = queue.take();
if (data instanceof ServiceDestroyedData) {
return;
}
// processing data
}
}
}
private class UploadInfoReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
queue.add(new UploadData(/* getting data from intent */));
}
}
}
And my problem is that if I do something like this in my App:
if (!isUploadServiceRunning()) {
// start the Service
}
Then it starts the Service, but when I move my App to the background and open task manager (android 4.2.2), and kill the app, Android restart my Service, and I can see that it creates a whole new instance of it, and I can see that onDestroy never gets called for the previous Service instance. And I also can see that the instance of the previous Processor Thread is no longer running. How can this be? If onDestroy never gets called how does Android know that it should stop my Thread?
Thanks for your answers.
Android will kill off anything that it finds that is attached to your apps classloader when you select force stop from the menu. Think kill -9 on Linux. There will be no nice callbacks to any onDestroy methods, the system will just end everything.
Now for your service:
while(true) should really NEVER be used. It will instantly kill the battery and will not do any work 99% of the time anyway.
You area already using a receiver, you can just put your while logic into there and once the upload is done call the next upload and so on. There is absolutely no need for the loop.
I have got an Android service that could be unavailable at some point.
If my Activity tries to call it, I want to know if the service is available.
My activity code is:
boolean bound = context.bindService(new Intent(SERVICE_NAME), mConnection, Context.BIND_AUTO_CREATE);
if (!bound) {
// ....
}
My service code is:
#Override
public void onCreate() {
mServiceEnabled = false;
// calling some C code
boolean readEnabled = JniCommunicator.enableRead();
if (!readEnabled) {
return;
}
// potentially return without reaching that point
mServiceEnabled = true;
}
public IBinder onBind(Intent intent) {
Log.d("Service", mServiceEnabled ? "enabled" : "DISABLED");
return mServiceEnabled ? serviceBinder : null;
}
As I understood, returning null would not bind the service, and my activity would be able to see it, but my bound variable is always true. And my mConnection callback is never called.
What is wrong?
Note that the service and the activity are in two separate APKs signed with two separate keys. I cannot use a static boolean to solve that.
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.
I have an Activity calling a Service defined in IDownloaderService.aidl:
public class Downloader extends Activity {
IDownloaderService downloader = null;
// ...
In Downloader.onCreate(Bundle) I tried to bindService
Intent serviceIntent = new Intent(this, DownloaderService.class);
if (bindService(serviceIntent, sc, BIND_AUTO_CREATE)) {
// ...
and within the ServiceConnection object sc I did this
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w("XXX", "onServiceConnected");
downloader = IDownloaderService.Stub.asInterface(service);
// ...
By adding all kinds of Log.xx I found that the code after if(bindService(...)) actually goes BEFORE ServiceConnection.onServiceConnected is being called - that is, when downloader is still null - which gets me into trouble. All the samples in ApiDemos avoid this timing problem by only calling services when triggered by user actions. But what should I do to right use this service after bindService succeeds? How can I wait for ServiceConnection.onServiceConnected being called reliably?
Another question related. Are all the event handlers: Activity.onCreate, any View.onClickListener.onClick, ServiceConnection.onServiceConnected, etc. actually called in the same thread (mentioned in the doc as the "main thread")? Are there interleaves between them, or Android would schedule all events come into being handled one-by-one? Or, When exactly is ServiceConnection.onServiceConnected actually going to be called? Upon completion of Activity.onCreate or sometime when A.oC is still running?
How can I wait for
ServiceConnection.onServiceConnected
being called reliably?
You don't. You exit out of onCreate() (or wherever you are binding) and you put you "needs the connection established" code in onServiceConnected().
Are all the event handlers:
Activity.onCreate, any
View.onClickListener.onClick,
ServiceConnection.onServiceConnected,
etc. actually called in the same
thread
Yes.
When exactly is
ServiceConnection.onServiceConnected
actually going to be called? Upon
completion of Activity.onCreate or
sometime when A.oC is still running?
Your bind request probably is not even going to start until after you leave onCreate(). Hence, onServiceConnected() will called sometime after you leave onCreate().
I had the same problem. I didn't want to put my bound service dependent code in onServiceConnected, though, because I wanted to bind/unbind with onStart and onStop, but I didn't want the code to run again every time the activity came back to the front. I only wanted it to run when the activity was first created.
I finally got over my onStart() tunnel vision and used a Boolean to indicate whether this was the first onServiceConnected run or not. That way, I can unbindService in onStop and bindService again in onStart without running all the start up stuff each time.
I ended up with something like this:
1) to give the auxiliary stuff some scope, I created an internal class. At least, the ugly internals are separated from the rest of the code. I needed a remote service doing something, therefore the word Something in class name
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
//...
}
2) there are two things necessary to invoke a remote service method: the IBinder and the code to execute. Since we don't know which one becomes known first, we store them:
private ISomethingService mISomethingService;
private Runnable mActionRunnable;
Each time we write to one of these fileds, we invoke _startActionIfPossible():
private void _startActionIfPossible() {
if (mActionRunnable != null && mISomethingService != null) {
mActionRunnable.run();
mActionRunnable = null;
}
}
private void performAction(Runnable r) {
mActionRunnable = r;
_startActionIfPossible();
}
This, of course, assumes that the Runnable has access to mISomethingService, but this is true for runnables created within the methods of the RemoteSomethingHelper class.
It is really good that the ServiceConnection callbacks are called on the UI thread: if we are going to invoke the service methods from the main thread, we do not need to care about synchronization.
ISomethingService is, of course, defined via AIDL.
3) Instead of just passing arguments to methods, we create a Runnable that will invoke the method with these arguments later, when invocation is possible:
private boolean mServiceBound;
void startSomething(final String arg1) {
// ... starting the service ...
final String arg2 = ...;
performAction(new Runnable() {
#Override
public void run() {
try {
// arg1 and arg2 must be final!
mISomethingService.startSomething(arg1, arg2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
4) finally, we get:
private RemoteSomethingHelper mRemoteSomethingHelper = new RemoteSomethingHelper();
class RemoteSomethingHelper {
private ISomethingService mISomethingService;
private Runnable mActionRunnable;
private boolean mServiceBound;
private void _startActionIfPossible() {
if (mActionRunnable != null && mISomethingService != null) {
mActionRunnable.run();
mActionRunnable = null;
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
// the methods on this class are called from the main thread of your process.
#Override
public void onServiceDisconnected(ComponentName name) {
mISomethingService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mISomethingService = ISomethingService.Stub.asInterface(service);
_startActionIfPossible();
}
}
private void performAction(Runnable r) {
mActionRunnable = r;
_startActionIfPossible();
}
public void startSomething(final String arg1) {
Intent intent = new Intent(context.getApplicationContext(),SomethingService.class);
if (!mServiceBound) {
mServiceBound = context.getApplicationContext().bindService(intent, mServiceConnection, 0);
}
ComponentName cn = context.getApplicationContext().startService(intent);
final String arg2 = ...;
performAction(new Runnable() {
#Override
public void run() {
try {
mISomethingService.startSomething(arg1, arg2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
context is a field in my class; in an Activity, you can define it as Context context=this;
I did not need queuing actions; if you do, you can implement it.
You likely will need a result callback in startSomething(); I did, but this is not shown in this code.
I did something similar before, the only different is I was not binding to service, but just starting it.
I would broadcast an intent from the service to notify the caller/activity about it is started.
I wanted to add some things you should or should not do:
bind the service not on create but onResume and unbind it onPause. Your app can go into pause (background) at any time by user interaction or OS-Screens.
Use a distinct try/catch for each and every service unbinding, receiver unregistering etc in onPause so if one is not bound or registered the exception doesn't prevent the others from being destroyed too.
I usually capsule binding in a public MyServiceBinder getService() Method. I also always use a blocking boolean variable so I don't have to keep an eye on all those calls using the servie in the activity.
Example:
boolean isBindingOngoing = false;
MyService.Binder serviceHelp = null;
ServiceConnection myServiceCon = null;
public MyService.Binder getMyService()
{
if(serviceHelp==null)
{
//don't bind multiple times
//guard against getting null on fist getMyService calls!
if(isBindingOngoing)return null;
isBindingOngoing = true;
myServiceCon = new ServiceConnection(
public void onServiceConnected(ComponentName cName, IBinder binder) {
serviceHelp = (MyService.Binder) binder;
//or using aidl: serviceHelp = MyService.Stub.AsInterface(binder);
isServiceBindingOngoing = false;
continueAfterServiceConnect(); //I use a method like this to continue
}
public void onServiceDisconnected(ComponentName className) {
serviceHelp = null;
}
);
bindService(serviceStartIntent,myServiceCon);
}
return serviceHelp;
}
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.");
}
This allows to bind to the service in a thread and wait until it is connected. E.g. stub:
private final AtomicBoolean connected = new AtomicBoolean()
private final Object lock = new Object();
...
private void myConnectMethod() {
// bind to service
ExecutorService executorService = Executors.newSingleThreadExecutor();
context.bindService(new Intent(context, MyServiceClass.class), Context.BIND_AUTO_CREATE, executorService, new
ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
synchronized (lock) {
// TODO: store service instance for calls in case of AIDL or local services
connected.set(true);
lock.notify();
}
});
synchronized (lock) {
while (!connected.get()) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException();
}
}
}
}
It is also necessary to run the service in a separate process:
<service
android:name=".MyServiceClass"
android:process=":service"
android:enabled="true"
android:exported="true" />
I figured out that these workarounds are only worth the effort and the wait only if your bound services are running in a different process than your application's main process.
For accessing data and methods in the same process (or application), I ended up implementing singleton classes. If the classes need a context for some methods, I leak the application context to the singleton classes. There is, of course, a bad consequence of it as it breaks the "instant run". But that is an overall better compromise, I think.
*The basic idea is same with #18446744073709551615, but I will share my code as well.
As a answer of main question,
But what should I do to right use this service after bindService succeeds?
[Original expectation (but not work)]
wait until service connected like below
#Override
protected void onStart() {
bindService(service, mWebServiceConnection, BIND_AUTO_CREATE);
synchronized (mLock) { mLock.wait(40000); }
// rest of the code continues here, which uses service stub interface
// ...
}
It won't work because both bindService() in onCreate()/onStart() and onServiceConnected() is called at same main thread.
onServiceConnected() is never called before wait finishes.
[Alternative solution]
Instead of "wait", define own Runnable to be called after Service Connected and execute this runnable after service connected.
Implement custom class of ServiceConnection as follows.
public class MyServiceConnection implements ServiceConnection {
private static final String TAG = MyServiceConnection.class.getSimpleName();
private Context mContext = null;
private IMyService mMyService = null;
private ArrayList<Runnable> runnableArrayList;
private Boolean isConnected = false;
public MyServiceConnection(Context context) {
mContext = context;
runnableArrayList = new ArrayList<>();
}
public IMyService getInterface() {
return mMyService;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "Connected Service: " + name);
mMyService = MyService.Stub.asInterface(service);
isConnected = true;
/* Execute runnables after Service connected */
for (Runnable action : runnableArrayList) {
action.run();
}
runnableArrayList.clear();
}
#Override
public void onServiceDisconnected(ComponentName name) {
try {
mMyService = null;
mContext.unbindService(this);
isConnected = false;
Log.v(TAG, "Disconnected Service: " + name);
} catch(Exception e) {
Log.e(TAG, e.toString());
}
}
public void executeAfterServiceConnected(Runnable action) {
Log.v(TAG, "executeAfterServiceConnected");
if(isConnected) {
Log.v(TAG, "Service already connected, execute now");
action.run();
} else {
// this action will be executed at the end of onServiceConnected method
Log.v(TAG, "Service not connected yet, execute later");
runnableArrayList.add(action);
}
}
}
And then use it in the following way (in your Activity class or etc),
private MyServiceConnection myServiceConnection = null;
#Override
protected void onStart() {
Log.d(TAG, "onStart");
super.onStart();
Intent serviceIntent = new Intent(getApplicationContext(), MyService.class);
startService(serviceIntent);
myServiceConnection = new MyServiceConnection(getApplicationContext());
bindService(serviceIntent, myServiceConnection, BIND_AUTO_CREATE);
// Instead of "wait" here, create callback which will be called after service is connected
myServiceConnection.executeAfterServiceConnected(new Runnable() {
#Override
public void run() {
// Rest of the code comes here.
// This runnable will be executed after service connected, so we can use service stub interface
IMyService myService = myServiceConnection.getInterface();
// ...
}
});
}
It worked for me. But there may be more better way.