Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 理解Volley -- Android 學習之路

理解Volley -- Android 學習之路

編輯:關於Android編程

介紹

Android中的網絡請求一般就是兩種 HttpURLConnection 和HttpClient,不論是哪一種在使用的時候都是經過一系列的封裝 很繁瑣有沒有,而Google在2013年推出的Volley網絡請求框架 ,使網絡請求更加的快捷,方便,只需創建隊列,創建請求,將請求放入隊列就可以了,volley所有的網絡請求都是異步的,不需要再操作線程的問題,而且自帶緩存,再也不擔心OOM了 我們只需要關心邏輯代碼就可以了,需要注意的是 Volley適用於頻繁發送請求 但是數據量不大(小於3M)的情況 。

優點

普通數據,json,圖片的異步加載 網絡請求優先級處理 硬盤緩存(普通數據,圖片,json) 與activity的生命周期聯動 activity死了他就取消了

缺點

不適合數據量較大的網絡操作

工作原理

先來看一張圖
工作原理
在執行RequestQueue的add() 時 volley會開啟一個緩存處理線程(cacheDispatcher)和一個網絡調度線程池。
當request被添加到queue(隊列)時,緩存處理線程會先在緩存中查看是否有緩存,如果有緩存就將緩存結果發送到主線程,沒有緩存的話,就將request放到queue中執行請求
得到請求結果後將數據發送到主線程解析並將結果寫入緩存,當然我們可以設置是否可以緩存
綠色的是 主線程,黃色的是緩存處理線程 ,橙色的是 網絡請求線程

請求類型

StringRequest 返回字符串 JsonRequest 返回json對象 ImageRequest 返回Bitmap

使用步驟

創建隊列RquesetQueue 創建請求 將請求放入隊列

取消請求

取消一個請求只需調用這個請求對象的cancel方法即可。請求取消後 ,響應將不會被調用(只是響應監聽哦)。
如果我們有好多請求需要取消,那就得跟蹤很多請求 ,豈不是很費精力,費內存嗎,還好有另一種辦法
volley中的每個請求都可以設置一個 tag (標簽) 我們可以通過這個tag來取消一個或多個對應的請求 ,
調用queue的cancelAll(Tag);方法 就可以取消對應的請求了 ,(多個請求是可以設置相同的標簽的)

示例代碼

mLoginRequest.cancel();//取消單個請求
//為Request設置tag
 mLoginRequest.setTag("login");
 mNewsRequest.setTag("news");
 mMsgListRequest.setTag("msg");
 mMsgRequest.setTag("msg");
//添加到隊列中
 mQueue.add(mLoginRequest);
 mQueue.add(mNewsRequest);
 mQueue.add(mMsgListRequest);
 mQueue.add(mMsgRequest);
    //取消隊列中包含msg標簽的請求
mQueue.cancelAll("msg");

使用注意:如果你要根據請求的響應去執行別的操作的話 就得慎重取消了,因為響應監聽不會被調用

volley 加載圖片的三種方式

NetworkImageView ImageRequst ImageLoader
- ImageLoader 用到了緩存 ,對於LRU緩存我也不是理解的很透徹就不再贅述了
- 使用loader 需要一個 ImageCache的緩存 ,上述的工作原理在這裡就比較明顯了,
- 每當loader去get一個地址 都會先去ImageCahce訪問 是否有緩存 ,如果緩存為空才會去網絡加載
在 ImageCache的getBitmap()方法返回為null的話就執行網絡加載如果不為null就不執行網絡請求了

代碼實例

RequestQueue請求隊列的創建
一個應用最好只有一個RequestQueue 實例,所有的請求都是在RequestQueue中進行的,為了更有效的執行請求,一個app中最好只有一個實例,這個我也不是理解的很清楚,看的麻煩指教一下。
RequestQueue 需要依賴於上文來創建的,這裡使用簡單的單例模式

/**
 * Created by sky-mxc
 */
public class Http {
    private Context context;
    private RequestQueue queue;
    private static Http http;

    private Http(Context context) {
        this.context = context;
        this.queue = Volley.newRequestQueue(context);
    }

    public static Http getInstance(Context context) {
        if (http == null) {
            http = new Http(context);
        }
        return http;
    }

}

StringRequest

StringRequest 的默認請求方式 是GET 如果需要 POST方式,指定即可 ,volley 重載了StringRequest的創建

get

 /**
*String請求get方式(default)
*@paramurl地址
*@paramsuccessListener成功監聽
*@paramerrorListener失敗監聽
*/
public void execStringRequest(Stringurl,Response.ListenersuccessListener,Response.ErrorListenererrorListener){
 StringRequestgetRequest = new StringRequest(url,successListener,errorListener);
queue.add(getRequest);
}

調用

    /**
     * Get方式加載數據
     */
    private void loadData(){
        String url ="http://toolsmi.com/starclass/lessons";
        Http.getInstance(this).execStringRequest(url, new Response.Listener() {
            @Override
            public void onResponse(String s) {
            if (!TextUtils.isEmpty(s)){
                Result> result = JSON.parseObject(s,new TypeReference>>(){});
                Toast.makeText(MainActivity.this,result.describe,Toast.LENGTH_SHORT).show();
                lessions.addAll(result.data);
                adapter.notifyDataSetChanged();
            }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(MainActivity.this,"網絡訪問出錯,請稍後重試",Toast.LENGTH_SHORT).show();
            }
        });
    }

post 關於參數的傳遞 可以重寫 getParams()方法 將參數封裝為Map集合 返回即可

/**
  *  String 請求 post方式
  * @param url 地址
  * @param params 參數 Map結構
  * @param successListener 成功監聽
  * @param errorListener 失敗監聽
  */
 public void execStringRequestPost(String url,final Map params, Response.Listener successListener, Response.ErrorListener errorListener){
     StringRequest getRequest = new StringRequest(Request.Method.POST,url,successListener ,errorListener){
         @Override
         protected Map getParams() throws AuthFailureError {

             return params;
         }
     };
     queue.add(getRequest);

 }

調用

/**
 * 檢查版本更新使用Post方式
 */
private void checkVersion(){
    String url ="http://toolsmi.com/starclass/ver";
    Map params = new HashMap<>();
    params.put("ver",BuildConfig.VERSION_CODE+"");
    Http.getInstance(this).execStringRequestPost(url,params, new Response.Listener() {
        @Override
        public void onResponse(String str) {
            Log.e("Tag","======onResponse=============="+str);
            //將字符串解析為對象
            if (!TextUtils.isEmpty(str)) {
                Result result = JSON.parseObject(str, new TypeReference>() {
                });
                if (result.state ==1){
                    new AlertDialog.Builder(MainActivity.this).setMessage("目前版本"+BuildConfig.VERSION_NAME+",檢查到新版本"+result.data.getVersionName()+",是否更新?")
                            .setPositiveButton("立即更新",null)
                            .setNegativeButton("下次再議",null)
                            .show();
                }else{
                    Toast.makeText(MainActivity.this,"目前已經是最新版本",Toast.LENGTH_SHORT).show();
                }
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(MainActivity.this,"網絡出錯,請稍後重試",Toast.LENGTH_SHORT).show();
        }
    });
}

JsonRequest

個人感覺Android原生的json操作有點繁瑣,所以一般不使用,所以遇到需要解析json的請求都是用StringRequest 返回數據後使用 fastjson或Gson解析,看上面就知道了

ImageRequest

聲明

/**
  * 執行圖片的加載
  * @param url 地址
  * @param successListener 成功監聽
  * @param maxWidth 最大寬度 px
  * @param maxHeight 最大高度 px
  * @param config 清晰度
  * @param errorListener 錯誤監聽
  */
 public void execImageRequest(String url, Response.Listener successListener, int maxWidth, int maxHeight, Bitmap.Config config, Response.ErrorListener errorListener){
     ImageRequest request = new ImageRequest(url,successListener,maxWidth,maxHeight, config,errorListener);
     queue.add(request);
}

調用

/**
 * ImageRequest加載圖片
 */
private void loadImageRequest() {
    String url =etUrl.getText().toString();
Http.getInstance(this).execImageRequest(url,
                new Response.Listener() {
            @Override
            public void onResponse(Bitmap bitmap) {
                if (bitmap!=null){
                    image0.setImageBitmap(bitmap);
                }
            }
        },
        (int) getResources().getDimension(R.dimen.image_w),
        (int)getResources().getDimension(R.dimen.image_h),
        Bitmap.Config.ARGB_8888,
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Toast.makeText(MainActivity.this,"網絡訪問出錯,請稍後重試",Toast.LENGTH_SHORT).show();
            }
        });
}

ImageLoader

通過這個看一下Volley的工作模式
在有緩存的情況下 就不會去執行網絡請求 在ImageCache的gitBitmap()返回一個Bitmap 當然也就不會執行放入緩存的操作(putBitmap())

ImageLoader 在Http類中的定義

/**
 * 獲取ImageLoad 實例
 * @param context 上下文
 * @param cache ImageCache 緩存
 * @return ImageLoader
 */
public static ImageLoader getLoader(Context context, ImageLoader.ImageCache cache){
    if (loader==null){
        loader = new ImageLoader(getInstance(context).queue,cache);
    }
    return loader;
}

調用

/**
 * 使用Loader加載圖片
 */
private void loadImageLoader() {
    String url = etUrl.getText().toString();
    ImageLoader.ImageCache cache = new ImageLoader.ImageCache() {
        @Override
        public Bitmap getBitmap(String s) {
            Log.e("Tag","====getBitmap()從緩存加載====="+s);
            //((BitmapDrawable) (image0.getDrawable())).getBitmap()
            return null;
        }

        @Override
        public void putBitmap(String s, Bitmap bitmap) {
            Log.e("Tag","====putBitmap()放入緩存====="+s);
        }
    };
    ImageLoader loader = Http.getLoader(this,cache);
    loader.get(url, new ImageLoader.ImageListener(){

        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(MainActivity.this,"網絡訪問出錯,請稍後重試",Toast.LENGTH_SHORT).show();
            Log.e("Tag",volleyError+"");
            image1.setImageResource(R.mipmap.jiantou);
        }

  /**
     * 這個方法會被調用兩次 緩存處理一次 網絡加載一次
     * @param imageContainer 網絡請求信息
     * @param b 區分是緩存(true) 還是網絡加載
     */
    @Override
    public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) {
        Log.e("Tag","===onResponse()======緩存加載:"+b+"====bitmap:"+imageContainer.getBitmap()+"=====url:"+imageContainer.getRequestUrl());

        Bitmap bmp = imageContainer.getBitmap();
        if (bmp!=null){//這裡需要注意 如果沒有使用緩存 從緩存中讀取的Bitmap就是空的,
            image1.setImageBitmap(bmp);
        }

    }
},(int)getResources().getDimension(R.dimen.image_w),(int)getResources().getDimension(R.dimen.image_h));


}

在有緩存的情況下 就不會去執行網絡請求 ;在ImageCache的gitBitmap()返回一個Bitmap 當然也就不會執行放入緩存的操作
在 有緩存的情況下的log日志:

E/Tag: ====getBitmap()從緩存加載=====#W100#H90http://www.codexiu.cn/static/blog/ad/4.jpg
 E/Tag: ===onResponse()======緩存加載:true====bitmap:android.graphics.Bitmap@631b631=====url:http://www.codexiu.cn/static/blog/ad/4.jpg

在無緩存的情況再去執行網絡請求, gitBitmap() 返回null

無緩存下的log 日志:

  E/Tag: ====getBitmap()從緩存加載=====#W100#H90http://www.codexiu.cn/static/blog/ad/4.jpg
E/Tag: ===onResponse()======緩存加載:true====bitmap:null=====url:http://www.codexiu.cn/static/blog/ad/4.jpg
E/Tag: ====putBitmap()放入緩存=====#W100#H90http://www.codexiu.cn/static/blog/ad/4.jpg
E/Tag: ===onResponse()======緩存加載:false====bitmap:android.graphics.Bitmap@225e93b5=====url:http://www.codexiu.cn/static/blog/ad/4.jpg

即使沒有緩存 Response 方法還是會被調用兩次 緩存處理一次,,網絡處理一次

NetworkImageView

這個控件繼承自 ImageView 只不過增加了幾個好用的方法 讓我們用著更方便
布局定義

  

加載圖片 ,加載時用到了ImageLoader 和ImageCache

 /**
   * 使用 NetWorkImageView
   */
  private void loadNetWorkImageView() {
      String url = etUrl.getText().toString();
      image2.setDefaultImageResId(R.mipmap.ic_launcher);//  網絡加載前的占位圖
      image2.setErrorImageResId(R.mipmap.jiantou);        //加載錯誤時提示圖
      image2.setImageUrl(url,Http.getLoader(this, new ImageLoader.ImageCache() {
          @Override
          public Bitmap getBitmap(String s) {
              return null;
          }

          @Override
          public void putBitmap(String s, Bitmap bitmap) {

          }
      }));
 }

關於Volley的使用 我寫了個Demo github地址:https://github.com/sky-mxc/AndroidDemo/tree/master/practicenetwork_volley

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved