HTTP Basics & Threads (Java)
This lesson starts the Networking section. Suppose RecipeBox adds a "Discover" screen that downloads a list of community-shared recipes from a server. Before touching any specific HTTP library, there is one rule that governs every networking approach on Android: never make a network call on the main (UI) thread. This lesson explains why, and shows the plain-Java tools for doing it correctly.
Why the Main Thread Is Off-Limits
Android runs all UI work — drawing, touch handling, animations — on a single thread, the main thread, also called the UI thread. A network request can take anywhere from a few milliseconds to many seconds, or hang entirely on a bad connection. If that call ran on the main thread, the entire app would freeze until it finished: no scrolling, no button taps, no animations. After roughly five seconds of an unresponsive main thread, Android shows the user an "Application Not Responding" (ANR) dialog and may kill the app.
The Deprecated Classic: AsyncTask
For years, AsyncTask was Android's built-in answer to "run this off the main thread, then update the UI afterward." It is officially deprecated since API 30, but it still appears constantly in older tutorials and legacy code, so it's worth recognizing:
// Legacy pattern — deprecated since API 30, shown for recognition only
private class FetchRecipesTask extends AsyncTask<String, Void, List<Recipe>> {
@Override
protected List<Recipe> doInBackground(String... urls) {
// Network call happens here, off the main thread
return downloadRecipes(urls[0]);
}
@Override
protected void onPostExecute(List<Recipe> recipes) {
// Back on the main thread automatically — safe to touch views here
adapter.submitList(recipes);
}
}
The Modern Java Approach: ExecutorService + runOnUiThread
In plain Java, the standard replacement is simple: an ExecutorService runs the network call on a background thread pool, and runOnUiThread() (or a Handler tied to the main Looper) posts the result back to the UI thread when it's ready.
// Fetching RecipeBox's recipe list on a background thread
private final ExecutorService networkExecutor = Executors.newSingleThreadExecutor();
public void loadDiscoverRecipes(String url) {
networkExecutor.execute(() -> {
List<Recipe> recipes = downloadRecipes(url); // runs on the background thread
runOnUiThread(() -> {
// runs back on the main thread — safe to update the RecyclerView
recipeAdapter.submitList(recipes);
});
});
}
- Executors.newSingleThreadExecutor() creates a background worker thread dedicated to network work, keeping the UI thread free.
- execute(() -> { ... }) hands the executor a block of code — the network call — to run there.
- runOnUiThread(), available on any Activity, hops back to the main thread to safely touch views like the RecyclerView adapter.
- Outside an Activity (e.g. in a plain class), the equivalent is new Handler(Looper.getMainLooper()).post(() -> { ... }).
Raw HTTP with HttpURLConnection
Java's own HttpURLConnection can make an HTTP request with zero external libraries. It's shown here once, purely for completeness — this is genuinely painful compared to a real HTTP library, and you would not want to write this by hand for every request in RecipeBox.
// Raw HttpURLConnection — verbose, shown once for completeness
private List<Recipe> downloadRecipes(String urlString) throws IOException {
HttpURLConnection connection = null;
try {
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new IOException("Server returned " + responseCode);
}
StringBuilder response = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
}
return parseRecipesJson(response.toString());
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
What Real Apps Use Instead
The main-thread rule and the executor pattern shown here stay the same no matter which HTTP tool you use — they're the foundation. But nobody writing a real app hand-rolls HttpURLConnection for every request. The next lessons cover the actual recommended tools:
| Lesson | What it covers |
|---|---|
| OkHttp (Java) | A modern, efficient HTTP client that handles connection pooling, timeouts, and interceptors for you |
| Retrofit (Java) + Gson | Turns a REST API directly into a Java interface, with JSON parsing built in |