Handle Empty or Null Cases

protected Event doInBackground(String... urls){
	if (urls.length < 1 || urls[0] == null){
		return null;
	}

	Event result = Utils.fetchEarthquakeData(urls[0]);
	return result;
}
protected void onPostExecute(Event result){
	if (result == null){
		return;
	}
	updateUi(result);
}
private class EarthquakeAsyncTask extends AsyncTask {

	protected Event doInBackground(String... urls){
		if (urls.length < 1 || urls[0] == null){
			return null;
		}

		Event result = Utils.fetchEarthquakeData(urls[0]);
		return result;
	}

	protected void onPostExecute(Event result){
		if (result == null){
			return;
		}

		updateUi(result);
	}

}

Asynctask callback methods

onPreExecute(): before the task is executed, Main
doInBackground(Params…): After onPreExecute, Background
onProgressUpdate(Progress…): After publishProgress() is called, while doInBackground is executing, Main
OnPostExecute(Result): After doInBackground() finishes, Main

Review of generics
ArrayList
add(E e)->requires object of type E as input
get(int index)->returns object of type E
ArrayAdapter

private class DownloadFileTask extends AsyncTask{
	protected Long doInBackground(RUL... urls){
		int count = urls.length;
		long totalSize = 0;
		for(int i = 0; i < count; i++){
			totalSize += Downloader.downloadFile(url[i]);
			publishProgress((int)((i/(float) count)* 100));
			if(isCancelled()) break;
		}
		return totalSize;
	}
	protected void onProgressUpdate(Integer... progress){
		setProgressPercent(progress[0]);
	}
	protected void onPostExecute(long result){
		showDialog("Downloaded " + result + " bytes");
	}
}
private class DownloadWebpageTask extends AsyncTask<String, Void, String>{
	@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){
		textView.setText(result);
	}
}

Main Thread vs Background Thread

Background Thread(In) -> Perform Network Request -> Out
Main Thread(In) -> Click Button -> Out

Asynctask to the rescue
Async -> 非同期
https://developer.android.com/reference/android/os/AsyncTask.html

 private class DownloadFilesTask extends AsyncTask {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

concrete class
abstract class
interface

public class Main Activity extends Activity {
	onCreate(){

	}
	onCreate(){

	}
}

public class EarthquakeAsyncTask extends AsyncTask {
	doInBackground(){
		
	}
}

Threads & Parallelism

Event.java

package com.example.android.didyoufeelit;

public class Event {
	public final String title;

	public final String numOfPeople;

	public final String perceivedStrength;

	public Event(String eventTitle, String eventNumOfPeople, String eventPerceivedStrength){
		title = eventTitle;
		numOfPeople = eventNumOfPeople;
		perceivedStrength = eventPerceivedStrength;
	}
}
package com.example.android.didyoufeelit;

import android.os.Bundle;
import android.support.v7.app.AppCompactActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
	private static final String USGS_REQUEST_URL =
		"http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2016-01-01&endtime=2016-05-02&minfelt=50&minmagnitude=5";

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

		Event earthquake = Utils.fetchEarthquakeData(USGS_REQUEST_URL);

		updateUi(eqrthquake);
	}

	private void updateUi(Event earthquake){
		TextView titleTextView = (TextView) findViewById(R.id.title);
		titleTextView.setText(earthquake.title);

		TextView tsunamiTextView = (TextView) findViewById(R.id.number_of_people);
		tsunamitextView.setText(getString(R.string.num_people_felt_it, earthquake.numOfPeople));

		TextView magnitudeTextView = (TextView) findViewById(R.id.perceive_magnitude);
		magnitudeTextView.setText(earthquake.perceivedStrength);
	}	
}

Reading from an input stream

the purpose of an InputStream
Represents a stream of bytes (small chunks of data)

the purpose of an BufferedReader
Helps us read text from an InputStream

String: Immutable(can’t change once created)
StringBuilder: Mutable(Can change once created)

StringBuilder builder = new StringBuilder();
builder.append("World");
builder.deleteCharAt(3);
builder.append(" builder").append(".");
String built = builder.toString();

what exception is caught
in the makeHttpRequest() method?
IOException
in the extractFeatureFromJson() method?
JSONException

Reading from an Input

private String readFromStream(InputStream inputStream) throws IOException {
	StringBuilder output = new StringBuilder();
	if (inputStream != null){
		InputStreamReader inputStreamReader =
			new InputStreamReader(inputStream, Charset.forName("UTF-8"));
		BufferedReader reader = new BufferedReader(inputStreamReader);
		String line = reader.readLine();
		while (line != null){
			output.append(line);
			line = reader.readLine();
		}
	}
	return output.toString();
}

HTTP Status Codes

Status code: Description
200: OK, request received, everything normal
301: moved permanently
404: Page not found
500: Internal server error

List of HTTP status codes
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3xx Redirection
4xx Client Error
5xx Server Error

the request is successful
200
the request is unsuccessful
400

EX

Error 400: Bad Request

Bad endtime value "2014-01-02asdfasdf". Valid values are ISO-8601 timestamps.

Usage details are available from https://earthquake.usgs.gov/fdsnws/event/1

200 response code -> Proceed with reading input stream and parsing the JSON response
Any other response code -> Return empty string for the JSON response

Abstraction

Android System Architecture
Physical Device Hardware, Android Operating System, Framework, App

urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.connect();

GET(Read):Requests data from a specified resource
POST(Write):Submits data to be processed to a specified resource
https://www.w3schools.com/tags/ref_httpmethods.asp

HTTP request method
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods

Why are we using a GET instead of a POST?
1.You want to retrieve data from the server
2.You’re not posting new information to the server

url connection
https://developer.android.com/reference/java/net/URLConnection.html?utm_source=udacity&utm_medium=course&utm_campaign=android_basics#connect()

HTTP request and response

Android client
1. form http request, send request to USGS

working with URL
https://docs.oracle.com/javase/tutorial/networking/urls/

Web API Endpoint Reference

Web API Endpoint Reference

HTTPConection
https://developer.android.com/reference/java/net/HttpURLConnection.html

   URL url = new URL("http://www.android.com/");
   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
   try {
     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
     readStream(in);
   } finally {
     urlConnection.disconnect();
   }

Soonami

package com.example.android.soonami;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;

public class MainActivity extends AppCompatActivity {
	public static final String LOG_TAG = MainActivity.class.getSimpleName();

	private static final String USGS_REQUEST_URL =
		"http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2012-01-01&endtime=2012-12-01&minmagnitude=6";

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

		TsunamiAsyncTask task = new TsunamiAsyncTask();
		task.execute();
	}

	private void updateUi(Event earthquake){
		TextView titleTextView = (TextView) findViewById(R.id.title);
		titleTextView.setText(earthquake.title);

		TextView dateTextView = (TextView) findViewById(R.id.date);
		dateTextView.setText(getDateString(earthquake.time));

		TextView tsunamiTextView = (TextView) findViewById(R.id.tsunami_alert);
		titleTextView.setText(earthquake.title);
	}

	private String getDateString(long timeInMilliseconds){
		SimpleDateFormat formatter = new SimpleDateFormat("EEE, d MMM yyy 'at' HH:mm:ss z");
		return formatter.format(timeInMilliseconds);
	}

	private String getTsunamiAlertString(int tsunamiAlert){
		switch (tsunamiAlert){
			case 0:
				return getString(R.string.alert_no);
			case 1:
				return getString(R.string.alert_yes);
			default:
				return getString(R.string.alert_no_available);
		}
	}

	private class TsunamiAsyncTask extends AsyncTask<URL, Void, Event>{

		@Override
		protected Event doInBackground(URL... urls){
			// Create URL object
			URL url = createUrl(USGS_REQUEST_URL);
		} catch (IOException e){
			// todo handle the IOException
		}

		Event earthquake = extractFeatureFromJson(jsonResponse);

		return earthquake;
	}

	@Override
	protected void onPostExecute(Event earthquake){
		if (earthquake == null){
			return;
		}

		updateUi(earthquake);
	}

	private URL createUrl(String stringUrl){
		URL url = null;
		try {
			url = new URL(stringUrl);
		} catch (MalformedURLException exception){
			Log.e(LOG_TAG, "Error with creating URL", exception);
			return null;
		}
		return url;
	}

	private String makeHttpRequest(URL url) throws IOException {
		String jsonResponse = "";
		HttpURLConnection urlConnection = null;
		InputStream inputStream = null;
		try {
			urlConnection = (HttpURLConnection) url.openConnection();
			urlConnection.setRequestMethod("GET");
			urlConnection.setReadTimeout(10000 /* milliseconds */);
			urlConnection.setConnectTimeout(15000 /* milliseconds */);
			urlConnection.connect();
			inputStream = urlConnection.getInputStream();
			jsonResponse = readFromStream(inputStream);
		} catch (IOException e){
			// todo
		} finally {
			if (urlConnection != null){
				urlConnection.disconnect();
			}
			if (inputStream != null){
				inputStream.close();
			}
		}
		return jsonResponse;
	}

	private String readFromStream(InputStream inputStream) throws IOException {
		StringBuilder output = new StringBuilder();
		if (inputStream != null){
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
			BufferedReader reader = new BufferedReader(inputStreamReader);
			String line = reader.readLine();
			while (line != null){
				output.append(line);
				line = reader.readLine();
			}
		}
		return output.toString();
	}

	private Event extractFeatureFromJson(String earthquakeJSON){
		try {
			JSONObject baseJsonResponse = new JSONObject(earthquakeJSON);
			JSONArray featureArray = baseJsonResponse.getJSONArray("features");

			if (featureArray.length() > 0){
				JSONObject firstFeature = featureArray.getJSONObject(0);
				JSONObject properties = firstFeature.getJSONObject("properties");

				String title = properties.getString("title");
				long time = properties.getLong("time");
				int tsunamiAlert = properties.getInt("tsunami");

				return new Event(title, time, tsunamiAlert);
			}
		} catch (JSONException e){
			Log.e(LOG_TAG, "Problem parsing the earthquake JSON results", e);
		}
		return null;
	}

}