We are Borg
We are Borg

Reputation: 5313

Android : Get data in real-time instead of using Async-Task

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.

Upvotes: 0

Views: 1302

Answers (3)

Bardya Momeni
Bardya Momeni

Reputation: 337

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".

Upvotes: 0

Karakuri
Karakuri

Reputation: 38605

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

Upvotes: 1

Mimmo Grottoli
Mimmo Grottoli

Reputation: 5773

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.

Upvotes: 2

Related Questions