網絡通訊框架之volley

介紹

咱們平時在開發Android應用的時候不可避免地都須要用到網絡技術,而多數狀況下應用程序都會使用HTTP協議來發送和接收網絡數據。Android系統中主要提供了兩種方式來進行HTTP通訊,HttpURLConnection和HttpClient,幾乎在任何項目的代碼中咱們都能看到這兩個類的身影,使用率很是高。前端

不過HttpURLConnection和HttpClient的用法仍是稍微有些複雜的,若是不進行適當封裝的話,很容易就會寫出很多重複代碼。因而乎,一些Android網絡通訊框架也就應運而生,好比說AsyncHttpClient,它把HTTP全部的通訊細節所有封裝在了內部,咱們只須要簡單調用幾行代碼就能夠完成通訊操做了。再好比Universal-Image-Loader,它使得在界面上顯示網絡圖片的操做變得極度簡單,開發者不用關心如何從網絡上獲取圖片,也不用關心開啓線程、回收圖片資源等細節,Universal-Image-Loader已經把一切都作好了。
java

Android開發團隊也是意識到了有必要將HTTP的通訊操做再進行簡單化,因而在2013年Google I/O大會上推出了一個新的網絡通訊框架——Volley。Volley但是說是把AsyncHttpClient和Universal-Image-Loader的優勢集於了一身,既能夠像AsyncHttpClient同樣很是簡單地進行HTTP通訊,也能夠像Universal-Image-Loader同樣輕鬆加載網絡上的圖片。除了簡單易用以外,Volley在性能方面也進行了大幅度的調整,android

Volley的設計目標就是很是適合去進行數據量不大,但通訊頻繁的網絡操做,而對於大數據量的網絡操做,好比說下載文件等,Volley的表現就會很是糟糕。json

應用場景:數據量不大,但網絡通訊頻繁的,處理高併發的小請求就很是適合使用Volley。數組


 

使用步驟緩存

對於網絡通訊須要在AndroidManifest.xml添加網絡權限:<uses-permission android:name="android.permission.INTERNET"/> 服務器

StringRequest的用法網絡

主要就是進行了如下三步操做:併發

第一步:建立一個RequestQueue對象app

 

RequestQueue queue = Volley.newRequestQueue(Context);

 

 第二步:建立一個StringRequest對象

String url = "http://www.baidu.com";
//封裝一個字符串請求
StringRequest request = new StringRequest(Request.Method.GET, url, new Response
        .Listener<String>() {
    @Override
    public void onResponse(String response) {
        //請求成功
        System.out.println("請求結果:" + response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        //請求失敗
        error.printStackTrace();
    }
});

 第三步:將StringRequest對象添加到RequestQueue裏面

queue.add(request);

這樣就實現了最基本的HTTP發送與響應的功能

實現須要提交參數給服務器功能-->只須要在StringRequest的匿名類中重寫getParams()方法,在這裏設置POST參數就便可

實現須要提交請求頭參數給服務器功能-->只須要在StringRequest的匿名類中重寫getHeaders()方法,在這裏設置POST參數就便可

 

StringRequest request = new StringRequest(Request.Method.GET, url, listener, errorListener) {
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "xxx");
        map.put("age", 18 + "");
        return map;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        HashMap<String, String> map = new HashMap<>();
        try {
            String userAgent = Build.MODEL + "/" + Build.VERSION.RELEASE + " Android"
                    + "/" + Build.VERSION.SDK + " loaderman/" + getPackageManager()
                    .getPackageInfo(getPackageName(), 0).versionName;

            System.out.println("user agent:" + userAgent);

            map.put("User-Agent", userAgent);
            map.put("IMEI", "8348320940480389042380");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        return map;
    }
};

JsonRequest的用法

相似於StringRequest,JsonRequest也是繼承自Request類的,不過因爲JsonRequest是一個抽象類,所以咱們沒法直接建立它的實例,那麼只能從它的子類入手了。JsonRequest有兩個直接的子類,JsonObjectRequest和JsonArrayRequest,從名字上你應該能就看出它們的區別了吧?一個是用於請求一段JSON數據的,一個是用於請求一段JSON數組的。

 

RequestQueue queue = Volley.newRequestQueue(this);

String url = "http://10.0.3.2:8080/update.json";

//參3: JsonObject, 給服務器提交的參數對象
//name=xxx&age=18
//{name:"xxxx",age:18}
//[]
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new
        Response.Listener<JSONObject>() {

            @Override
            public void onResponse(JSONObject response) {

                //此方法在找不到字段時不會拋異常
                   String des = response.optString("des");
                System.out.println("json解析結果:" + des2);
            }

        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
    }
});
request.setTag(TAG);
queue.add(request);
RequestQueue queue = Volley.newRequestQueue(this);

String url = "http://10.0.3.2:8080/array.json";

JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, url, null, new
        Response.Listener<JSONArray>() {

            @Override
            public void onResponse(JSONArray response) {
                System.out.println("JsonArray:" + response.length());
            }

        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
    }
});

request.setTag(TAG);
queue.add(request);

 

 ImageRequest的用法

RequestQueue queue = Volley.newRequestQueue(this);
String url = "http://img4.duitang.com/uploads/item/201403/08/20140308185645_f2hy4.thumb" + ".600_0.jpeg";
//參3,4: 圖片最大寬高; 參5:縮放模式
// 參6:圖片格式  ARGB_8888: 4個字節; ARGB_4444: 2字節; RGB_565: 沒有透明度信息 2字節;
// ALPHA_8: 灰度圖片, 以後透明度值,1個字節
ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap response) {
        ivImage.setImageBitmap(response);
    }
}, 200, 200, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888, new Response
        .ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
    }
});
queue.add(request);

 

能夠看到,ImageRequest的構造函數接收六個參數,

第一個參數就是圖片的URL地址;

第二個參數是圖片請求成功的回調,這裏咱們能夠把返回的Bitmap參數設置到ImageView中。

第三第四個參數分別用於指定容許圖片最大的寬度和高度,若是指定的網絡圖片的寬度或高度大於這裏的最大值,則會對圖片進行壓縮,指定成0的話就表示無論圖片有多大,都不會進行壓縮。

第五個參數用於指定圖片的顏色屬性,Bitmap.Config下的幾個常量均可以在這裏使用,其中ARGB_8888能夠展現最好的顏色屬性,每一個圖片像素佔據4個字節的大小,而RGB_565則表示每一個圖片像素佔據2個字節大小。

第六個參數是圖片請求失敗的回調,這裏咱們能夠當請求失敗時在ImageView中設置顯示一張默認圖片。


 

 ImageLoader的用法

實際上,Volley在請求網絡圖片方面能夠作到的還遠遠不止這些,而ImageLoader就是一個很好的例子。ImageLoader也能夠用於加載網絡上的圖片,而且它的內部也是使用ImageRequest來實現的,不過ImageLoader明顯要比ImageRequest更加高效,由於它不只能夠幫咱們對圖片進行緩存,還能夠過濾掉重複的連接,避免重複發送請求。

因爲ImageLoader已經不是繼承自Request的了,因此它的用法也和以前的用法有所不一樣,總結起來大體能夠分爲如下幾步:

1. 建立一個RequestQueue對象。

RequestQueue queue = Volley.newRequestQueue(this);

 2. 建立一個ImageLoader對象。

ImageLoader imageLoader = new ImageLoader(queue, new ImageLoader.ImageCache() {
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
    }
    @Override
    public Bitmap getBitmap(String url) {
        return null;
    }
});

能夠看到,ImageLoader的構造函數接收兩個參數,

第一個參數就是RequestQueue對象,

第二個參數是一個ImageCache對象,這裏先new出一個空的ImageCache的實現便可。

3. 獲取一個ImageListener對象。

ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView,R.drawable.default_image, R.drawable.failed_image);

咱們經過調用ImageLoader的getImageListener()方法可以獲取到一個ImageListener對象,getImageListener()方法接收三個參數,第一個參數指定用於顯示圖片的ImageView控件,第二個參數指定加載圖片的過程當中顯示的圖片,第三個參數指定加載圖片失敗的狀況下顯示的圖片。

4. 調用ImageLoader的get()方法加載網絡上的圖片。

String url = "http://img5.duitang.com/uploads/item/201508/05/20150805165717_NRTfW.thumb" + ".700_0.jpeg";imageLoader.get(url,listener));
 

5. 自定義圖片緩存

雖然如今咱們已經掌握了ImageLoader的用法,可是剛纔的ImageLoader的優勢卻尚未使用到。爲何呢?由於這裏建立的ImageCache對象是一個空的實現,徹底沒能起到圖片緩存的做用。其實寫一個ImageCache也很是簡單,可是若是想要寫一個性能很是好的ImageCache,最好就要藉助Android提供的LruCache功能了

import android.graphics.Bitmap;
import android.util.LruCache;
import com.android.volley.toolbox.ImageLoader;

/**
 * 自定義內存緩存
 */

public class MyImageCache implements ImageLoader.ImageCache {
    private LruCache<String, Bitmap> mCache;
    public MyImageCache() {
        mCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory() / 8)) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }
    @Override
    public Bitmap getBitmap(String url) {
        return mCache.get(url);
    }
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        mCache.put(url, bitmap);
    }
}

 能夠看到,這裏咱們將緩存圖片的大小設置爲最大內存的八分之一,也能夠寫固定值好比10M。接着修改建立ImageLoader實例的代碼,第二個參數傳入BitmapCache的實例,以下所示:

ImageLoader imageLoader = new ImageLoader(queue, new MyImageCache());

NetWorkImageLoader的用法

除了以上兩種方式以外,Volley還提供了第三種方式來加載網絡圖片,即便用NetworkImageView。不一樣於以上兩種方式,NetworkImageView是一個自定義控制,它是繼承自ImageView的,具有ImageView控件的全部功能,而且在原生的基礎之上加入了加載網絡圖片的功能。NetworkImageView控件的用法要比前兩種方式更加簡單,大體能夠分爲如下五步:

1. 建立一個RequestQueue對象

RequestQueue queue = Volley.newRequestQueue(this);

 2. 建立一個ImageLoader對象

ImageLoader imageLoader = new ImageLoader(queue, new MyImageCache());

 3. 在佈局文件中添加一個NetworkImageView控件。

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/networkImageView"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_centerHorizontal="true"/>

 4. 在代碼中獲取該控件的實例

NetworkImageView netImage = (NetworkImageView) findViewById(R.id.networkImageView);

 5. 設置要加載的圖片地址

netImage.setDefaultImageResId(R.mipmap.ic_launcher);
netImage.setErrorImageResId(R.mipmap.ic_launcher);
netImage.setImageUrl(url, imageLoader);

第一個參數用於指定圖片的URL地址,第二個參數則是前面建立好的ImageLoader對象。

說明:

NetworkImageView並不須要提供任何設置最大寬高的方法也可以對加載的圖片進行壓縮。這是因爲NetworkImageView是一個控件,在加載圖片的時候它會自動獲取自身的寬高,而後對比網絡圖片的寬度,再決定是否須要對圖片進行壓縮。也就是說,壓縮過程是在內部徹底自動化的,並不須要咱們關心,NetworkImageView會始終呈現給咱們一張大小剛恰好的網絡圖片,不會多佔用任何一點內存,這也是NetworkImageView最簡單好用的一點吧。

固然了,若是你不想對圖片進行壓縮的話,其實也很簡單,只須要在佈局文件中把NetworkImageView的layout_width和layout_height都設置成wrap_content就能夠了,這樣NetworkImageView就會將該圖片的原始大小展現出來,不會進行任何壓縮。


自定義GsonRequest請求

JsonRequest的數據解析是利用Android自己自帶的JSONObject和JSONArray來實現的,配合使用JSONObject和JSONArray就能夠解析出任意格式的JSON數據。不過仍是以爲很麻煩,還有不少方法可讓JSON數據解析變得更加簡單,好比說GSON。但是,Volley中默認並不支持使用自家的GSON來解析數據,須要自定義一個GsonRequest。

第一步:加入Gson依賴庫

第二步:定義一個GsonRequest繼承自Request

 

import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;

import java.io.UnsupportedEncodingException;

/**
 * <p>
 * 自定義Gson的請求
 */

public class GsonRequest<T> extends Request<T> {

    private final Response.Listener<T> mListener;

    Class<T> clazz;

    public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener,
                       Response.ErrorListener errorListener) {
        super(method, url, errorListener);

        this.clazz = clazz;
        mListener = listener;
    }

    //解析網絡返回結果
    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            //參2:指的是字符編碼 UTF-8,GBK...., 默認是ISO-8859-1
            //此編碼是經過響應頭取出來的:HttpHeaderParser.parseCharset(response.headers)
            parsed = new String(response.data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            //編碼解析失敗
            parsed = new String(response.data);
        }

        //使用Gson解析json
        Gson gson = new Gson();
        T t = gson.fromJson(parsed, clazz);

        //返回解析的結果
        return Response.success(t, HttpHeaderParser.parseCacheHeaders(response));
    }

    //解析成功以後的回調, 經過此方法能夠將解析結果回傳給前端頁面
    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }
}

能夠看到,GsonRequest是繼承自Request類的,而且一樣提供了兩個構造函數。在parseNetworkResponse()方法中,先是將服務器響應的數據解析出來,而後經過調用Gson的fromJson方法將數據組裝成對象。在deliverResponse方法中仍然是將最終的數據進行回調。

第三步:經過服務的數據建立bean類

public class UpdateInfo {
//    {
//            description: "發現最新版本,趕忙下載哦!",     
//    }
    public String description;
 
}

 第四步:請求的使用:

RequestQueue queue = Volley.newRequestQueue(this);

String url = "http://10.0.3.2:8080/update.json";

GsonRequest request = new GsonRequest(Request.Method.GET, url, UpdateInfo.class, new
        Response.Listener<UpdateInfo>() {
            @Override
            public void onResponse(UpdateInfo response) {
                System.out.println("updateInfo:" + response.description);
            }
        }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
    }
});
queue.add(request);

 

自定義XmlRequest請求

import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.StringReader;

class XMLRequest extends Request<XmlPullParser> {

    private final Response.Listener<XmlPullParser> mListener;

    public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener,
                      Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
        try {
            String xmlString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlString));
            return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
        } catch (Exception e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(XmlPullParser response) {
        mListener.onResponse(response);
    }

}  

 

能夠看到,其實並無什麼太多的邏輯,基本都是仿照StringRequest寫下來的,XMLRequest也是繼承自Request類的,只不過這裏指定的泛型類是XmlPullParser,說明咱們準備使用Pull解析的方式來解析XML。在parseNetworkResponse()方法中,先是將服務器響應的數據解析成一個字符串,而後設置到XmlPullParser對象中,在deliverResponse()方法中則是將XmlPullParser對象進行回調。

使用:

與StringRequest使用相似,不一樣之處在於返回的是XmlPullParser ,須要對onResponse()方法中解析響應的XML數據便可

爲了內存優化和發揮volley優點.通常須要寫一個本身的工具類,實現以下:

 

import android.content.Context;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;


public class VolleyTools {

    private static VolleyTools mInstance;
    private final RequestQueue queue;
    private final ImageLoader loader;

    private VolleyTools(Context ctx) {
        queue = Volley.newRequestQueue(ctx);
        loader = new ImageLoader(queue, new
                MyImageCache());
    }

    public static VolleyTools getInstance(Context ctx) {
        if (mInstance == null) {
            synchronized (VolleyTools.class) {
                if (mInstance == null) {
                    mInstance = new VolleyTools(ctx);
                }
            }
        }

        return mInstance;
    }

    public RequestQueue getQueue() {
        return queue;
    }

    public ImageLoader getImageLoader() {
        return loader;
    }
   
}

 根據工具類,能夠對上面的方法中的使用代碼進行對應的修改便可


取消請求的實現:

步驟:

第一步:給請求設置Tag或者經過過濾器取消

request.setTag(TAG);

 第二步:通常在onDestroy方法中取消

VolleyTools.getInstance(this).getQueue().cancelAll(TAG);
VolleyTools.getInstance(this).getQueue().cancelAll(new RequestQueue.RequestFilter() {
    @Override
    public boolean apply(Request<?> request) {
        //request.getUrl().equals("")

        return false;
    }
});
相關文章
相關標籤/搜索