Nicola Corti Google Developer Group Pisa
Android Software Engineer
@ Monetas AG
Prior to Froyo, HttpURLConnection had some frustrating bugs. In particular, calling close() on a readable InputStream could poison the connection pool...
...the large size of this API makes it difficult for us to improve it without breaking compatibility. The Android team is not actively working on Apache HTTP Client.
public static JSONObject requestRestResponse() {
HttpURLConnection urlConnection = null;
try {
// create connection
URL urlToRequest = new URL("http://mybackend.com/v1/req");
urlConnection = (HttpURLConnection)
urlToRequest.openConnection();
urlConnection.setConnectTimeout(CONNECTION_TIMEOUT);
urlConnection.setReadTimeout(DATARETRIEVAL_TIMEOUT);
// handle issues
int statusCode = urlConnection.getResponseCode();
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
// handle unauthorized (if service requires user login)
} else if (statusCode != HttpURLConnection.HTTP_OK) {
// handle any other errors, like 404, 500,..
}
// create JSON object from content
InputStream in =
new BufferedInputStream(urlConnection.getInputStream());
return new JSONObject(getResponseText(in));
} catch (MalformedURLException e) {
// URL is invalid
} catch (SocketTimeoutException e) {
// data retrieval or connection timed out
} catch (IOException e) {
// could not read response body
} catch (JSONException e) {
// response body is no valid JSON string
}
return null;
}} finally {
if (urlConnection != null) {
// Don't forget to release resources
urlConnection.disconnect();
}
}
return null;
}
Don't forget about NetworkOnMainThreadException. The most common solution for this kind of problems are AsyncTasks!
Have you ever wrote?
@Override
protected void onPostExecute(String result) {
if (getActivity() == null){
return; // Here Activity is gone...
}
...
}
If you have more than
1K lines
of code for creating and handling your HTTP requests...
Ask yourself if you're doing it right!
compile 'com.squareup.retrofit2:retrofit:2.0.0'
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(
@Path("user") String user);
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(
@Path("user") String user,
@Query("type") String type);
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(
@Path("user") String user,
@QueryMap Map<String, String> options);
}
public interface GitHubService {
@Headers("User-Agent: my-Awesome-Retrofit-powered-app")
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
@GET("gists/public")
Call<List<Gist>> listGists(@Header("User-Agent") String uAgent)
}
public interface GitHubService {
@GET
Call<List<User>> getCustomUsers(@Url String reqUrl);
@POST("gists")
Call<Gist> createGist(@Body GistRequest grequest);
}
com.squareup.retrofit:converter-gson
com.squareup.retrofit:converter-jackson
com.squareup.retrofit:converter-moshi
com.squareup.retrofit:converter-protobuf
com.squareup.retrofit:converter-wire
com.squareup.retrofit:converter-simplexml
You can also implement yours with Converter.Factory
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(JacksonConverterFactory.create())
.build();
service = retrofit.create(GitHubService.class);
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
service = retrofit.create(GitHubService.class);
You can plug a Call Adapter to work with:
// Sync call
Call<Repo> call = service.loadRepo();
Response<Repo> response = call.execute();
// Async call
Call<Repo> call = service.loadRepo();
call.enqueue(new Callback<Repo>() {
@Override
public void onResponse(Response<Repo> response) {
// Get result Repo from response.body()
}
@Override
public void onFailure(Throwable t) {
// Handle failure
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
public interface APIService {
@GET("gists/public")
Call<Gists> getGists();
@GET("gists/public")
Observable<Gists> getGistsRx();
}
getGistsRx().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(gists -> {
Toast.makeText(getApplicationContext(),
"Fetched gists: " + gists.size(),
Toast.LENGTH_SHORT).show();
});
public interface GitHubService {
@POST("/list")
Repo loadRepo();
@POST("/list")
void loadRepo(Callback<Repo> cb);
}
public interface GitHubService {
@POST("/list")
Call<Repo> loadRepo();
}
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
// Choose desired logging level
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // Or FULL
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.client(client)
.build();
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("example.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("example.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.build())
.build();
Use CertificatePinner to restrict which certificates and certificate authorities are trusted.
Certificate pinning increases security, but limits your server team’s abilities to update their TLS certificates.
Do not use certificate pinning without the blessing of your server’s TLS administrator!
From OkHTTP Official Wiki
call.cancel();
compile 'com.android.volley:volley:1.0.0'
RequestQueue queue =
Volley.newRequestQueue(getApplicationContext());
StringRequest request = new StringRequest(
Request.Method.GET,
"https://api.github.com/gists/public",
this::handleResponse,
this::handleError);
request.setShouldCache(true);
request.setTag(requestTag); // A class member 'requestTag'
queue.add(request);
@Override
protected void onStop() {
queue.cancelAll(requestTag);
super.onStop();
}
Don't forget to do it!
ImageLoader mImageLoader;
NetworkImageView mNetworkImageView;
private static final String IMAGE_URL =
"http://i.imgur.com/RLKixQW.png";
// Retrieve the ImageLoader (singleton/application/...)
// Retrieve the NetworkImageView (findViewById)
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true" />
mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> cache = new LruCache<>(20);
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
~$ adb shell setprop log.tag.Volley VERBOSE
D/Volley (670 ms) [ ] https://api.github.com/users/cortinico
D/Volley (+0 ) [ 1] add-to-queue
D/Volley (+0 ) [238] cache-queue-take
D/Volley (+0 ) [238] cache-miss
D/Volley (+10 ) [242] network-queue-take
D/Volley (+630 ) [242] network-http-complete
D/Volley (+0 ) [242] network-parse-complete
D/Volley (+0 ) [242] network-cache-written
D/Volley (+0 ) [242] post-response
D/Volley (+30 ) [ 1] done
D/Volley (10 ms) [ ] https://api.github.com/users/cortinico
D/Volley (+0 ) [ 1] add-to-queue
D/Volley (+0 ) [238] cache-queue-take
D/Volley (+0 ) [238] cache-hit
D/Volley (+0 ) [238] cache-hit-parsed
D/Volley (+0 ) [238] post-response
D/Volley (+10 ) [ 1] done