dilluns, 13 de febrer del 2017

Asynchronous task in Android. AsyncTask

Continuing with asynchronous task, this time in Android.
The problem  is the same what we have in javascript or another programming language. We have to wait which a specific task ends or wait some data from a query, etc... If these task block the main thread for more of five seconds the ANR dialog is presented to de user. For that you can use threads and runs the task in background. An Android native application commonly is programming with java and the threads aren't a problem. Here can you read more about processes and threads in Android.
For facilitate the task and make it more easy is designed AsyncTask. Here can you read more about that.

Continuing with the same example used in the javascript promises entry, first we must write a class which extends AsyncTask.

   private class GetInfoTask extends AsyncTask<String, Void, String> {

   }

In an asynchronous task we use types. The first indicates the type of the parameters and it is an Array. The second, the type of the progress and it is  used for show the progress dialog and finally the type of the result. In this case, the parameters are an array of string, the progress are Void because we will use an spinner in the progress dialog and returns an string
Android Studio shows a message asking if you want to implement methods as in this image 



And if you select Implement methods the result would be like this


doInBackground is one of the four methods that we can use and it is required. This method is invoked on the background and it do the task. The rest of methods are onPreExecute() it is invoked on the UI thread and it is used for setup the task and show the progress dialog. onProgressUpdate(Progress ...) it is invoked on the UI thread and is used to display the progress. onPostExecute(Result) invoked on the UI thread when doInBackground finishes. The Result parameter is the value returned by doInBackground.
In principle it is not necessary use a constructor but if you want to show a progress dialog an application context is needed and the constructor it's a good way to pass it.

So, for our example would be

        private ProgressDialog progressDialog;
        private Context context;

        public GetInfoTask (Context context) {
            this.context = context;
        }

We first declare a ProgressDialog and a Context as global variables on the class and init the Context in the class constructor.


       @Override
       protected void onPreExecute () {
           progressDialog = new ProgressDialog(context);
           progressDialog.setCancelable(true);
           progressDialog.setMessage("Loading data...");
           progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
           progressDialog.show();
       }


In the onPreExecute we initialize the progress dialog with with the Context  and show it.

        @Override
        protected String doInBackground(String... urls) {
            String line = "";
            HttpURLConnection httpURLConnection = null;

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

                httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setRequestMethod("GET");
                httpURLConnection.setReadTimeout(5000);
                httpURLConnection.setConnectTimeout(10000);
                httpURLConnection.setDoOutput(true);
                httpURLConnection.connect();

                BufferedReader bufferedReader =
                 new BufferedReader(new InputStreamReader(url.openStream()));
                StringBuilder stringBuilder = new StringBuilder();

                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }

                bufferedReader.close();



                return stringBuilder.toString();

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
}

doInBackground execs the task, in this case  makes a GET connection to a URL passed in the parameter urls[0] and returns a string  with the result.


        @Override
        protected void onPostExecute (String result) {
            progressDialog.dismiss();
            String data = "";

            if (result != null)
                try {
                    JSONArray jsonArray = new JSONArray(result);

                    data = jsonArray.getString(2);

                    data = data.substring(2, data.length() - 2);

                } catch (JSONException e) {
                    e.printStackTrace();
                }
                textView.setText(data);
        }

Finally in onPostExecute the result is processed, first dismiss the progress dialog and if result is not null parse the JSON Array, extract the data and show on a TextView.


In this example would be missing the progress update, which is where I would enter onProgressUpdate like


        @Override
        protected void onProgressUpdate (Integer... values) {
            super.onProgressUpdate(values);
        }
For obtain the values to update on the progress dialog use publishProgress(Progress ...) into doInBackgound.

And finally use it


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

        final EditText editText = (EditText) findViewById(R.id.editText);
        Button button = (Button) findViewById(R.id.button);
        textView = (TextView) findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                GetInfoTask getInfoTask = new GetInfoTask(MainActivity.this);

                Locale locale;

                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    locale = getResources().getConfiguration()
                                          .getLocales().get(0);
                } else {
                    locale = getResources().getConfiguration().locale;
                }

                String finalUrl = "https://" + locale.getLanguage() + wikiURL
                                    + "?action=opensearch&search="
                                    + editText.getText().toString()
                                    + "&limit=10&explaintext&format=json";


                getInfoTask.execute(finalUrl);
            }
        });

First we create an object GetInfoTask and finally use it with the string containing the url getInfoTask.execute(finalUrl)

And the result can be like this

Cap comentari:

Publica un comentari a l'entrada