0

I have a button that runs an AsyncTask to download some info from the web, and put that info into a local variable in the activity, which can be viewed by pressing another button. Also, I update a view in the UI to state whether the sync is running or ready.

For some reason, sometimes the onPostExecute does not update the UI and local variable as expected, though sometimes it does. I checked with debugger, and the code that updates the variable (handleDownloadComplete) is running, but still the UI and show data button don't update properly. Note: issue happens mostly when connection times out, but still I saw with debugger that the return value was correct - "Connection timed out", yet activity doesn't update.

Thanks!

The AsyncTask class:

public class DownloadDataTask extends AsyncTask<String, Integer, String> {
    public interface DownloadCompleteHandler
    {
        void handleDownloadComplete(String result);
    }

    private DownloadCompleteHandler handler;


    @Override
    protected String doInBackground(String... urls) {

        try {
            return downloadUrl(urls[0]);
        } catch (IOException e) {
            return "Unable to retrieve web page. URL may be invalid.";
        }
    }

    @Override
    protected void onPostExecute(String result) {
        handler.handleDownloadComplete(result);
    }

    private String downloadUrl(String urlStr) throws IOException
    {
        InputStream is = null;
        String result = new String();

        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("GET");
            conn.setDoInput(true);

            conn.connect();
            int response = conn.getResponseCode();
            is = conn.getInputStream();

            BufferedReader in = new BufferedReader(new InputStreamReader(is));
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                result += inputLine;            
            }       
        }
        catch (MalformedURLException ex) {
            result = "Malformed URL: " + urlStr;
        }
        catch (SocketTimeoutException ex) {
            result = "Connection timed out";
        }
        finally {
            if (is != null)
                is.close();
        }

        return result;
    }

    public void setHandler(DownloadCompleteHandler handler) {
        this.handler = handler;
    }   
}

The Activity:

public class MainActivity extends Activity implements DownloadDataTask.DownloadCompleteHandler{

    private String downloadResult = "";
    private Boolean isSyncing = false;

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

    @Override
    public void onResume() {
        super.onResume();
        checkNetworkConnection();
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putString(KEY_DOWNLOAD_RESULT, downloadResult);
        savedInstanceState.putBoolean(KEY_IS_SYNCING, isSyncing);

        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        downloadResult = savedInstanceState.getString(KEY_DOWNLOAD_RESULT);
        isSyncing = savedInstanceState.getBoolean(KEY_IS_SYNCING);
        updateAppDataView();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                settingsMenu();
                return true;
            case R.id.action_show_result:
                showUrlResultDialog();
                return true;
            case R.id.action_sync:
                getHttpData();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    void settingsMenu() {
        Intent intent = new Intent(this, SettingsActivity.class);
        startActivity(intent);
    }

    private void checkNetworkConnection() {
        ConnectivityManager connMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()) {
                // test app connection
            } else {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.titleNoNetwork).setMessage(R.string.msgNoNetwork);
                builder.setCancelable(false);

                builder.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        Intent intent = new Intent(Intent.ACTION_MAIN);
                        intent.addCategory(Intent.CATEGORY_HOME);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivity(intent);
                    }
                });

                AlertDialog dialog = builder.create();
                dialog.show();
            }
    }

    private void getHttpData()
    {
        if (isSyncing) return;

        isSyncing = true;

        TextView view = (TextView)findViewById(R.id.textWebResult);
        view.setText("Syncing");

        String serverId = PreferenceManager.getDefaultSharedPreferences(this).getString(getString(R.string.keyServerIp), "");
        String url = "https://" + serverId;
        DownloadDataTask downloader = new DownloadDataTask();
        downloader.setHandler(this);
        downloader.execute(url);
    }

    public void handleDownloadComplete(String result)
    {
        downloadResult = result;
        TextView view = (TextView)findViewById(R.id.textWebResult);
        view.setText("Ready");
        isSyncing = false;
    }

    private void showUrlResultDialog()
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(R.string.titleUrlResultData).setMessage(downloadResult);

        builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        });     

        AlertDialog dialog = builder.create();
        dialog.show();
    }
}

Edit: I noticed I was missing onSaveInstanceState and onRestoreInstanceState implementation, and thought it might be the cause since the problem only occurs when the connection times out, which might cause the activity to restart from some reason. So I added them (also in the code above), but the problem still occurs...

Any ideas anyone?

tbkn23
  • 5,205
  • 8
  • 26
  • 46
  • in that case, is control coming to handleDownloadComplete() method.? – Dev.Sinto Apr 16 '13 at 07:22
  • Yes, according to the debugger it is, and still after running the view's setText() method, the GUI remains unchanged. – tbkn23 Apr 17 '13 at 05:49
  • Let's try by set textview directly in onPostExecute method without using interface tell me the result. – Dev.Sinto Apr 17 '13 at 08:23
  • Same thing... Why should it matter? It's just a function call, not a different thread... – tbkn23 Apr 18 '13 at 06:15
  • BTW, it only happens when the connection times out. If the connection succeeds it never happens. So might be something with waiting 15 seconds for a result... – tbkn23 Apr 18 '13 at 06:16

2 Answers2

0

I am be wrong,

But i think its not connection times out problem. It could be when u change the orientation of the devices. Since when a orientation changes the activity is recreated and there is a new activity object with different textview.

But that's the case, then u should try adding android:configChanges="orientation|keyboardHidden|screenSize" to your mainfest.

This post describes the issue at hand well:

Background task, progress dialog, orientation change - is there any 100% working solution?

EDITED

onSaveInstanceState will just save your data used by that activity and not the views. The views will be recreated. Since u are passing the reference of your activity through downloader.setHandler(this); so after download complete handler will point to old activity not the new one and the textview will be belong to old activity. So the new activity does not get the event of download complete.

If u are sure that handler.handleDownloadComplete(result); is being called then this only might be the issue.

Community
  • 1
  • 1
Vivek Khandelwal
  • 7,829
  • 3
  • 25
  • 40
  • I thought that might be it, but I tried making sure the phone stayed in the same orientation the entire time and the problem still occured... I'll try it anyway. – tbkn23 Apr 19 '13 at 13:20
  • Ok, it definitely has to do with this somehow. Can you please explain a bit more? Since I am saving the information in onSaveInstanceState, why is this not working? I don't want to override the configChanges if I can help it. – tbkn23 Apr 19 '13 at 13:39
0

You must call runOnUiThread to update your widgets on ui thread properly, like so:

public void handleDownloadComplete(final String result)
{
     runOnUiThread(new Runnable() {
         public void run() {
             downloadResult = result;
             TextView view = (TextView)findViewById(R.id.textWebResult);
             view.setText("Ready");
             isSyncing = false;
         }    
     }); 
}
ararog
  • 1,092
  • 10
  • 18
  • That's not it, since onPostExecute() of AsyncTask is always executed on the UI thread. – tbkn23 Apr 19 '13 at 13:32
  • Have you tried to move the call to `handler.handleDownloadComplete(result)` from postExecute to downloadUrl method? The code above will be also necessary. Maybe postExecute isn't calling for some reason when a timeout happens. – ararog Apr 19 '13 at 13:41
  • postExecute is calling, I can see it in the debugger. I trace the commands one by one, and see that when I run the update view command nothing happens in the UI. – tbkn23 Apr 19 '13 at 13:45