0

I am relatively new to Android programming, but I do come from a Java background, building back-end stuff for Enterprise applications. Now, I am working on an Android project in which I would like to retrieve a java.util.List of Restaurants from the server.

Currently, to achieve these tasks, I am using a simple Thread with a new Runnable, as the thread is immediately in Thread-pool and I can then simply join the Thread, and wait till it receives a reply from the server and then send it back. Very convenient this is and working good.

However, there are two things I cannot do with Threads, check for Internet-connection on the client-side and display a progress-bar. As far as I understood and read, these both things can be achieved by AsyncTask.

But here is the critical point, I don't want the AsyncTask to execute in some near future and then return the result, because with my current Thread model, I can simply join the Thread and relax.

Now, my question is, is the same possible in AsyncTask, I don't mean using get method after 1000msec. I meant actually waiting for it. How do I accomplish that? And with that, I want to know how do I check for INternet, and if successful, then only make a request.

Here is one of the Async methods I have in the project and then the next is the simple Thread model I have in the project.

 private class LoginUserViaRest extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {

            final EditText userEmail = (EditText) findViewById(R.id.usernameText);
            final EditText userPassword = (EditText) findViewById(R.id.PasswordField);

            rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            StaticRestTemplate.jsessionid = rest.execute(StaticRestTemplate.baseURL+"j_spring_security_check", HttpMethod.POST,
                    new RequestCallback() {
                        @Override
                        public void doWithRequest(ClientHttpRequest request) throws IOException {
                            request.getBody().write(("j_username=" + userEmail.getText().toString() + "&j_password=" + userPassword.getText().toString()).getBytes());
                        }
                    }, new ResponseExtractor<String>() {
                        @Override
                        public String extractData(ClientHttpResponse response) throws IOException {
                            List<String> cookies = response.getHeaders().get("Cookie");
                            if (cookies == null) {
                                cookies = response.getHeaders().get("Set-Cookie");
                            }
                            String cookie = cookies.get(cookies.size() - 1);
                            int start = cookie.indexOf('=');
                            int end = cookie.indexOf(';');

                            return cookie.substring(start + 1, end);
                        }
                    });
            return null;
        }

        @Override
        protected void onPostExecute(String aVoid) {
            super.onPostExecute(aVoid);
        }
    }

Thread to retrieve images :

 @Override
    public List<RestImage> getImagesForMenuCard(final int menuCardId) {
        final RestTemplate restTemplate = StaticRestTemplate.getRest();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                HttpHeaders requestHeaders = new HttpHeaders();
                requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid);
                requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json")));
                HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
                restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
                menuCardImageEntity = restTemplate.exchange(menucardImageList + menuCardId, HttpMethod.GET, requestEntity, RestImage[].class);
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        RestImage[] restImages = menuCardImageEntity.getBody();
        List<RestImage> restImageList = new ArrayList<>();
        Collections.addAll(restImageList, restImages);
        return restImageList;
    }

I find the Thread model easy, as I know it better. I really don't understand the requirement to create a class to make a Network request. Is my understanding of AsyncTask broken or this is the way it is supposed to be.

Apologies for the copious amount of text, thought I would concisely explain my situation. Thanks a lot.

We are Borg
  • 5,117
  • 17
  • 102
  • 225
  • Unfortunately Android does not support Java8, or else I would have simply put Lambda expressions in the Thread to create a more compact version. – We are Borg Sep 04 '15 at 14:16
  • Using AsyncTask is referred as painless threading, here's what android developers has to say about AsyncTask vs Threads: [Painless threading](http://android-developers.blogspot.com/2009/05/painless-threading.html) and another good read here: [Handler vs AsyncTask vs Thread](http://stackoverflow.com/a/9800870/593709) – Adil Soomro Sep 04 '15 at 14:25
  • @AdilSoomro : I have neither long running tasks, neither frequent UI updates... The link you gave just refers to showing that someone is freezing the View. Not relevant as I would like to display Progress bar when Network Request is being made. – We are Borg Sep 04 '15 at 14:29
  • Making a request to network is actually a 'long running task', `AsyncTask` lets you simplify the management of threads, `preExecute()` lets you do something like showing progress bar, right before you want to do the actual network request in `doInBackground()`. and `publishProgress()` lets you update the progressbar in `onProgressUpdate()`, and at last `onPostExecute()` lets you update the UI finally, since your data has downloaded, populate the view. – Adil Soomro Sep 04 '15 at 14:41
  • I have never worked on UI before, didn't knew I was blocking UI thread itself. I will make the modifications to use the suggestions given in the answers below. – We are Borg Sep 04 '15 at 15:01
  • That's what every developer coming from desktop and backend experiences first. :) Welcome to android world. – Adil Soomro Sep 04 '15 at 15:11
  • @AdilSoomro : Lol, indeed... Thanks a lot for your comments. If you have any suggestions like alternatives, please create an answer so other users may benefit too. Thank you!! – We are Borg Sep 04 '15 at 15:13
  • Retrofit is awesome for network and thread management, other alternatives and a handful of discussion can be found here [Comparison of Android Networking Libraries: OkHTTP, Retrofit, Volley](http://stackoverflow.com/q/16902716/593709) – Adil Soomro Sep 04 '15 at 15:22

3 Answers3

2

If you call join() your thread is waiting for the other thread to die. Since the thread where you called the join is the Main or UI Thread, it means that you're blocking the UI, that is something that you should avoid. Now - I really hate AsyncTask, but it's the Android standard way of performing (potentially) long running task. You could take a look to IntentService (but you will have to manage the communication between the service and the UI) or you could take a look at RxJava and RxAndroid (that's my favourite framework).

About checking connection, you need the ConnectivityManager class, that of course requires a Context.

Mimmo Grottoli
  • 5,758
  • 2
  • 17
  • 27
  • RxJava and RxAndroid look promising and quite convenient rather then messing around with AsyncTask. One last thing before I accept your answer, can you help me translate the Login code which I have in main Post to RxAndroid code. I am setting the value of JSESSIONID which is returned by the server. – We are Borg Sep 04 '15 at 14:45
  • It's a bit tricky code with Login, that's why. Thanks a lot. :-) – We are Borg Sep 04 '15 at 14:46
  • Ok. Take a loot at [Retrofit](http://square.github.io/retrofit/), so you can forget all the stuff related to http. The library can provide you an Observable for every network request. When you have the Observable, you just need to subscribe to it to get the object :) – Mimmo Grottoli Sep 04 '15 at 14:49
1

There is no requirement to use AsyncTask for anything in particular. You can use any Java concurrency classes/techniques/frameworks you like.

The benefit of AsyncTask is it gives you a nice abstraction for guaranteeing certain code be executed on certain threads (doInBackground() on a background thread; onPreExecute(), onPostExecute(), and onProgressUpdate() on the main thread), so if you want to update the UI based on the progress/completion of the AsyncTask, it's very easy to do. Generally I recommend AsyncTask for relatively short operations that may have direct UI impact, e.g. blurring a bitmap in the background and then setting it as the content of an ImageView

The important takeaway is that you should never ever block the main (UI) thread. This means calling join() on that thread is always a bad idea. You need to design your code in a way that you can be notified when an asynchronous operation has completed--usually this involves callback interfaces or some other observable pattern. Think of it this way: Just because you are "waiting" for the result does not mean the thread has to be blocked, you just may have to ensure that the user can't interact with some part of the UI.

There are some good books on the subject, I suggest doing a search for them.

EDIT here's one: http://shop.oreilly.com/product/0636920029397.do

Karakuri
  • 38,365
  • 12
  • 84
  • 104
  • This is the right answer. Never use threads with join causing you to block the UI thread. Android 101 says never hinder the users experience, if you must use a spinner, and just use AsyncTasks... saves you much time and effort – Lucas Crawford Sep 04 '15 at 14:51
0

Don't ever use join() in main thread, because if the thread you are waiting for stuck in loop or don't ever return (returning from its run method), you will get ANR.

As for your question, there is no difference between AsyncTask class and other concurrency methods in Java. What AsyncTask does is abstracting what is done in background, a way to give notification about the operation in the background and posting the background operations result.

I for myself prefer using the Thread class instead, and to call back my operations status using interfaces. Consider something like below:

    public class BackgroundOperationWorker implements Runnable{

       private Thread thread;
       private IOperationListener listener;
       Handler handler;

       public BackgroundOperationWorker(Handler mainThreadHandler,IOperationListener listener) 
       {
            this.handler = mainThreadHandler;
            this.listener = listener;
       }

        public void doIt(){
        thread = new Thread(this);
        thread.start();
        }

        @Override
        public void run(){
        /**
         do what you have to do in the thread
        **/

        if(success){
           handler.post(new Runnable(){
           listener.operationSuccess();
           });
          }else{
           handler.post(new Runnable(){
           listener.operationFailed();
           });
          }
        }
}

be wary that if the listener is implemented in your activity class, on configuration change like rotation, the instance of the interface becomes invalid and you must set the instance again in activity's onConfigurationChanged(Bundle bundle) method.

EDIT: Edited the sample code above to make it more simple.

consider the code below:

public class MyClass implements IOperationListener{

public BackgroundOperationWorker worker;

public MyClass(Context context){

this.worker = new BackgroundOperationWorker(new Handler(context.getMainLooper()),this/*as an instance of IOperationListener*/);

this.worker.doIt();
}

@Override
public void onOperationSuccess(){
//notify the user that the operation is successful
}

@Override
public void onOperationFailed(){
//notify the user that the operation has failed
}

}

This MyClass is an example. This can be your activity implementing the "IOperationListener".

Bardya Momeni
  • 337
  • 4
  • 13
  • How do you call the method from above class and where are you getting reply back? Do you have any code which uses the above class? Thanks a lot!!! – We are Borg Sep 04 '15 at 14:58
  • @WeareBorg I edited the answer to be more clear. The MyClass uses the BackgroundWorker class. The doIt method of the BackgroundWorker class initializes a background thread and starts it. Because BackgroundWorker implements Runnable, I passed the BackgroundWorker instance to the background thread. After the thread is started the run method of the BackgroundWorker is called from the thread. – Bardya Momeni Sep 04 '15 at 15:51