Let's say that we want to create an app with some heavy background work, e.g. messenger or
image gallery. For messenger background work is to communicate with remote service
(send and receive messages), for image gallery - to download images (as we know from ICS
this work must not be done on main app thread as it will cause crash).
How can we implement this? Obvious solutions are:
- Using android.os.AsyncTask
- Using android.app.Service
- Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Of course the experienced developer immediately says: AsyncTask is not designed for long operations. And he
is correct, here is the
part of documentation for AsyncTask (source):
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
OK, first solution is bad, what else do we have:
- Using android.os.AsyncTask
- Using android.app.Service
- Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Let's read documentation for android.app.Service (source):
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use
That's what we were looking for - long running operations in background. Cool, let's code:
public final class DownloadImageService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// initialization logic
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
// start logic
}
}
Let's say we need to load some data (e.g. images from remote server):
@Nonnull
private static final String EXTRA_URL = "url";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int result = super.onStartCommand(intent, flags, startId);
downloadImage(intent.getStringExtra(EXTRA_URL));
return result;
}
private void downloadImage(@Nonnull String url) {
// downloading
}
Compile, package, start - bump - android.os.NetworkOnMainThreadException.
WTF?
It turns out that Android service is working on the main application thread (source):
Note that services, like other application objects, run in the main thread of their hosting process.
OK, then we need to create a manual background thread management class and use it inside the service...
Stop. But why do we need service after all?
It's much easier to communicate with local class, we can use callbacks/listeners with local class and we
cannot use it all with service.
Service binding takes some time and if we want to use local service we need to wait until it will be bound
which makes code less clean.
Service only accepts input which is parcelable (as we need to put in into Intent (for remote service) or use
AIDL (for local service)).
And we've come to a conclusion that only one right way of implementing background task is to create own
solution
(as Android doesn't provide it out of the box):
- Using android.os.AsyncTask
- Using android.app.Service
- Writing own solution - manual thread management (Threads, ExecutorServices, etc)
Summarizing everything above:
- Android Service doesn't help you to create background task
- Most probably you don't need Service at all
- Sometimes documentation is misleading and you should not always rely on it