1. 程式人生 > >訪問網路的框架(Volley和OkHttp3)使用

訪問網路的框架(Volley和OkHttp3)使用

Volley

Volley是在Google/IO大會上提出的,使得android應用網路操作更方便快捷

Volley既可以非常簡單的訪問網路,也可以輕鬆的載入圖片,在不同的執行緒上非同步執行所有請求而避免了阻塞主執行緒

設計目標: 適合資料量不大但通訊操作頻繁的網路操作,不適合大資料量的網路操作,比如檔案下載

使用

  • Velloy中的RequestQueue和Request

    • RequestQueue用來執行請求的請求佇列
    • Request用來構造一個請求物件
    • Request物件主要有以下幾種型別:

      • StringReqeust 響應的主體為字串
      • JsonArrayReqeust 傳送和接收JSON物件
      • JsonObjectRequest傳送和接收Json物件
      • ImageRequest傳送和接收Image
  • 基本使用

    • 建立一個RequestQueue例項

      //這裡的this指的是Context
      RequestQueue requestQueue = Volley.newRequestQueue(this);
      
    • 建立一個Request(義JsonObjectRequest為例,其它幾個類似)

      private final String url="http:/xxxxx"//所需url
          JsonObjectRequest req=new JsonObjectRequest(url,null,new Response.Listener<JsonObject>(){
              @Override
              public void onResponse(JsonObject response){
                  //新增自己的響應邏輯,
              }
          },
          new ResponseError.Listener(){
              @Override
              public void onResponseError(VollerError error){
                  //錯誤處理
                  L.d("Error Message:","Error is"+error);
              }
         });  
      
      • 將request例項新增到requestQueue中

        requestQueue.add(jsonObjectRequest);

  • ImageLoader

    ImageLoader比ImageRequest更加高效,它不僅可以圖片進行快取,還可以過濾掉重複的連結,避免重複傳送請求。

    由於ImageLoader不是繼承Request的所以它的使用傳送和以前不同,步驟如下:

    • 建立一個RequestQueue物件

      //這裡的this指的是Context
      RequestQueue requestQueue = Volley.newRequestQueue(this);
      
    • 建立一個ImageLoader物件

      ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {  
          @Override  
          public void putBitmap(String url, Bitmap bitmap) {  
          }  
      
          @Override  
          public Bitmap getBitmap(String url) {  
              return null;  
          }  
      });  
      
    • 獲取一個ImageListerner物件

      ImageListener listener = ImageLoader.getImageListener(imageView,  
              R.drawable.default_image, R.drawable.failed_image);  
      
    • 呼叫ImageLoader的get()方法載入網路上的圖片

      imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener); 
      //過載 
      mageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",  
                      listener, 200, 200);  
      
    • 為了很好的快取圖片,藉助Android提供的LruCache功能,建立一個ImageCache

      • 新建一個BitmapCache並實現ImageCache介面

        public class BitmapCache implements ImageCache {  
        
            private LruCache<String, Bitmap> mCache;  
        
            public BitmapCache() {  
                int maxSize = 10 * 1024 * 1024;  
                mCache = new LruCache<String, Bitmap>(maxSize) {  
                    @Override  
                    protected int sizeOf(String key, Bitmap bitmap) {  
                        return bitmap.getRowBytes() * bitmap.getHeight();  
                    }  
                };  
            }  
            @Override  
            public Bitmap getBitmap(String url) {  
                return mCache.get(url);  
            }  
            @Override  
            public void putBitmap(String url, Bitmap bitmap) {  
                mCache.put(url, bitmap);  
            }  
        }  
        
  • NetworkImageView的用法

    除了ImageReqeust和ImageLoader外,Volley還提供了NetworkImageView。NetworkImageView是一個自定義控制,它繼承自ImageView,具備ImageView控制元件的所有功能,並且在原生的基礎之上加入了載入網路圖片的功能。NetworkImageView控制元件的用法簡單,步驟如下:

    • 建立一個RequestQueue物件
    • 建立一個ImageLoader物件
    • 在佈局檔案中新增一個NetworkImageView控制元件
    • 在程式碼中獲取該控制元件的例項
    • 設定要載入的圖片地址

    其中,第一第二步和ImageLoader的用法是完全一樣的,因此這裡我們就從第三步開始學習了。首先修改佈局檔案中的程式碼,在裡面加入NetworkImageView控制元件,如下所示:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        android:orientation="vertical" >  
    
        <Button  
            android:id="@+id/button"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="Send Request" />  
    
        <com.android.volley.toolbox.NetworkImageView   
            android:id="@+id/network_image_view"  
            android:layout_width="200dp"  
            android:layout_height="200dp"  
            android:layout_gravity="center_horizontal" />  
    
    </LinearLayout> 
    

    接著在Activity獲取到這個控制元件的例項,這就非常簡單了,程式碼如下所示:

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

    得到了NetworkImageView控制元件的例項之後,我們可以呼叫它的setDefaultImageResId()方法、setErrorImageResId()方法和setImageUrl()方法來分別設定載入中顯示的圖片,載入失敗時顯示的圖片,以及目標圖片的URL地址,如下所示:

    networkImageView.setDefaultImageResId(R.drawable.default_image);  
    networkImageView.setErrorImageResId(R.drawable.failed_image);  
    networkImageView.setImageUrl("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",  
                    imageLoader);  
    
  • 關閉

    如果在一個Activity裡面啟動了網路請求,而在這個網路請求還沒返回結果的時候,如果Activity被結束了,則我們需要寫如下程式碼作為防守:

    @Override public void onPostExecute(Result r) {  
        if (getActivity() == null) {  
            return;  
        }  
        // ...  
    }  
    

    Activity被終止之後,如果繼續使用其中的Context等,除了無辜的浪費CPU,電池,網路等資源,有可能還會導致程式crash,所以,我們需要處理這種一場情況。

    使用Volley的話,我們可以在Activity停止的時候,同時取消所有或部分未完成的網路請求。

    Volley裡所有的請求結果會返回給主程序,如果在主程序裡取消了某些請求,則這些請求將不會被返回給主執行緒。
    比如,可以針對某些個request做取消操作:

    @Override  
    public void onStop() {  
        for (Request <?> req : mInFlightRequests) {  
            req.cancel();  
        }  
        ...  
    }  
    

    或者,取消這個佇列裡的所有請求:

    @Override pubic void onStop() {  
        mRequestQueue.cancelAll(this);  
        ...  
    }  
    

    也可以根據RequestFilter或者Tag來終止某些請求:

    @Override 
    public void onStop() {  
        mRequestQueue.cancelAll( new RequestFilter() {})  
        ...  
        // or  
        mRequestQueue.cancelAll(new Object());  
        ... 
    } 
    

OkHttp3

  • Android Studio 配置gradle環境:

    compile 'com.squareup.okhttp3:okhttp:3.5.0'
    compile 'com.squareup.okio:okio:1.11.0'
    
  • 新增網路許可權

    <uses-permission android:name="android.permission.INTERNET"/>
    

使用教程

  • Http Get

    • 非同步的Get

      在Http請求中最常見的就是get方法了,在大多數的使用場景中,我們使用的都是非同步的Get請求

      // step 1: 建立 OkHttpClient 物件    
      OkHttpClient okHttpClient = new OkHttpClient();
      
      // step 2: 建立一個請求,不指定請求方法時預設是GET。
      Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com");
      //可以省略,預設是GET請求
      requestBuilder.method("GET",null);
      
      // step 3:建立 Call 物件
      Call call = okHttpClient.newCall(requestBuilder.build());
      
      //step 4: 開始非同步請求
      call.enqueue(new Callback() {
          @Override
          public void onFailure(Call call, IOException e) {
              // TODO: 17-1-4  請求失敗
          }
          @Override
          public void onResponse(Call call, Response response) throws IOException {
              // TODO: 17-1-4 請求成功
              //獲得返回體
              ResponseBody body = response.body();
          }
      });
      
    • 檔案下載

      //step 1: 不變的第一步建立 OkHttpClick
      OkHttpClient okHttpClient = new OkHttpClient();
      
      //step 2: 建立Requset
      Request request = new Request.Builder()
              .url("http://www.ssyer.com/uploads/org_2017010593503_775.jpg")
              .build();
      
      //step 3:建立聯絡,建立Call
      mOkHttpClient.newCall(request).enqueue(new Callback() {
          @Override
          public void onFailure(Call call, IOException e) {
          }
      
          @Override
          public void onResponse(Call call, Response response) {
              InputStream inputStream = response.body().byteStream();
              FileOutputStream fileOutputStream = null;
              try {
                  File file = new File(Environment.getExternalStorageDirectory() + "大獅子.jpg");
                  fileOutputStream = new FileOutputStream(file);
                  byte[] buffer = new byte[2048];
                  int len = 0;
                  while ((len = inputStream.read(buffer)) != -1) {
                      fileOutputStream.write(buffer, 0, len);
                  }
                  fileOutputStream.flush();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              Log.d("downloadAsynFile", "檔案下載成功");
          }
      });
      
    • 同步的Get

      當然 Get 也支援阻塞方式的同步請求,不過在開發中這種方法很少被使用。上面我們也說了Call有一個 execute() 方法,你也可以直接呼叫 call.execute() 返回一個 Response 。然後利用 isSuccessful() 判讀是否成功,進行相應的結果解析。

  • 非同步的Http Post

    • Post 上傳鍵值對

      //step 1: 同樣的需要建立一個OkHttpClick物件
      OkHttpClient okHttpClient = new OkHttpClient();
      //step 2: 建立  FormBody.Builder
      FormBody formBody = new FormBody.Builder()
              .add("name", "dsd")
              .build();
      //step 3: 建立請求
      Request request = new Request.Builder().url("http://www.baidu.com")
              .post(formBody)
              .build();
      //step 4: 建立聯絡 建立Call物件
      okHttpClient.newCall(request).enqueue(new Callback() {
          @Override
          public void onFailure(Call call, IOException e) {
              // TODO: 17-1-4  請求失敗
          }
          @Override
          public void onResponse(Call call, Response response) throws IOException {
              // TODO: 17-1-4 請求成功
          }
      });
      

      在使用 Post 的時候,引數是包含在請求體中的。所以我們通過 FormBody ,新增多個String鍵值對,然後為 Request 新增 post(formBody) 完成我們 Request 的構造。之後的步驟就和Get的步驟一樣了,是不是很簡單啊!

  • Post非同步上傳檔案

    // step 1: 建立 OkHttpClient 物件
    OkHttpClient okHttpClient = new OkHttpClient();
    //step 2:建立 RequestBody 以及所需的引數
    //2.1 獲取檔案
    File file = new File(Environment.getExternalStorageDirectory() + "test.txt");
    //2.2 建立 MediaType 設定上傳檔案型別
    MediaType MEDIATYPE = MediaType.parse("text/plain; charset=utf-8");
    //2.3 獲取請求體
    RequestBody requestBody = RequestBody.create(MEDIATYPE, file);
    //step 3:建立請求
    Request request = new Request.Builder().url("http://www.baidu.com")
            .post(requestBody)
            .build();
    //step 4 建立聯絡
    okHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            // TODO: 17-1-4  請求失敗
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            // TODO: 17-1-4 請求成功
        }
    });
    
    //新增許可權
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    ---------------------------------------------------------------------------
    引數  說明
    text/html   HTML格式
    text/plain  純文字格式
    text/xml    XML格式
    image/gif   gif圖片格式
    image/jpeg  jpg圖片格式
    image/png   png圖片格式
    application/xhtml+xml   XHTML格式
    application/xml XML資料格式
    application/atom+xml    Atom XML聚合格式
    application/json    JSON資料格式
    application/pdf pdf格式
    application/msword  Word文件格式
    application/octet-stream    二進位制流資料