Android Networking Problems with AsyncTask

Go To StackoverFlow.com

0

I'm working on an Android app for a networking exercise and I'm having all sorts of problems. I have a working C server that I am running on my computer, and the app that I am writing aims to connect to the server, authenticate, and then initiate a file transfer from the server to the client. I am very new to Android, and I only very recently learned that blocking the UI thread is generally prohibited.

I have created an AsyncTask to help me with this. However, it completely and totally fails when I call it. It doesn't fail conspicuously, however; the only kind of problem that I see is that my main screen shrinks and moves around weirdly. Sometimes the UI thread blocks completely and the OS tells me that my app isn't responding, which doesn't make any sense to me because I'm threading the action that's blocking!

This is the code that I am using for my AsyncTask:

private class LoginTask extends AsyncTask<String, Void, Boolean> {

    @Override
    protected Boolean doInBackground(String... input) {
        int count = input.length;
        if (count != 4)
            return false;
        String ipAddress = input[0];
        int portNumberInt = Integer.parseInt(input[1]);
        String username = input[2];
        String password = input[3];
        // Step 0: Establish a connection to the server
        PrintWriter out;
        BufferedReader in;
        try {
            serverSocket = new Socket(ipAddress, portNumberInt);
            out = new PrintWriter(serverSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        // Step 1: send "Authorize" to the server
        out.print("Authorize");
        // Step 2: server sends a random 64 character challenge string
        char[] buffer = new char[64];
        String challenge = null;
        try {
            in.read(buffer);
            challenge = new String(buffer);
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        challenge = username + password + challenge;
        // Step 3: Compute MD5 hash of username + password + challenge and send it to the server
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            return false;
        }
        byte[] digest = md5.digest(challenge.getBytes());
        out.print(digest);
        // Step 4: Server computes the same hash, determines whether or not to accept the connection
        String authResult;
        try {
            in.read(buffer);
            authResult = new String(buffer);
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        if (authResult.equals("Pass")) {
            return true;
        } else {
            return false;
        }
    }

}

And this is the code that calls the AsyncTask:

public boolean authenticate(final String ipAddress, final int portNumberInt, final String username, final String password) {
    try {
        Toast.makeText(getApplicationContext(), "Starting async task...", Toast.LENGTH_LONG).show();
        boolean result = new LoginTask().execute(ipAddress, Integer.toString(portNumberInt), username, password).get();
        Toast.makeText(getApplicationContext(), "Async task done!", Toast.LENGTH_LONG).show();
        return result;
    } catch (InterruptedException e) {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        return false;
    } catch (ExecutionException e) {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        return false;
    }

}

What could possibly be causing this problem? I am running my server and it isn't showing any indication that a connection is made. If this is the case, then my code should never block... right?

2012-04-04 23:50
by Sean Gillespie
I don't see any blocking code either, but one thing to watch out for is that the counterpart to "you can't do blocking work in the UI thread" is "you can't access the UI from a background thread". Read this doc for a good explanation of all of this - kabuko 2012-04-05 00:02


1

There are a number of issues that I'd fix here although I can't give a definitive answer to your problem. Two quotes from your question...

I only very recently learned that blocking the UI thread is generally prohibited.

Actually it's not just 'generally' prohibited, just don't do it ever.

...the OS tells me that my app isn't responding, which doesn't make any sense to me because I'm threading the action that's blocking!

Not exactly true due to this line in your authenticate method...

boolean result = new LoginTask().execute(<cut-for-clarity>).get();

You're using the get() method of AsyncTask which 'waits' for a result basically turning your call to execute the asynchronous task into a synchronous one.

Using get() to perform network operations with AsyncTask is potentially as bad as simply executing the code on the UI thread. I've never understood why AsyncTask has the get(...) methods.

The next problems I see are in the following code...

try {
    serverSocket = new Socket(ipAddress, portNumberInt);
    out = new PrintWriter(serverSocket.getOutputStream(), true);
    in = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
} catch (IOException e) {
    ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
    ((TextView)findViewById(R.id.textView2)).setText("");
    return false;
}

Firstly your catch block is only looking for IOException. Creating your socket can potentially also throw UnknownHostException. Creating your PrintWriter could also throw NullPointerException etc etc

Secondly, in your catch block you're attempting to set the text of two TextViews. The second golden rule of threading (aside from not blocking the UI thread) is to never try to manipulate UI elements from any thread other than the UI thread - it won't work.

So basically

  1. Clean up your code to at least handle the relevant exceptions - put a generic try{...} catch(Exception e) {...} around everyting in doInBackground(...) if necessary.
  2. Don't use the get() method of AsyncTask
  3. Get rid of anything which attempts to manipulate UI elements from doInBackground(...)
  4. Look at logcat to see what's failing and on which line.

EDIT:

Much more generally, how can I pass information from my AsyncTask back to the UI thread?

That's entirely the point of the onPostExecute(...) method of AsyncTask which executes on the UI thread.

Use doInBackground(...) not only to authenticate but also to download the file you need. If there are any problems, pass false as a result to onPostExecute(...) and have that method create some sort of alert or error message and then clean up.

2012-04-05 00:31
by Squonk
Thank you very much for your response. A question, though; how do I access the result of the authentication when execute() doesn't give it to me? execute() gives me back another AsyncTask, which isn't helpful. I need to know the result of the authentication in order to continue with my program. Much more generally, how can I pass information from my AsyncTask back to the UI thread - Sean Gillespie 2012-04-05 03:29
@Swgillespie: See my EDIT at the end of my answer - Squonk 2012-04-05 06:39
Thank you very much - Sean Gillespie 2012-04-05 15:12


0

If you have a C server running on your computer than you need a client on android. A client would use a Socket. You are using a ServerSocket...

2012-04-05 21:37
by Helper
No, I'm not... the name of the Socket object that I'm creating is called serverSocket. I explicitly called the Socket constructer - Sean Gillespie 2012-04-06 00:31
Ads