ANDROID HTTP CLIENT & IMAGE LOADER
VOLLEY
Why volley?
- Simple
- Powerful
- Extendable
- Build-in memory cache
- Build-in disk cache
how to use it?
Step 1 - create request queue
RequestQueue requestQueue =
Volley.newRequestQueue(context.getApplicationContext());
Step 2 - create request
StringRequest request = new StringRequest(
Request.Method.GET,
url,
listener,
errorListener);
Step 3 - create listeners
Response.Listener<String> listener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
L.d("Success Response: " + response.toString());
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error.networkResponse != null) {
L.d("Error Response code: " +
error.networkResponse.statusCode);
}
}
};
step 4 - add request to queue
requestQueue.add(request);
Request methods
- Request.Method.GET
- Request.Method.POST
- Request.Method.PUT
- Request.Method.DELETE
Request types
Every request listener returns appropriate type
String / JSON Object / JSON Array / Bitmap
or you can create your own..
public class CookieRequest extends StringRequest {
private String mCookieValue;
public CookieRequest(String url, String cookieValue,
Response.Listener<String> listener,
Response.ErrorListener errorListener) {
super(Method.GET, url, listener, errorListener);
mCookieValue = cookieValue;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> map = new HashMap<String, String>();
map.put("Cookie", mCookieValue);
return map;
}
}
how to pass post request parameters?
@Override getParams() method
StringRequest request = new StringRequest(
Request.Method.POST,
url,
listener,
errorListener) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = new HashMap<String, String>();
map.put("name", "Jon Doe");
map.put("age", "21");
return map;
}
};
how to set request retry policy?
StringRequest request = new StringRequest(
Request.Method.GET,
url,
listener,
errorListener);
request.setRetryPolicy(
new DefaultRetryPolicy(
DefaultRetryPolicy.DEFAULT_TIMEOUT_MS, // 2500
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, // 1
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); //1f
HTTP Basic Auth
StringRequest request = new StringRequest(
Request.Method.GET,
url,
listener,
errorListener) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return createBasicAuthHeader("user", "passwd");
}
};
Map<String, String>
createBasicAuthHeader(String username, String password) {
Map<String, String> headerMap = new HashMap<String, String>();
String credentials = username + ":" + password;
String base64EncodedCredentials =
Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
headerMap.put("Authorization", "Basic " + base64EncodedCredentials);
return headerMap;
}
how to cancel request
StringRequest request1 = new StringRequest(...);
request1.setTag("weather-screen");
StringRequest request2 = new StringRequest(...);
request2.setTag("weather-screen");
requestQueue.add(request1);
requestQueue.add(request2);
to cancel request you just need to remember
request tag
requestQueue.cancelAll("weather-screen");
i need to instantiate request queue in each
Service or Activity?
no
i need to create singleton wrapper?
yes
Why ?
- Request Queue object creation is quite expensive
- You will be able to access and cancel all your requests
- You will be able to access and clear your cache
example
Request Proxy
- wrapper for RequestQueue
- holds list of all application requests
Request Manager
- singleton which hold RequestProxy
request proxy
public class RequestProxy {
private RequestQueue mRequestQueue;
// package access constructor
RequestProxy(Context context) {
mRequestQueue =
Volley.newRequestQueue(context.getApplicationContext());
}
public void login() {
// login request
}
public void weather() {
// weather request
}
}
Request Manager
part 1
public class RequestManager {
private static RequestManager instance;
private RequestProxy mRequestProxy;
private RequestManager(Context context) {
mRequestProxy = new RequestProxy(context);
}
public RequestProxy doRequest() {
return mRequestProxy;
}
.. init methods see part 2
}
REQUEST MANAGER
PART 2
public static synchronized RequestManager getInstance(Context context) {
if (instance == null) {
instance = new RequestManager(context);
}
return instance;
}
public static synchronized RequestManager getInstance() {
if (instance == null) {
throw new IllegalStateException
(RequestManager.class.getSimpleName() +
" is not initialized, call getInstance(..) method first.");
}
return instance;
}
usage sample
initialize request manager in application class
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// RequestManager initialization
RequestManager.getInstance(getApplicationContext());
}
}
start requests
RequestManager.getInstance().doRequest().login(..);
RequestManager.getInstance().doRequest().weather(..);
image loader
you need to load image?
we have view for this!
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/imgAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
just set up url and image loader
NetworkImageView imgAvatar =
(NetworkImageView) findViewById(R.id.imgAvatar);
imageView.setImageUrl(url, imageLoader);
imageView.setDefaultImageResId(..);
imageView.setErrorImageResId(..);
wait what?.. image loader?
YES
to create image loader we need 2 things:
-
RequestQueue
-
ImageCache
ImageLoader imageLoader =
new ImageLoader(Volley.newRequestQueue(context), imageCache);
memory cache part 1
public class BitmapLruCache
extends LruCache<String, Bitmap>
implements ImageLoader.ImageCache {
public BitmapLruCache() {
this(getDefaultLruCacheSize());
}
public BitmapLruCache(int sizeInKiloBytes) {
super(sizeInKiloBytes);
}
.. more methods see part 2
public static int getDefaultLruCacheSize() {
final int maxMemory =
(int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
return cacheSize;
}
}
MEMORY CACHE PART 2
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
put it all together
ImageLoader.ImageCache imageCache = new BitmapLruCache();
ImageLoader imageLoader = new ImageLoader(Volley.newRequestQueue(context), imageCache);
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/imgAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
NetworkImageView imgAvatar =
(NetworkImageView) findViewById(R.id.imgAvatar);
imageView.setImageUrl(url, imageLoader);
Are we ready ?
NO
why ?
we need disk cache
DIG IT
Volley.newRequestQueue(context)
more..
// Inside newRequestQueue method
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
Even more..
Method description: getCacheDir()
You should always have a reasonable maximum, such as 1 MB, for the amount of space you consume with cache files, and prune those files when exceeding that space.
is 1 mb
reasonable enough
for
image loader ?
we need to use external cache directory
to cache images
and replace
Volley.newRequestQueue(context)
with our own implementation
configure request queue part 1
// Default maximum disk usage in bytes. private static final int DEFAULT_DISK_USAGE_BYTES = 25 * 1024 * 1024;
// Default cache folder name private static final String DEFAULT_CACHE_DIR = "photos";
private static RequestQueue newRequestQueue(Context context) {
// define cache folder
File rootCache = context.getExternalCacheDir(); if (rootCache == null) { L.w("Can't find External Cache Dir, " + "switching to application specific cache directory"); rootCache = context.getCacheDir(); }
File cacheDir = new File(rootCache, DEFAULT_CACHE_DIR); cacheDir.mkdirs();
.. more lines of code see part 2
CONFIGURE REQUEST QUEUE PART 2
HttpStack stack = new HurlStack();
Network network = new BasicNetwork(stack);
DiskBasedCache diskBasedCache =
new DiskBasedCache(cacheDir, DEFAULT_DISK_USAGE_BYTES);
RequestQueue queue = new RequestQueue(diskBasedCache, network);
queue.start();
return queue;
}
PUT IT ALL TOGETHER... again
ImageLoader.ImageCache imageCache = new BitmapLruCache();
ImageLoader imageLoader =
new ImageLoader(newRequestQueue(context), imageCache);
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/imgAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
NetworkImageView imgAvatar =
(NetworkImageView) findViewById(R.id.imgAvatar);
imageView.setImageUrl(url, imageLoader);
now it should work..
i don't want view, i want bitmap!
don't panic, use image request..
new ImageRequest(
url,
listener,
maxWidth,
maxHeight,
decodeConfig,
errorListener);
Response.Listener<Bitmap> listener = new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
// use your bitmap
}
};
you should know
Volley decides whether to cache response or not
based only on headers "Cache-Control" and "Expires"
Override parseNetworkResponse method and
implement your own parseCacheHeaders method.
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
try {
String result = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(result,
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
Volley
By Dmytro Danylyk
Volley
- 7,167