1

I've made an Android app that authenticates with a server via an Asynctask that does this in background on user button click. While this is happening, the user is shown a loading popup or a spinner. This is all currently working.

I am now trying to create redundancy by setting up another server just in case the 1st one goes down. To accommodate this change on my app, I thought of making 2 asynctasks call one after the other.

However, I am encountering some issues.

As test urls, I have been using 10.255.255.1 (un-routable address) and www.google.com:81 (blocked port, dropped packets).

  1. In case server 1 does not respond or takes a long time (tested using the urls above), I am trying to do a timer on the asynctask for 5-10 seconds which I have tried using

    new MyAsyncTask().execute(serverurl1).get(5000, TimeUnit.MILLISECONDS);
    

    It didn't work. Curiously, it waited 5 seconds before showing the spinner wheel and then performed as normal (waiting forever past 5 or even 10 second mark).

  2. I guess this 2nd issue question hinges on the 1st question but how do I make sure the 1st asynctask is done (or has executed and gone over the time limit) before creating the 2nd asynctask to query the server 2? I guess I could use STATUS.running (but only after making sure the 1st asynctask has run its course?

    For this one, I have tried shooting out 2 asynctasks like so:

    new MyAsyncTask().execute(serverurl1);
    new MyAsyncTask().execute(serverurl2);
    

    hoping that shooting out 2 asynctasks would get me some sort of response. Even if 1 failed, we'd still get authenticated but alas, it didn't work.

I know some of you might say that by serializing the behavior of asynctask, then it defeats the purpose of it. I use it to show the spinner on screen while the app is checking itself with the server.

Questions:

  • if an asynctask is stopped/killed by timer or other things, is onPostExecute still called? I'm wondering if this would be a good place to put the 2nd asynctask if it is ALWAYS called.

My ideal solution would be:

  • user clicks button

  • asynctask 1 runs and queries with server 1

  • if something goes wrong, asynctask 1 is killed after timer runs out

  • asynctask 2 then runs with query to server 2

  • if something goes wrong, asynctask 2 is killed after timer runs out

  • (no process that runs forever and clear error message shown)

OR

  • user clicks button

  • asynctask 1 runs and queries with server 1

  • if something goes wrong, asynctask 1 is killed after timer runs out

  • user is then informed to retry (internally set a flag to query server 2)

  • user clicks on button again

  • asynctask 2 then runs with query to server 2

  • if something goes wrong, asynctask 2 is killed after timer runs out

  • (again, no process that runs forever and clear error message shown)

Thanks.

I call the async block using:

private void startTask() throws IOException {
    String url = "http://google.com:81";    
    //String url = "http://10.255.255.1";
    String url2 = "someurlthatworks";   

    // tried this
    new MyAsyncTask().execute(url);
    new MyAsyncTask().execute(url2);

    // and this
    try {
        new MyAsyncTask().execute(url).get(5000, TimeUnit.MILLISECONDS);
    }catch(Exception e){}
}

I saw this on How can I put a timer in an AsyncTask without creating another thread? (edited with my variables):

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    public void run() {
        new MyAsyncTask.execute(url);
    }
}, 5000);

Would it work for my issue?

Here is my asynctask code block: class MyAsyncTask extends AsyncTask {

@Override
protected void onPreExecute() {                                                 
    super.onPreExecute();                                                       
    showDialog(DIALOG_DOWNLOAD_PROGRESS);                                       
}

@Override
protected String doInBackground(String... urls) {                               
    int count;                                                                  
    InputStream input = null;                                                   
    HttpURLConnection httpconnection = null;                                    

    try {
        URL url = new URL(urls[0]);                                             

        httpconnection = (HttpURLConnection) url.openConnection();              
        httpconnection.connect();                                               
        httpconnection.setConnectTimeout(7000);                                 
        httpconnection.setReadTimeout(10000);

        // check here if there is connectivity, server is accessible, http code is 200
        // return error message if any of the above not found

        int lengthOfFile = httpconnection.getContentLength();                   
        input = httpconnection.getInputStream();                                
        byte data[] = new byte[1024];                                           
        long total = 0;                                                         

        while ((count = input.read(data)) != -1) {                              
            total += count;                                                     
            publishProgress(""+(int)((total*100)/lengthOfFile));                
        }

        String msg = new String(data);                                          

        if(msg.contains(someinfo)) {
            allow = true;
        }

        input.close();                                                          
    } catch (Exception e) {}                                                    

    return null;
}

protected void onProgressUpdate(String... progress) {                           
     mProgressDialog.setProgress(Integer.parseInt(progress[0]));                
}

@Override
protected void onPostExecute(String unused) {                                   
    dismissDialog(DIALOG_DOWNLOAD_PROGRESS);                                    

    if(allow){                          
        Toast.makeText(getBaseContext(),"Success",Toast.LENGTH_SHORT).show(); 
    }else{      
        Toast.makeText(getBaseContext(),"Failed",Toast.LENGTH_SHORT).show(); 
    }

}
}

as I've been told

httpconnection.setConnectTimeout(7000);                                 
httpconnection.setReadTimeout(10000);

should work for my problem but somehow it doesn't and the asynctask just keeps waiting.

Update:

As suggested by Marcin Jędrzejewski that I move connect and read timeouts before connect, I get:

... [socket][1] - [socket][13] here ... 
[socket][14:42141] exception
[CDS]close[42141]
close [socket][/0.0.0.0:42141]
[socket][14] connection www.google.com/4.35.153.212:81;LocalPort=59339(7000)
[CDS]connect[www.google.com/4.35.153.212:81] tm:7
[socket][15:59339] exception
[CDS]close[59339]
close [socket][/0.0.0.0:59339]
[socket][15] connection www.google.com/4.35.153.237:81;LocalPort=56148(7000)
[CDS]connect[www.google.com/4.35.153.237:81] tm:7
[socket][16:56148] exception
[CDS]close[56148]

The setconnecttimeout seems to be the timeout each time it attempts a connection but is there any way to set how many times it retries a connection instead of always going 16 tries?

Community
  • 1
  • 1
publicknowledge
  • 634
  • 1
  • 9
  • 17
  • 2
    ***Curiously,** it waited 5 seconds before showing the spinner* ... rather **obviously** because `get` blocks main thread – Selvin Dec 09 '15 at 12:42
  • Try executing the 2nd async task in the first async task's postExecute() method. – AndroidDev Dec 09 '15 at 12:42
  • Did you read this http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean) and this: http://developer.android.com/reference/android/os/AsyncTask.html#get(long, java.util.concurrent.TimeUnit)? – Raanan Dec 09 '15 at 12:43

2 Answers2

2

You can read in docs, that AsyncTask.get will throw TimeoutException in case the time has elapsed with no result. My suggestion for you is to add timeouts on your Url connection (if you use it), like:

httpUrlConnection.setConnectTimeout(5000);
httpUrlConnection.setReadTimeout(5000);

this will cause your Url connection to unblock after 5s and then also your AsyncTask will finish. Then in onPostExecute you can execute another AsyncTask with new Url - in case of connection error.

If you would like to abort current connection - in progress - then use:

httpUrlConnection.disconnect();

And finally, calling AsyncTask.get() is not very good idea, as I described above - it does not give you any guarantee that connection will end, and also you cannot call it on UI thread.

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • I actually already set connect and read timeout. The problem, I read, is it only works when there is a connection already established and doesn't work if the issue is DNS not being resolved. So as a catch-all, I thought it better to control the whole asynctask run time instead of trying to look for fail cases. Oh and I have tested and on those 2 urls I mentioned, they don't seem to work. – publicknowledge Dec 09 '15 at 12:52
  • 1
    @publicknowledge the problem with startTask is that it must be called from UI thread (AsyncTask.execute must be invoked on UI thread), so the first two execute are ok, but the third one calls get on UI thread - which is always bad idea. You might try using handler with postDelayed - which will after 5s call disconnect on any pending connection, then start new async task. – marcinj Dec 09 '15 at 13:01
  • I see. I kinda figured the 3rd one wouldnt be so good after all the bad comments on the "get" method and it not working for me. I will look into the postDelayed method to set a 5 second wait time for my asynctasks. – publicknowledge Dec 09 '15 at 13:09
  • added more info to my question. can you advise on how to disconnect connections or cancellation of the asynctask from that code? I know asynctask kinda still runs in the background even after user presses back and cancels the spinner icon. – publicknowledge Dec 09 '15 at 13:15
  • @publicknowledge actually timeouts set on you url connection should suffice, if you have problem with that then I would use handler just before making connection using: new Handler(Looper.getMainLooper()).postDelayed, and use handler only for disconnecting. Then start new asynctask, from inside of AsyncTask.onPostExecute - of course only if last connection failed. If disconnect will not work for you then you might try with AsyncTask.cancel, but then AsyncTask.onPostExecute is not called. – marcinj Dec 09 '15 at 13:23
  • I'm not sure why my url connection timeouts aren't working. let me post my asynctask code. – publicknowledge Dec 09 '15 at 13:49
  • @publicknowledge call setReadTimeout/setConnectTimeout before connect – marcinj Dec 09 '15 at 16:00
  • I put the connect and read timeouts before connect but the sockets still go from [0] to [16]. Even cutting down the timeouts to httpconnection.setConnectTimeout(3000); httpconnection.setReadTimeout(5000); it still goes from [0] to [16] – publicknowledge Dec 09 '15 at 22:19
0

So I found this topic How to timeout Asynctask and dismiss ProgressDialog?

And I implemented my code below, in the doInBackground:

long waitTime = 7000;  
long curWaitTime = 0;
while (!timeout && curWaitTime < waitTime){
    // put your network/file code here
    // if the data finishes then you can set timeout to true

    try {
        URL url = new URL(urls[0]);

        httpconnection = (HttpURLConnection) url.openConnection();              
        httpconnection.setConnectTimeout(7000);                                   
        httpconnection.setReadTimeout(9000);
        httpconnection.connect();       

        // check here if there is connectivity, server is accessible, http code is 200
        // return error message if any of the above not found

        input = httpconnection.getInputStream();                                

        byte data[] = new byte[1024];                                           
        input.read(data);
        String msg = new String(data);                                          

        if (msg.contains(someinfo)){                
            allow = true;                                                       
            timeout = true;
        }

        input.close();                                                          
    } catch (Exception e) {}                                                    
    curWaitTime += 7000; 
    try{
        Thread.sleep(100);
    }catch(Exception e){}
}

I set the waitTime to be the same as my setConnectTimeout which makes the while loop timeout roughly after 1 connect attempt.

My startTask() is basically just

new MyAsyncTask().execute(url);

with some flags for the app to know if the 1st asynctask failed or not so it can try:

new MyAsyncTask().execute(url2);

Not sure what the thread.sleep(100) is for. I know the thread sleeps for 100 milliseconds after each while loop. Code block still works without it.

Community
  • 1
  • 1
publicknowledge
  • 634
  • 1
  • 9
  • 17