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 methodFile 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 folderFile 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