Today we’ll show you how to load data from the web an app. This must be done through an AsyncTask (or through a background thread, but we’ll demonstrate the former), in order to let the UI continue respond to the user. That is, because, the main thread of the app is running the UI thread, so if you start pulling data from the web, while the app downloads/processed the data (which can even take seconds), the UI will froze. AsyncTask fixes this issue by creating a background thread and using it to process everything that would otherwise block the main thread.
Don’t worry if it sounds complicated, it really isn’t. As always, the code will be properly commented so you should be able to make sense of everything.
First of all, create a simple Android app as described here, and give internet permission to it.
Then, in ActivityMain.xml, insert the following. This will essentially create a LinearLayout, which allows us to put data into it. We also create a ProgressBar, which is just a rotating circle that will show us the fact that the UI thread doesn’t stop.
<
?
xml version="1.0" encoding="utf-8"?
>
<
LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.codingfy.demonetowrkload.MainActivity" android:id="@+id/addViews"><
ProgressBar android:id="@+id/myRotatingProgressBarBar" style="@android:style/Widget.ProgressBar.Small.Inverse" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true"/><
/
LinearLayout>
Then, here comes the actual Java code of the app. As expected, this goes in MainActivity.java, if you named your class that way. You might want to change some things to match your project's details (e.g. the package name).
package com.codingfy.demonetowrkload; import android.os.AsyncTask; import android.os.StrictMode; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.LinearLayout; import android.widget.TextView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { LinearLayout linearLayoutToAddTo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //We find our LinearLayout. linearLayoutToAddTo = (LinearLayout)findViewById(R.id.addViews); //This is how we fire the AsyncTask new LoadFromNetwork().execute(); } private class LoadFromNetwork extends AsyncTask{ ArrayList cities = new ArrayList<>(); /* This is running on the background thread, so anything that has the potential of blocking the UI thread should go here (network calls, heavy processing etc). */ @Override protected String doInBackground(String... params) { try { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); //This will delay loading the data by a second, so that the rotating circle and //the data do not appear instantly. SystemClock.sleep(1000); //The Link which we are calling. Replace with your own link! URL url = new URL ("http://programming.codingfy.com/wp-content/themes/parallax-child/demoapi.php"); //And connection settings. HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000); conn.setConnectTimeout(15000); conn.setRequestMethod("POST"); conn.setDoInput(true); conn.setDoOutput(true); //This is in case you need to send anything to the server. List queryParams = new ArrayList (); queryParams.add(new AbstractMap.SimpleEntry<>("dataToSend", "insert data you wish to send")); OutputStream os = conn.getOutputStream(); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(os, "UTF-8")); writer.write(getQuery(queryParams)); writer.flush(); writer.close(); os.close(); //You need this. conn.connect(); //And also this in order to successfully make the network call! BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( conn.getInputStream())); //Next, we are just getting things from the link, using the "city" tag. String next; while ((next = bufferedReader.readLine()) != null){ JSONArray ja = new JSONArray(next); for (int x = 0; x < ja.length(); x++) { JSONObject jo = (JSONObject) ja.get(x); cities.add(jo.getString("city")); } return "success"; } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); return "failed"; } return "Executed"; } /* This function is running back on the UI thread, so do not do anything that might block the thread here. */ @Override protected void onPostExecute(String result) { if(result.equals("success")){ for (String city : cities) { TextView tv = new TextView(MainActivity.this); tv.setText(city); linearLayoutToAddTo.addView(tv); } } } @Override protected void onPreExecute() {} @Override protected void onProgressUpdate(Void... values) { } } /* This is a helper function to help me send the query parameters. It works fine so it doesn't have to be edited. I always use it the way it is. */ public String getQuery(List params) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); boolean first = true; for (AbstractMap.SimpleEntry pair : params) { if (first) first = false; else result.append("&"); Log.d("sending key", pair.getKey().toString()); Log.d("sending value", pair.getValue().toString()); result.append(URLEncoder.encode(pair.getKey().toString(), "UTF-8")); result.append("="); result.append(URLEncoder.encode(pair.getValue().toString(), "UTF-8")); } return result.toString(); } }
And that's pretty much it, really. While the code above works fine, it might be a different story when integrated into existing code. If you run into trouble, please comment below and we'll do our best to help you!