【開源項目13】Volley框架 以及 設置request超時時間


Volley提供了優美的框架,使android程序網絡訪問更容易、更快。

Volley抽象實現了底層的HTTP Client庫,我們不需關注HTTP Client細節,專注於寫出更加漂亮、干凈的RESTful HTTP請求。

Volley請求會異步執行,不阻擋主線程。

Volley提供的功能

  1. 封裝了異步的RESTful請求API
  2. 一個優雅and穩健的請求隊列
  3. 一個可擴展的架構,使開發者能實現自定義的請求和響應處理機制
  4. 能使用外部Http Client庫
  5. 緩存策略
  6. 自定義的網絡圖像加載視圖(NetworkImageView,ImageLoader等)

為什么使用異步Http請求

Android中要求HTTP請求異步執行,如果在主線程執行HTTP請求,可能會拋出 android.os.NetworkOnMainThreadException  異常。阻塞主線程有一些嚴重的后果,它阻礙UI渲染,用戶體驗不流 暢,它可能會導致可怕的ANR(Application Not Responding)。要避免這些陷阱,作為一個開發者,應該始終確保HTTP請求是在一個不同的線程

怎樣使用Volley

 

1、安裝和使用Volley庫

 

2、使用請求隊列

 

3、異步的JSON、String請求

 

4、取消請求

 

5、重試失敗的請求,自定義請求超時

 

6、設置請求頭(HTTP headers)

 

7、使用Cookies

 

8、錯誤處理

1.安裝和使用Volley庫

引入Volley非常簡單,首先,從git庫先克隆一個下來:

git clone https://android.googlesource.com/platform/frameworks/volley

然后編譯為jar包,再把jar包放到自己的工程的libs目錄。

 

2.使用請求隊列

 Volley的所有請求都放在一個隊列,然后進行處理,這里是你如何將創建一個請求隊列:

 RequestQueue mRequestQueue = Volley.newRequestQueue(this); // 'this' is Context

 

 理想的情況是把請求隊列集中放到一個地方,最好是初始化應用程序類中初始化請求隊列,下面類做到了這一點:

 

public class ApplicationController extends Application {

    /**
     * Log or request TAG
     */
    public static final String TAG = "VolleyPatterns";

    /**
     * Global request queue for Volley
     */
    private RequestQueue mRequestQueue;

    /**
     * A singleton instance of the application class for easy access in other places
     */
    private static ApplicationController sInstance;

    @Override
    public void onCreate() {
        super.onCreate();

        // initialize the singleton
        sInstance = this;
    }

    /**
     * @return ApplicationController singleton instance
     */
    public static synchronized ApplicationController getInstance() {
        return sInstance;
    }

    /**
     * @return The Volley Request queue, the queue will be created if it is null
     */
    public RequestQueue getRequestQueue() {
        // lazy initialize the request queue, the queue instance will be
        // created when it is accessed for the first time
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    /**
     * Adds the specified request to the global queue, if tag is specified
     * then it is used else Default TAG is used.
     * 
     * @param req
     * @param tag
     */
    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);

        VolleyLog.d("Adding request to queue: %s", req.getUrl());

        getRequestQueue().add(req);
    }

    /**
     * Adds the specified request to the global queue using the Default TAG.
     * 
     * @param req
     * @param tag
     */
    public <T> void addToRequestQueue(Request<T> req) {
        // set the default tag if tag is empty
        req.setTag(TAG);

        getRequestQueue().add(req);
    }

    /**
     * Cancels all pending requests by the specified TAG, it is important
     * to specify a TAG so that the pending/ongoing requests can be cancelled.
     * 
     * @param tag
     */
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

 

3.異步的JSON、String請求

Volley提供了以下的實用工具類進行異步HTTP請求:

JsonObjectRequest — To send and receive JSON Object from the Server

JsonArrayRequest — To receive JSON Array from the Server

StringRequest — To retrieve response body as String (ideally if you intend to parse the response by yourself)

JsonObjectRequest

這個類可以用來發送和接收JSON對象。這個類的一個重載構造函數允許設置適當的請求方法(DELETE,GET,POST和PUT)。如果您正在使用一個RESTful服務端,可以使用這個類。下面的示例顯示如何使GET和POST請求

 GET請求:

final String URL = "/volley/resource/12";
// pass second argument as "null" for GET requests
JsonObjectRequest req = new JsonObjectRequest(URL, null,
       new Response.Listener<JSONObject>() {
           @Override
           public void onResponse(JSONObject response) {
               try {
                   VolleyLog.v("Response:%n %s", response.toString(4));
               } catch (JSONException e) {
                   e.printStackTrace();
               }
           }
       }, new Response.ErrorListener() {
           @Override
           public void onErrorResponse(VolleyError error) {
               VolleyLog.e("Error: ", error.getMessage());
           }
       });

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

 

 POST請求:

 

final String URL = "/volley/resource/12";
// Post params to be sent to the server
HashMap<String, String> params = new HashMap<String, String>();
params.put("token", "AbCdEfGh123456");

JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
       new Response.Listener<JSONObject>() {
           @Override
           public void onResponse(JSONObject response) {
               try {
                   VolleyLog.v("Response:%n %s", response.toString(4));
               } catch (JSONException e) {
                   e.printStackTrace();
               }
           }
       }, new Response.ErrorListener() {
           @Override
           public void onErrorResponse(VolleyError error) {
               VolleyLog.e("Error: ", error.getMessage());
           }
       });

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

 

 

 

 

JsonArrayRequest

這個類可以用來接受 JSON Arrary,不支持JSON Object。這個類現在只支持 HTTP GET。由於支持GET,你可以在URL的后面加上請求參數。類的構造函數不支持請求參數

final String URL = "/volley/resource/all?count=20";
JsonArrayRequest req = new JsonArrayRequest(URL, new Response.Listener<JSONArray> () {
    @Override
    public void onResponse(JSONArray response) {
        try {
            VolleyLog.v("Response:%n %s", response.toString(4));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.e("Error: ", error.getMessage());
    }
});

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

 

 

StringRequest

這個類可以用來從服務器獲取String,如果想自己解析請求響應可以使用這個類,例如返回xml數據。它還可以使用重載的構造函數定制請求

final String URL = "/volley/resource/recent.xml";
StringRequest req = new StringRequest(URL, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        VolleyLog.v("Response:%n %s", response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.e("Error: ", error.getMessage());
    }
});

// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);

 

4.取消請求

Volley提供了強大的API取消未處理或正在處理的請求。取消請求最簡單的方法是調用請求隊列cancelAll(tag)的方法,前提是你在添加請求時設置了標記。這樣就能使標簽標記的請求掛起。

給請求設置標簽:

request.setTag("My Tag");

使用ApplicationController添加使用了標簽的請求到隊列中:

ApplicationController.getInstance().addToRequestQueue(request, "My Tag");

取消所有指定標記的請求:

mRequestQueue.cancelAll("My Tag");

5.重試失敗的請求,自定義請求超時

Volley中沒有指定的方法來設置請求超時時間,可以設置RetryPolicy 來變通實現。DefaultRetryPolicy類有個initialTimeout參數,可以設置超時時間。要確保最大重試次數為1,以保證超時后不重新請求。

Setting Request Timeout

request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));

設置請求頭(HTTP headers)        如果你想失敗后重新請求(因超時),您可以指定使用上面的代碼,增加重試次數。注意最后一個參數,它允許你指定一個退避乘數可以用來實現“指數退避”來從RESTful服務器請求數據。

有時候需要給HTTP請求添加額外的頭信息,一個常用的例子是添加 “Authorization”到HTTP 請求的頭信息。Volley請求類提供了一個 getHeaers()的方法,重載這個方法可以自定義HTTP 的頭信息。

添加頭信息:

JsonObjectRequest req = new JsonObjectRequest(URL, new JSONObject(params),
           new Response.Listener<JSONObject>() {
               @Override
               public void onResponse(JSONObject response) {
                   // handle response
               }
           }, new Response.ErrorListener() {
               @Override
               public void onErrorResponse(VolleyError error) {
                   // handle error                        
               }
           }) {

       @Override
       public Map<String, String> getHeaders() throws AuthFailureError {
           HashMap<String, String> headers = new HashMap<String, String>();
           headers.put("CUSTOM_HEADER", "Yahoo");
           headers.put("ANOTHER_CUSTOM_HEADER", "Google");
           return headers;
       }
   };

 

 

6.使用Cookies

Volley中沒有直接的API來設置cookies,Volley的設計理念就是提供干凈、簡潔的API來實現RESTful HTTP請求,不提供設置cookies是合理的。

下面是修改后的ApplicationController類,這個類修改了getRequestQueue()方法,包含了 設置cookie方法,這些修改還是有些粗糙

// http client instance
private DefaultHttpClient mHttpClient;
public RequestQueue getRequestQueue() {
    // lazy initialize the request queue, the queue instance will be
    // created when it is accessed for the first time
    if (mRequestQueue == null) {
        // Create an instance of the Http client. 
        // We need this in order to access the cookie store
        mHttpClient = new DefaultHttpClient();
        // create the request queue
        mRequestQueue = Volley.newRequestQueue(this, new HttpClientStack(mHttpClient));
    }
    return mRequestQueue;
}

/**
 * Method to set a cookie
 */
public void setCookie() {
    CookieStore cs = mHttpClient.getCookieStore();
    // create a cookie
    cs.addCookie(new BasicClientCookie2("cookie", "spooky"));
}


// add the cookie before adding the request to the queue
setCookie();

// add the request to the queue
mRequestQueue.add(request);

 

7.錯誤處理

正如前面代碼看到的,在創建一個請求時,需要添加一個錯誤監聽onErrorResponse。如果請求發生異常,會返回一個VolleyError實例。

以下是Volley的異常列表:

AuthFailureError:如果在做一個HTTP的身份驗證,可能會發生這個錯誤。

NetworkError:Socket關閉,服務器宕機,DNS錯誤都會產生這個錯誤。

NoConnectionError:和NetworkError類似,這個是客戶端沒有網絡連接。

ParseError:在使用JsonObjectRequest或JsonArrayRequest時,如果接收到的JSON是畸形,會產生異常。

SERVERERROR:服務器的響應的一個錯誤,最有可能的4xx或5xx HTTP狀態代碼。

TimeoutError:Socket超時,服務器太忙或網絡延遲會產生這個異常。默認情況下,Volley的超時時間為2.5秒。如果得到這個錯誤可以使用RetryPolicy。

可以使用一個簡單的Help類根據這些異常提示相應的信息:

public class VolleyErrorHelper {
     /**
     * Returns appropriate message which is to be displayed to the user 
     * against the specified error object.
     * 
     * @param error
     * @param context
     * @return
     */
  public static String getMessage(Object error, Context context) {
      if (error instanceof TimeoutError) {
          return context.getResources().getString(R.string.generic_server_down);
      }
      else if (isServerProblem(error)) {
          return handleServerError(error, context);
      }
      else if (isNetworkProblem(error)) {
          return context.getResources().getString(R.string.no_internet);
      }
      return context.getResources().getString(R.string.generic_error);
  }
  
  /**
  * Determines whether the error is related to network
  * @param error
  * @return
  */
  private static boolean isNetworkProblem(Object error) {
      return (error instanceof NetworkError) || (error instanceof NoConnectionError);
  }
  /**
  * Determines whether the error is related to server
  * @param error
  * @return
  */
  private static boolean isServerProblem(Object error) {
      return (error instanceof ServerError) || (error instanceof AuthFailureError);
  }
  /**
  * Handles the server error, tries to determine whether to show a stock message or to 
  * show a message retrieved from the server.
  * 
  * @param err
  * @param context
  * @return
  */
  private static String handleServerError(Object err, Context context) {
      VolleyError error = (VolleyError) err;
  
      NetworkResponse response = error.networkResponse;
  
      if (response != null) {
          switch (response.statusCode) {
            case 404:
            case 422:
            case 401:
                try {
                    // server might return error like this { "error": "Some error occured" }
                    // Use "Gson" to parse the result
                    HashMap<String, String> result = new Gson().fromJson(new String(response.data),
                            new TypeToken<Map<String, String>>() {
                            }.getType());

                    if (result != null && result.containsKey("error")) {
                        return result.get("error");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                // invalid request
                return error.getMessage();

            default:
                return context.getResources().getString(R.string.generic_server_down);
            }
      }
        return context.getResources().getString(R.string.generic_error);
  }
}

 

總結:

Volley是一個非常好的庫,你可以嘗試使用一下,它會幫助你簡化網絡請求,帶來更多的益處。

我也希望更加全面的介紹Volley,以后可能會介紹使用volley加載圖像的內容,歡迎關注

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM