Updated 8 days ago | GitHub

Sending and Managing Network Requests

Overview

Network requests are used to retrieve or modify API data or media from a server. This is a very common task in Android development especially for dynamic data-driven clients.

The underlying Java class used for network connections is HttpURLConnection. It is lower-level and requires completely manual management of parsing the data from the input stream and executing the request asynchronously. The Apache HTTP Client (including DefaultHttpClient) was removed in Android 6.0 (API 23); it is no longer available in the platform SDK. The reason for the historical two-clients situation is described in this blog article.

For most common cases, we are better off using a lightweight library called AsyncHttpClient or OkHttp which will handle the entire process of sending and parsing network requests for us in a more robust and easy-to-use way.

Permissions

In order to access the internet, be sure to specify the following permissions in AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.simplenetworking"
    android:versionCode="1"
    android:versionName="1.0" >
 
   <uses-permission android:name="android.permission.INTERNET" /> 
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

</manifest>

Cleartext HTTP requests

NOTE: Insecure HTTP requests (i.e. http://) are no longer permitted as of Android P. If you see CLEARTEXT_NOT_PERMITTED errors, it means this policy is being enforced. Only secure HTTPS (https://) are now allowed. Disabling clear text permissions can be done as shown in this Google code lab exercise or directly by using the useCleartextTraffic attribute in your AndroidManifest.xml file:

<!-- WARNING: USE ONLY FOR TESTING -->
<application android:usesCleartextTraffic="true">

Sending an HTTP Request (Third Party)

There are at least three major third-party networking libraries you should consider using.

  • See the Android Async Http Client guide for making basic network calls. It is the library often used for learning Android but would not be used in a production application.

  • See the OkHttp guide for making synchronous and asynchronous calls.

    • See also the Retrofit guide, which uses OkHttp and makes it easier to make more RESTful API calls. Read through this guide to understand how the Gson library works with Retrofit.
  • Check out the Volley guide, a library built by Google that has fallen out of favor for OkHttp. It was one of the first networking libraries released for Android and provides a more convenient way to make networking requests than using bare HttpURLConnection on hand-managed background threads.

There can be a bit of a learning curve when using these libraries, so your best bet when first learning is to use Android Async Http Client or Volley. With OkHttp you also have to deal with the complexity of whether your callbacks need to be run on the main thread to update the UI, as explained in the guide.

Here is a comparison of the different aspects of the libraries.

Android Async Http OkHttp Volley
Debugging Use Stetho Use LogInterceptor Use verbose mode
Disk Caching Yes Yes Yes
Request Queueing No No Included
Remote Image Fetching Manual Requires Picasso or Glide Included
Animated GIF Support No Requires Glide Requires Glide
Release Cadence Monthly Monthly Infrequent
Transport Layer OkHttp OkHttp HttpUrlConnection (or OkHttp)
Synchronous Calls N/A execute() instead of enqueue() use RequestFuture
HTTP/2 Yes Yes Works with OkHttp
Automatic Gzip processing Yes Yes No (unless using OkHttp)
Author Roger Hu Square Google

One hint with Android Async Http Client is that the library enables Stetho to observe network traces that are useful for debugging. Volley provides remote fetching images out of the box, while Android Async Http client requires more manual work and OkHttp needs the Picasso or Glide library in order to do so.

Another important point is that OkHttp is not only a standalone networking library but also can be used for the underlying implementation for HttpUrlConnection. For this reason, Volley can also leverage OkHttp to support automatic Gzip and HTTP/2 processing.

Sending an HTTP Request (The “Hard” Way)

Sending an HTTP Request with only the built-in HttpURLConnection involves the following conceptual steps:

  1. Declare a URL Connection
  2. Open InputStream to connection
  3. Download and decode based on data type
  4. Execute the request on a background thread and deliver the result back to the main thread

This would translate to the following networking code to send a simple request (with try-catch structured exceptions not shown here for brevity):

// 1. Declare a URL Connection
URL url = new URL("https://www.google.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2. Open InputStream to connection
conn.connect();
InputStream in = conn.getInputStream();
// 3. Download and decode the string response using builder
StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
    stringBuilder.append(line);
}

The fourth step is required because Android throws a NetworkOnMainThreadException if you perform network I/O on the main thread. Earlier versions of this guide used AsyncTask for this step, but AsyncTask was deprecated in API level 30 with the guidance to “Use the standard java.util.concurrent or Kotlin concurrency utilities instead”. In Java, that means running the request on an ExecutorService and posting the result back to the main thread with a Handler:

private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper());

private void downloadResponseFromNetwork() {
    // 4. Execute the request on a background thread
    executor.execute(() -> {
        // ... code shown above to send request and build the string response
        String response = stringBuilder.toString();
        // ... and deliver the result back to the main thread
        mainHandler.post(() -> {
            // This runs on the main thread with access to the result
            // DO SOMETHING WITH STRING RESPONSE
        });
    });
}

In Kotlin, the same step is a coroutine scoped to the screen’s lifecycle (via lifecycleScope from the androidx.lifecycle:lifecycle-runtime-ktx artifact), with the blocking I/O shifted onto Dispatchers.IO:

lifecycleScope.launch {
    val response = withContext(Dispatchers.IO) {
        // ... code shown above to send request and build the string response
        stringBuilder.toString()
    }
    // Back on the main thread with access to the result
    // DO SOMETHING WITH STRING RESPONSE
}

Displaying Remote Images (The “Easy” Way)

Displaying images is easiest using a third party library such as Glide which will download and cache remote images and abstract the complexity behind an easy to use DSL:

String imageUri = "https://i.imgur.com/tGbaZCY.jpg";
ImageView ivBasicImage = (ImageView) findViewById(R.id.ivBasicImage);
Glide.with(context).load(imageUri).into(ivBasicImage);

Refer to our Glide Guide for more detailed usage information and configuration.

Displaying Remote Images (The “Hard” Way)

Suppose we wanted to load an image using only the built-in Android network constructs. In order to download an image from the network, convert the bytes into a bitmap and then insert the bitmap into an imageview, you would use the following pseudo-code:

// 1. Declare a URL Connection
URL url = new URL("https://i.imgur.com/tGbaZCY.jpg");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2. Open InputStream to connection
conn.connect();
InputStream in = conn.getInputStream();
// 3. Download and decode the bitmap using BitmapFactory
Bitmap bitmap = BitmapFactory.decodeStream(in);
in.close();
// 4. Insert into an ImageView
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(bitmap);

Here’s the complete code needed to download a remote image off the main thread and display it in an ImageView using just the official Google Android SDK, written with Kotlin coroutines:

class MainActivity : AppCompatActivity() {
    private lateinit var ivBasicImage: ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ivBasicImage = findViewById(R.id.ivBasicImage)
        val url = "https://i.imgur.com/tGbaZCY.jpg"
        // Download image from URL and display within ImageView
        lifecycleScope.launch {
            val bitmap = withContext(Dispatchers.IO) { downloadBitmap(url) }
            bitmap?.let { ivBasicImage.setImageBitmap(it) }
        }
    }

    // Runs on Dispatchers.IO; returns null if the download fails
    private fun downloadBitmap(address: String): Bitmap? {
        return try {
            // 1. Declare a URL Connection
            val conn = URL(address).openConnection() as HttpURLConnection
            // 2. Open InputStream to connection
            conn.connect()
            // 3. Download and decode the bitmap using BitmapFactory
            conn.inputStream.use { BitmapFactory.decodeStream(it) }
        } catch (e: IOException) {
            Log.e("MainActivity", "Exception downloading image", e)
            null
        }
    }
}

The Java equivalent follows the same shape as the request example above: run steps 1–3 on an ExecutorService, then post imageView.setImageBitmap(bitmap) back to the main thread through a Handler(Looper.getMainLooper()).

Of course, doing this the “hard” way is not recommended. In most cases, to avoid having to manually manage caching and download management, we are better off utilizing existing third-party libraries.

Note: If you use the approach above to download and display many images within a ListView or RecyclerView, you can end up spinning up an unbounded number of concurrent downloads, and recycled views whose downloads finish late will display the wrong image. Managing a bounded pool of downloads and canceling stale requests when a view is recycled is exactly the complexity that libraries such as Glide handle for you.

Checking for Network Connectivity

Checking Network is Connected

First, make sure to setup the android.permission.ACCESS_NETWORK_STATE permission as shown above. To verify network availability you can then define and call this method:

private Boolean isNetworkAvailable() {
    ConnectivityManager connectivityManager 
          = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
}

Note that having an active network interface doesn’t guarantee that a particular networked service is available or that the internet is actually connected. Network issues, server downtime, low signal, captive portals, content filters and the like can all prevent your app from reaching a server. For instance you can’t tell for sure if your app can reach Twitter until you receive a valid response from the Twitter service.

See this official connectivity guide for more details.

Checking the Internet is Connected

To verify if the device is actually connected to the internet, we can use the following method of pinging the Google DNS servers to check for the expected exit value:

public boolean isOnline() {
    Runtime runtime = Runtime.getRuntime();
    try {
        Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
        int     exitValue = ipProcess.waitFor();
        return (exitValue == 0);
    } catch (IOException e)          { e.printStackTrace(); } 
      catch (InterruptedException e) { e.printStackTrace(); }
    return false;
}

Note that this does not need to be run in background and does not require special privileges. See this stackoverflow post for the source of this solution.

Troubleshooting

Take a look at Troubleshooting API Calls to understand how to gain better visibility about what your network calls are doing.

References