Alexander Hargraves
Alexander Hargraves

Reputation: 45

How to use AsyncTask?

I am writing a program and need to run separate thread for server connection, but I don't know how to properly use the AsyncTask. I have read the documentation and am still not certain as to how I would properly use it.

The method that's supposed to connect me to the server:

public boolean start()
{
    // try to connect to the server
    try {
        socket = new Socket(server, port);
    }
    // if it failed not much I can so
    catch(Exception ec) {
        display("Error connectiong to server:" + ec);
        return false;
    }

    /* Creating both Data Stream */
    try
    {
        sInput  = new ObjectInputStream(socket.getInputStream());
        sOutput = new ObjectOutputStream(socket.getOutputStream());
    }
    catch (IOException eIO) {
        display("Exception creating new Input/output Streams: " + eIO);
        return false;
    }

    // creates the Thread to listen from the server
    new ListenFromServer().start();
    // Send our username to the server this is the only message that we
    // will send as a String. All other messages will be ChatMessage objects
    try
    {
        sOutput.writeObject(username);
    }
    catch (IOException eIO) {
        display("Exception doing login : " + eIO);
        disconnect();
        return false;
    }
    // success we inform the caller that it worked
    return true;
}

The ListenFromServer class used above:

class ListenFromServer extends Thread {

    public void run() {
        while(true) {
            try {
                String msg = (String) sInput.readObject();
                // if console mode print the message and add back the prompt
                if(clientActivity == null) {
                    System.out.println(msg);
                    System.out.print("> ");
                }
                else {
                    clientActivity.append(msg);
                }
            }
            catch(IOException e) {
                if(clientActivity != null)
                    clientActivity.connectionFailed();
                display("Server has close the connection: " + e);
                break;
            }
            // can't happen with a String object but need the catch anyhow
            catch(ClassNotFoundException e2) {
            }
        }
    }
}

I know that I am supposed to use AsyncTask, but I don't know how.

Upvotes: 1

Views: 1281

Answers (2)

jburn2712
jburn2712

Reputation: 167

AsyncTask is a lot easier than it looks. There are three main methods that you need to worry about to get started. Calling execute() on an AsyncTask will kick off these methods:

onPreExecute(); 

This is called before doInBackground() is called. You can do your setup here if it's needed. Maybe set a ProgressBar's visibility or something.

doInBackground()

This is where the asynchronous operations take place. If you need to make a network call or whatever, put the code here.

onPostExecute();

This is called when doInBackground() is finished. Whatever is returned from doInBackground() will be passed to this method. It's important to note that this method is run on the main thread so you can make changes to UI from it.

Now for the actual class itself:

public class MyTask extends AsyncTask<String, Integer, Boolean> {

}

You'll notice that it takes three type parameters. The first type (String) is the type of paramters that will be sent to the task upon execution. For example, if you need to send a String(s) to the Task when it executes, you'll pass them into the execute() method. Example:

myTask.execute("these", "strings", "will", "be", "passed", "to", "the", "task");

Note that you can use 'Void' here (capital V) if you don't want to pass in anything from the execute() method. (To be clear, you can pass in any object of any type for these parameters -- Integer, Boolean, CustomClass, etc.. Just remember that if you're using a primitive type, you need to use its wrapper class, e.g. Integer, Boolean.)

public class MyTask extends AsyncTask<Void, Integer, Boolean> {

}

Now, to understand how to access these strings, we need to go to the doInBackground() method in the task.

@Override
protected Boolean doInBackground(String... params) {
    //...
} 

You'll notice that the doInBackground() method has a vararg parameter.

String... params 

are the Strings that you passed in earlier through execute(), packaged into an array. So the String "these" is located at params[0] and "will" is located at params[2].

The second type paramter,

Integer

is the type of object that will be passed to onProgressUpdate() when you call publishProgress() from doInBackground(). This is used to update UI from the main thread while the Task's execution is still ongoing. We won't worry about this for now -- You can figure that out later. You can pass 'Void' here as well if you don't plan on using the onProgressUpdate().

Now for the third and final type paramter

Boolean

This is the type of object that doInBackground() will return. It's pretty self-explanatory. You can pass 'Void for this paramter as well if you want, just note that you still need to

return null;

from doInBackground(). So what happens to this return Value once doInBackground() finishes? It gets sent to

@Override
protected void onPostExecute(Boolean...aBoolean) {

}

From here, you can do whatever you want with the result, which in this case is a boolean. Again, this method is run on the main thread so you can update Views from here. A lot of times, I'll pass a listener to the AsyncTask and call it from onPostExecute(). For example:

@Override
protected void onPostExecute(Boolean aBoolean) {
    mListener.onTaskFinished(aBoolean);
}

If your AsyncTask class is declared directly within your Activity, then you can just update Views or do whatever you need to do directly from onPostExecute().

One last thing, you can also pass objects to your Task in the more conventional way -- through the constructor. You don't have to pass arguments to execute() if you don't want to.

new MyTask("hello", 55).execute();

Just set those to the correct fields within your AsyncTask class and you're set. You can use them just like any other class.

Edit: Had some time so I figured I'd show the full implementation of an AsyncTask, including the interface used to create a callback for the result.

So let's say I need to make a network call to some API that will return a String of JSON data (or whatever kind of data, what matters is that it's a string). My AsyncTask will need a url to give to whatever network client I'm using, so we'll pass that in through the execute() method. So the first type parameter of the AsyncTask will be a String. Since I'm only making one network call and getting one response, I don't need to update any progress, so the second type parameter will be Void. The response I'm getting from the API call is a String, so I'm going to be returning that String from the doInBackground() method. So the third and final type parameter is String.

So my AsyncTask class will look like this:

public class NetworkTask extends AsyncTask<String, Void, String> {

    //I'll use this to pass the result from onPostExecute() to the
    //activity that implements the NetworkTaskListener interface, as
    //well as notify that activity that onPreExecute has been called.
    private NetworkTaskListener mListener;

    //Set the NetworkTaskListener
    public NetworkTask(NetworkTaskListener listener) {
        mListener = listener;
    }

    //onProgressUpdate() doesn't need to be overridden

    //Called after execute(), but before doInBackground()
    @Override
    protected void onPreExecute() {
        mListener.onBeforeExecution();
    }

    //Called automatically after onPreExecute(). Runs in background.
    @Override
    protected void doInBackground(String... params) {
        //"params" holds the Strings that I will pass in when I call 
        //execute() on this task. Since I'm only passing in one thing
        //(a String URL), "params" will be an array with a length of one
        //and the the String URL will be at params[0].

        String url = params[0];
        //Make the network call using whatever client you like.
        //I'll use OkHttp just as an example.

        OKHttpClient client = new OKHttpClient();
        Request request = new Request.Builder()
            .url(url)
            .build();
        Call call = client.newCall(request);

        //It's okay to use a blocking network call here since we're 
        //already off of the main thread.
        try {
            Response response = call.execute();
            //response.body().string() is the JSON data we want
            String jsonData = response.body().string();
            //This will be sent to onPostExecute()
            return jsonData;
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("NetworkTask", "Error during network call");
            //If there was an error, return null
            return null;
        }
    }

    //Called automatically after doInBackground(). The return value
    //from doInBackground() is passed in here automatically.
    @Override
    protected void onPostExecute(String aString) {
        //Check if the String is null to determine if there was an error
        //while making the network call.
        if (aString == null) {
            mListener.onNetworkCallError();
            return;
        }
        /*
        If this is executed, it means that the response was successful.
        Call the listner's onNetworkResponse() method and pass in the 
        string. The String will be used by the Activity that implements
        the NetworkTaskListener.
        */
        mListener.onNetowrkResponse(aString);
    }

}

So now that we've got the AsyncTask all set, we need to create the NetworkTaskListener interface. It will contain 3 methods (which you've already seen in the NetworkTask class) that must be implemented by any class that implements the interface. In this case, the activity will implement this interface and thus must override all 3 methods.

public interface NetworkTaskListener {
    //We call this method in onPreExecute() of the NetworkTask
    void onBeforeExecution();

    //Called from onPostExecute() when there was an error during the
    //network call
    void onNetworkCallError();

    //Called from onPostExecute() when the response was successful
    void onNetworkResponse(String jsonData);
}

Now that the Interface is taken care of, we just need to have our Activity implement it. For simplicity, I'm going to have the NetworkTask begin as soon as the Activity starts. You'll notice that we pass this into the NetworkTask. What that means is that we're passing the Activity as a NetworkTaskListener into the NetworkTask. Remember, the Activity is also a NetworkTaskListener since it implements it. So when we call mListener.onNetworkResponse(jsonData) from the the NetworkTask, it is calling the 'onNetworkResponse(String jsonData)' method from within the Activity. (I know, I know... now I'm explaining basic Java but I want this to be clear since AsyncTasks are used frequently by beginners.)

public class MainActivity extends Activity implements NetworkTaskListener {
    //ProgressBar that will be displayed when NetworkTask begins
    private ProgressBar mProgressBar;

    //TextView that will be used to display the String that we get back
    //from the NetworkTask
    private TextView mTextView;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate();
        setContentView(R.layout.activity_main);

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTextView = (TextView) findViewById(R.id.textView);

        String url = "https://www.exampleAPIurl.com";
        //The listener that we are passing in here is this Activity
        NetworkTask task = new NetworkTask(this);
        //We pass the URL through the execute() method
        task.execute(url);

        /*
        From this point, the NetworkTask will begin calling the
        NetworkTaskListener methods, which are implemented below.
        Specifically, it will call onBeginExecution(), and then either 
        onNetworkResponse(String jsonData) or onNetworkCallError();
        */
    }

   ///////Implemented methods from NetworkTaskListener

    @Override
    public void onBeginExecution() {
        //Called when the task runs onPreExecute()
        mProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void onNetworkCallError() {
        //Called from task's onPostExecute() when a Network error
        //occured
        mProgressBar.setVisibility(View.INVISIBLE);
        mTextView.setText("An error occured during the network call");
    }

    @Override
    public void onNetworkResponse(String jsonData) {
        //Called from task's onPostExecute() when network call was
        //successful
        mProgressBar.setVisibility(View.INVISIBLE);
        mTextView.setText(jsonData);
    }
} 

Note that you can also use the NetworkResponseListener as an anonymous class and the Activity won't have to implement it.

Upvotes: 1

Magesh Pandian
Magesh Pandian

Reputation: 9369

See below code,

private class ListenFromServer extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        while(true) {
        try {
            String msg = (String) sInput.readObject();
            // if console mode print the message and add back the prompt
            if(clientActivity == null) {
                System.out.println(msg);
                System.out.print("> ");
            }
            else {
                clientActivity.append(msg);
            }
        }
        catch(IOException e) {
            if(clientActivity != null)
                clientActivity.connectionFailed();
            display("Server has close the connection: " + e);
            break;
        }
        // can't happen with a String object but need the catch anyhow
        catch(ClassNotFoundException e2) {
        }
    }

    }

    @Override
    protected void onPostExecute(Voidresult) {
       //after completed



     try
    {
        sOutput.writeObject(username);
    }
    catch (IOException eIO) {
        display("Exception doing login : " + eIO);
        disconnect();
        return false;
    }
    // success we inform the 
    }

    @Override
    protected void onPreExecute() {}

    @Override
    protected void onProgressUpdate(Void... values) {}
}

Call this Asyntask,

new ListenFromServer().excute();

Upvotes: 0

Related Questions