-
Notifications
You must be signed in to change notification settings - Fork 0
Sending and Managing Network Requests
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 or DefaultHTTPClient. Both of these are lower-level and require completely manual management of parsing the data from the input stream and executing the request asynchronously.
For most common cases, we are better off using a popular third-party library called android-async-http which will handle the entire process of sending and parsing network requests for us in a more robust and easy-to-use way.
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>
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();
}
See this official connectivity guide for more details.
Using a third-party library such as android-async-http, sending an HTTP request is quite straightforward. First, we add the library to gradle:
dependencies {
compile 'com.loopj.android:android-async-http:1.4.8'
}
Android Marshmallow Compatibility Issues
-
Apache HTTP client (a dependency of android-async-http) has been removed from Marshmallow. If your app targets API 23, you'll need to add the following to your gradle file until the library is updated:
android { compileSdkVersion 23 useLibrary 'org.apache.http.legacy' // required if compileSdkVersion >= 23 }
-
You may also need to add
import org.apache.http.Header;
manually to your Java file. There is a current bug in Android Studio 1.3.1 where it may not recognized this added library. Assuming you have included theuseLibrary
statement, your build should however compile successfully.
Now, we just create an AsyncHttpClient
, and then execute a request specifying an anonymous class as a callback:
import com.loopj.android.http.*;
import org.apache.http.Header;
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("key", "value");
params.put("more", "data");
client.get("http://www.google.com", params, new TextHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, String res) {
// called when response HTTP status is "200 OK"
}
@Override
public void onFailure(int statusCode, Header[] headers, String res, Throwable t) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
}
);
This will automatically execute the request asynchronously and fire the onSuccess
when the response returns a success code and onFailure
if the response does not.
Similar to sending a regular HTTP request, android-async-http can also be used for sending JSON API requests:
String url = "https://ajax.googleapis.com/ajax/services/search/images";
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("q", "android");
params.put("rsz", "8");
client.get(url, params, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
// Root JSON in response is an dictionary i.e { "data : [ ... ] }
// Handle resulting parsed JSON response here
}
@Override
public void onFailure(int statusCode, Header[] headers, String res, Throwable t) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
});
The request will be sent out with the appropriate parameters passed in the query string and then the response will be parsed as JSON and made available within onSuccess
. Check the Converting JSON to Models guide for more details on parsing a JSON response.
API requests tend to be JSON or XML responses that are sent to a server and then the result needs to be parsed and processed as data models on the client. In addition, many API requests require authentication in order to identify the user making the request. This is typically done with a standard OAuth process for authentication.
Fortunately, there are several OAuth libraries out there to simplify the process of authentication such as scribe and signpost. You can explore several examples of using scribe or signpost to authenticate.
We have also created a meta-library to make this process as simple as possible called android-oauth-handler and a skeleton app to act as a template for a simple rest client called android-rest-client-template. You can see the details of these libraries by checking out their respective READMEs.
Using these wrappers, you can then send an API request and properly process the response using code like this:
// SomeActivity.java
RestClient client = RestClientApp.getRestClient();
RequestParams params = new RequestParams();
params.put("key", "value");
params.put("more", "data");
client.getHomeTimeline(1, new JsonHttpResponseHandler() {
public void onSuccess(int statusCode, Header[] headers, JSONObject json) {
// Response is automatically parsed into a JSONArray
// json.getJSONObject(0).getLong("id");
// Here we want to process the json data into Java models.
}
public void onFailure(int statusCode, Header[] headers, Throwable t, JSONObject e) {
// Handle the failure and alert the user to retry
Log.e("ERROR", e.toString());
}
});
Note that as shown above you should also handle failure cases with [JsonHttpResponseHandler](http://loopj.com/android-async-http/doc/com/loopj/android/http/JsonHttpResponseHandler.html#onFailure\(java.lang.Throwable, org.json.JSONObject)) using the onFailure
method so your application is robust to "losing internet" and user doesn't become confused with unexpected results.
Sending an HTTP Request involves the following conceptual steps:
- Declare a URL Connection
- Open InputStream to connection
- Download and decode based on data type
- Wrap in AsyncTask and execute in background 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("http://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 requires this networking request to be executed in a background task using AsyncTasks such as shown below:
// The types specified here are the input data type, the progress type, and the result type
// Subclass AsyncTask to execute the network request
// String == URL, Void == Progress Tracking, String == Response Received
private class NetworkAsyncTask extends AsyncTask<String, Void, Bitmap> {
protected String doInBackground(String... strings) {
// Some long-running task like downloading an image.
// ... code shown above to send request and retrieve string builder
return stringBuilder.toString();
}
protected void onPostExecute(String result) {
// This method is executed in the UIThread
// with access to the result of the long running task
// DO SOMETHING WITH STRING RESPONSE
}
}
private void downloadResponseFromNetwork() {
// 4. Wrap in AsyncTask and execute in background thread
new NetworkAsyncTask().execute("http://google.com");
}
Displaying images is easiest using a third party library such as Picasso from Square which will download and cache remote images and abstract the complexity behind an easy to use DSL.
Adding Picasso to our app/build.gradle
file:
dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
}
We can then load a remote image into any ImageView
with:
String imageUri = "https://i.imgur.com/tGbaZCY.jpg";
ImageView ivBasicImage = (ImageView) findViewById(R.id.ivBasicImage);
Picasso.with(context).load(imageUri).into(ivBasicImage);
We can do more sophisticated work with Picasso configuring placeholders, error handling, adjusting size of the image, and scale type with:
Picasso.with(context).load(imageUri).fit().centerCrop()
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
For more details check out the Picasso documentation.
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 construct an AsyncTask
that downloads a remote image and displays the image in an ImageView
using just the official Google Android SDK. See the Creating and Executing Async Tasks for more information about executing asynchronous background tasks:
public class MainActivity extends Activity {
private ImageView ivBasicImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivBasicImage = (ImageView) findViewById(R.id.ivBasicImage);
String url = "https://i.imgur.com/tGbaZCY.jpg";
// Download image from URL and display within ImageView
new ImageDownloadTask(ivBasicImage).execute(url);
}
// Defines the background task to download and then load the image within the ImageView
private class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
ImageView imageView;
public ImageDownloadTask(ImageView imageView) {
this.imageView = imageView;
}
protected Bitmap doInBackground(String... addresses) {
Bitmap bitmap = null;
InputStream in;
try {
// 1. Declare a URL Connection
URL url = new URL(addresses[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2. Open InputStream to connection
conn.connect();
in = conn.getInputStream();
// 3. Download and decode the bitmap using BitmapFactory
bitmap = BitmapFactory.decodeStream(in);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(in != null)
in.close();
}
return bitmap;
}
// Fires after the task is completed, displaying the bitmap into the ImageView
@Override
protected void onPostExecute(Bitmap result) {
// Set bitmap image for the result
imageView.setImageBitmap(result);
}
}
}
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 creating your own libraries or in most cases utilizing existing third-party libraries.
Note: If you use the approach above to download and display many images within a ListView, you might run into some threading issues that cause buggy loading of images. The blog post Multithreading for Performance offers a solution in which you manage the active remote downloading background tasks to ensure that too many tasks are not being spun up at once.
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.
Interested in ramping up on Android quickly?
(US Only) If you are an existing engineer with 2+ years of professional experience in software development and are serious about ramping up on Android quickly, be sure to apply for our free evening 8-week Android bootcamp.
We've trained over a thousand engineers from top companies including Apple, Twitter, Airbnb, Uber, and many others leveraging this program. The course is taught by Android experts from the industry and is specifically designed for existing engineers.
Not in the United States? Please fill out our application of interest form and we’ll notify you as classes become available in your area powered by local organizers.