Search code examples
javaandroidmultithreadingandroid-asynctaskrunnable

Does a Runnable in AsyncTask block the service/activity? It does for me


I have a service that executes a huge time-consuming computation. So I added an AsyncTask to run it in background. I have the call to requestLocationUpdates() which apparently can't be executed in an AsyncTask without a Looper. Most suggested against using a Looper, for I don't know why. So finally I had to add a Runnable in the AsyncTask.doInBackground() with a Handler. The Runnable seems to be blocking the activity that calls this service. This does not happen when my application is minimised. In fact, another activity of my app is also momentarily blocked whenever the execution happens.

1. What exactly is happening when I call a Runnable on a Handler?

2. How do I make it really run in background?

    private void forkAsyncForTracking(){
    new AsyncTask<Void, Void, Void>(){
        private Location loc = null;
        @Override
        protected Void doInBackground(Void... params) {
            handler.post(new Runnable(){
                @Override
                public void run() {
                    loc = getLocation();//this blocks the activity
                }
            });
            return null;
        }

        @Override
        protected void onPostExecute(Void lol){
            doIt(loc);
        }
    }.execute();
}

Solution

  • By default, a service runs on 'main' thread. So if you declare a handler as a private to the service, it's considered as declared on main thread. Hence, the runnable declared inside the doInBackground will execute on main thread. Remember, it depends on the handler which is posting the runnable on where it'll be executed. You've to declare the handler inside the worker thread (which in this case will be doInBackground method), and also define a looper using Looper.prepare() (since, by default a worker thread doesn't have a looper, so there'd be no message queue which the handler can use). Try this out, and that method shouldn't cause any blocking thereafter.

    protected Void doInBackground(Void... params) {
                Handler handler = new Handler();                 //Declared on worker thread.
                Looper.prepare();
                handler.post(new Runnable(){
                    @Override
                    public void run() {
                        loc = getLocation();//this blocks the activity
                    }
                });
                return null;
            }
    

    Another approach could be to use an IntentService, which runs on a worker thread by default, so you won't need any async task then. For IntentService, check this out : http://developer.android.com/reference/android/app/IntentService.html