使用Google Volley及遇到的坑

http://blog.csdn.net/guolin_blog/article/details/17482095      http://blog.csdn.net/airk000/article/details/39003587
下载Volley的源码: git clone https://android.googlesource.com/platform/frameworks/volley

1 > Volley的介绍

    Volley从服务器端请求字符串数据图片数据,和JSON格式数据。Volley可以写出标准化的模板代码,并让我们更加关注于我们的业务逻辑,这对于我们的App来说是非常重要的。

 

Volley的优势:

 

  •  自动的调度网络请求
  •  多并发的网络请求
  •   可以缓存http请求
  •  支持请求的优先级
  •  支持取消请求的API,可以取消单个请求,可以设置取消请求的范围域。
  •  代码标准化,使开发者更容易专注于我们的业务的逻辑处理
  •  更容易给UI填充来自网络请求的数据
  •  Volley可以是作为调试和跟踪的工具

    Volley非常不适合大的文件流操作,例如上传和下载。因为Volley会把所有的服务器端返回的数据在解析期间缓存进内存。大量的下载操作可以考虑用DownLoaderManager和异步加载来实现。

Volley库的地址如下:

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

 

2 > Volley的工作原理

      为了发送一个请求,你可以通过构造方法new出来一个Request,然后调用add()把请求添加进RequestQueue中, 当调用add方法时,Volley会运行一个缓存处理线程和一个网络调度线程池.如果请求被缓存线程已经缓存的话,请求将不会放进请求队列, 也就是说不会发出Http请求。 而会直接复用请求 ,并将数据返回到主线程。如果缓存线程中没有缓存到请求的话,请求将会放进 请求队列, 网络请求成功后,  请求 将会被缓存进cache,接着网络调度线程将处理该请求,并解析数据。
Volley的工作原理图如下:

3 使用volley请求数据

    3.1 利用StringRequest请求字符串数据

    Volley提供了一个便利的方法Volley.newRequestQueue可以使用默认的设置创建一个RequestQueue,例如:

 

Java代码   收藏代码
  1. finalTextView mTextView =(TextView) findViewById(R.id.text);  
  2. ...  
  3.    
  4. // Instantiate the RequestQueue.  
  5. RequestQueue queue =Volley.newRequestQueue(this);  
  6. String url ="http://www.google.com";  
  7.    
  8. // Request a string response from the provided URL.  
  9. StringRequest stringRequest =newStringRequest(Request.Method.GET, url,  
  10. newResponse.Listener<String>(){  
  11. @Override  
  12. publicvoid onResponse(String response){  
  13. // Display the first 500 characters of the response string.  
  14. mTextView.setText("Response is: "+ response.substring(0,500));  
  15. }  
  16. },newResponse.ErrorListener(){  
  17. @Override  
  18. publicvoid onErrorResponse(VolleyError error){  
  19. mTextView.setText("That didn't work!");  
  20. }  
  21. });  
  22. // Add the request to the RequestQueue.  
  23. queue.add(stringRequest);  
 

3.2 利用ImageRequest请求图片

Java代码   收藏代码
  1. publicclassMainActivityextendsActivity{  
  2. privateImageView mImageView;  
  3. @Override  
  4. protectedvoid onCreate(Bundle savedInstanceState){  
  5.     super.onCreate(savedInstanceState);  
  6.     setContentView(R.layout.activity_main);  
  7.     mImageView =(ImageView) findViewById(R.id.iv_image);  
  8.    
  9. }  
  10.    
  11. publicvoid requestImage(){  
  12. // Retrieves an image specified by the URL, displays it in the UI.  
  13.    String url ="http://i.imgur.com/7spzG.png";  
  14.    ImageRequest request =newImageRequest(url,  
  15.    newResponse.Listener<Bitmap>(){  
  16. @Override  
  17. publicvoid onResponse(Bitmap bitmap){  
  18.    Toast.makeText(MainActivity.this,"success",1).show();  
  19.    mImageView.setImageBitmap(bitmap);  
  20. }  
  21. },0,0,null,newResponse.ErrorListener(){  
  22. publicvoid onErrorResponse(VolleyError error){  
  23. /*mImageView 
  24. .setImageResource(R.drawable.image_load_error);*/  
  25. }  
  26. });  
  27.     Volley.newRequestQueue(getApplicationContext()).add(request);  
  28. }  
  29.    
  30. }  
     

3.3 使用NetworkImageView结合imageLoader加载网络图片

Java代码   收藏代码
  1. <com.android.volley.toolbox.NetworkImageView  
  2. android:id="@+id/networkImageView"  
  3. android:layout_width="150dp"  
  4. android:layout_height="170dp"  
  5. android:layout_centerHorizontal="true"/>  
Java代码   收藏代码
  1. ImageLoader mImageLoader;  
  2. ImageView mImageView;  
  3. // The URL for the image that is being loaded.  
  4. private static final String IMAGE_URL =  
  5.     "http://developer.android.com/images/training/system-ui.png";  
  6. ...  
  7. mImageView = (ImageView) findViewById(R.id.regularImageView);  
  8.    
  9. // Get the ImageLoader through your singleton class.  
  10.   mImageLoader = MySingleton.getInstance(this).getImageLoader();  
  11.   mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,R.drawable.def_image, R.drawable.err_image));  
 
Java代码   收藏代码
  1. TextView mTxtDisplay;  
  2. ImageView mImageView;  
  3. mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);  
  4. String url = "http://my-json-feed";  
  5.    
  6. JsonObjectRequest jsObjRequest = new JsonObjectRequest  
  7.         (Request.Method.GET, url, nullnew Response.Listener<JSONObject>() {  
  8.    
  9.     @Override  
  10.     public void onResponse(JSONObject response) {  
  11.         mTxtDisplay.setText("Response: " + response.toString());  
  12.     }  
  13. }, new Response.ErrorListener() {  
  14.    
  15.     @Override  
  16.     public void onErrorResponse(VolleyError error) {  
  17.         // TODO Auto-generated method stub  
  18.    
  19.     }  
  20. });  
  21.    
  22. // Access the RequestQueue through your singleton class.  
  23. MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);  
 

 3.4 使用JsonObjectRequest请求json格式数据(个人感觉和StringRequest一样)

Java代码   收藏代码
  1. TextView mTxtDisplay;  
  2. ImageView mImageView;  
  3. mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);  
  4. String url = "http://my-json-feed";  
  5.    
  6. JsonObjectRequest jsObjRequest = new JsonObjectRequest  
  7.         (Request.Method.GET, url, nullnew Response.Listener<JSONObject>() {  
  8.    
  9.     @Override  
  10.     public void onResponse(JSONObject response) {  
  11.         mTxtDisplay.setText("Response: " + response.toString());  
  12.     }  
  13. }, new Response.ErrorListener() {  
  14.    
  15.     @Override  
  16.     public void onErrorResponse(VolleyError error) {  
  17.         // TODO Auto-generated method stub  
  18.    
  19.     }  
  20. });  
  21.    
  22. // Access the RequestQueue through your singleton class.  
  23. MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);  

4>  如何取消请求

   Volley通过调用cancel()方法取消一个请求,并保证请求不会在Response中回调处理,你可以在activity中的onStop中 取消所有你想取消的请求,最简单的方法就是 给所有想取消的请求 调用setTag(TAG)设置标记,然后放进请求队列,在Activity的onStop方法中调用cancelAll(TAG)方法取消请求。
代码如下:

   给请求设置TAG,并放进请求队列

Java代码   收藏代码
  1. public static final String TAG = "MyTag";  
  2. StringRequest stringRequest; // Assume this exists.  
  3. RequestQueue mRequestQueue;  // Assume this exists.  
  4. // 给请求设置tag  
  5. stringRequest.setTag(TAG);  
  6. // 把请求放进请求队列  
  7. mRequestQueue.add(stringRequest);  

   在onstop方法中取消所有设置Tag的请求

Java代码   收藏代码
  1. @Override  
  2. protected void onStop () {  
  3.     super.onStop();  
  4.     if (mRequestQueue != null) {  
  5.         mRequestQueue.cancelAll(TAG);  
  6.     }  
  7. }  

 

注意:当取消请求的时候,onResponse和onErrorResponse方法将不会执行

 

5>  如何新建一个RequestQueue

 5.1 RequestQueue的配置

      Volley.newRequestQueue ​可以创建一个请求队列,但那只能使用Volley
的默认配置,下面来讲讲如何来创建自己的一个请求队列。每次去new一个请求队列是非常消耗内存的,我们可以利用单列模式来避免每次都去new一个请求队列。
 
Java代码   收藏代码
  1. RequestQueue mRequestQueue;  
  2.    
  3. // 配置缓存大小  
  4. Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap  
  5.    
  6. // 设置网络请求方式(UrlConnection和HttpClient)  
  7. Network network = new BasicNetwork(new HurlStack());  
  8.    
  9. //初始化RequestQueue  
  10. mRequestQueue = new RequestQueue(cache, network);  
  11.    
  12. // 开启RequestQueue  
  13. mRequestQueue.start();  
  14.    
  15. String url ="http://www.myurl.com";  
  16.    
  17. // Formulate the request and handle the response.  
  18. StringRequest stringRequest = new StringRequest(Request.Method.GET, url,  
  19.         new Response.Listener<String>() {  
  20.     @Override  
  21.     public void onResponse(String response) {  
  22.         // Do something with the response  
  23.     }  
  24. },  
  25.     new Response.ErrorListener() {  
  26.         @Override  
  27.         public void onErrorResponse(VolleyError error) {  
  28.             // Handle error  
  29.     }  
  30. });  
  31.    
  32. // 把请求添加进请求队列  
  33. mRequestQueue.add(stringRequest);  
  5.2 RequestQueue中BasicNewWork(网络)的内部机制
      查看volley源码可以知道: Volley一般联网的方式有HttpClient和HttpURLConnection,
如果API低于9的话可以调用HttpRULConnection请求网络,如果API高于9的话, 会用HttpClient
请求网络,源码如下:

 

Java代码   收藏代码
  1. HttpStack stack;  
  2. ...  
  3. // If the device is running a version >= Gingerbread...  
  4. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {  
  5.     // ...use HttpURLConnection for stack.  
  6. else {  
  7.     // ...use AndroidHttpClient for stack.  
  8. }  
  9. Network network = new BasicNetwork(stack);    

 

 5.3  使用requestQueue的注意事项

 

  • 如果你的项目要进行频繁的网路请求,你可以在application的onCreate方法中创建一个RequestQueue,google官方文档强烈建议我们把RequestQueue设计成单列模式。
  • 但是要注意,在RequestQueue中的Context对象为applicationContext,而不是Activity对应的context对象,可以避免在activity中每次都去消费使用activity中的context对象。
下面是使用单列设计模式创建的一个请求队列

 

Java代码   收藏代码
  1. private static MySingleton mInstance;  
  2.     private RequestQueue mRequestQueue;  
  3.     private static Context mCtx;  
  4.    
  5.     public static synchronized MySingleton getInstance(Context context) {  
  6.         if (mInstance == null) {  
  7.             mInstance = new MySingleton(context);  
  8.         }  
  9.         return mInstance;  
  10.     }  
  11.    
  12.     public RequestQueue getRequestQueue() {  
  13.         if (mRequestQueue == null) {  
  14.             // getApplicationContext() is key, it keeps you from leaking the  
  15.             // Activity or BroadcastReceiver if someone passes one in.  
  16.             mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());  
  17.         }  
  18.         return mRequestQueue;  
  19.     }  
  20.    
  21.     public <T> void addToRequestQueue(Request<T> req) {  
  22.         getRequestQueue().add(req);  
  23.     }  
  24.    
  25.     public ImageLoader getImageLoader() {  
  26.         return mImageLoader;  
  27.     }  
  28. }  

-----------------------------------------------------------------
在实际开发中可能并不那么实用,下面来总结我们项目中是如何封装使用的。

 1>  Volley是用来请求网络数据的,首先我们要准备好要传递的URL参数

Java代码   收藏代码
  1. HashMap<String, String> params = new HashMap<String, String>();  
  2.        params.put("user_id", userId);  
  3.        params.put("count", count + "");  
  4.        params.put("since_time", timeSince);  
  5.        params.put("profile_user_id", profileUserId);  
  6.        String url = HttpConstant.DOMAIN_NORMAL + HttpConstant.FRIENDS_FANS_1;  

 

2 把URL拼凑成一个完整的链接

Java代码   收藏代码
  1. public void get(String url, Map<String, String> data, String tag, final CallbackLightRequest<LightResponse> callback) {  
  2.         if (!ConfigRequests.isNetworkOkWithAlert(mContext)) {  
  3.             callNetworkError(tag, url, data, callback);  
  4.             return;  
  5.         }  
  6.         Map<String, String> defaultParams = ConfigRequests.getInstance().getDefaultParams();  
  7.         if (defaultParams != null && defaultParams.size() > 0) {  
  8.             data.putAll(defaultParams);  
  9.         }  
  10.         if (data != null && data.size() > 0) {  
  11.             url = url + "?" + RequestUtils.map2QueryString(data);  
  12.         }  
  13.         this._get(url, tag, callback);  
  14.     }  

    拼凑URl的方法:map2QueryString

Java代码   收藏代码
  1. public class RequestUtils {  
  2.     /** 
  3.      * 生成QueryString,以 a=1&b=2形式返回 
  4.      */  
  5.     public static String map2QueryString(Map<String, String> map) {  
  6.         StringBuilder sb = new StringBuilder();  
  7.         String value;  
  8.         try {  
  9.             if (map != null && map.size() > 0) {  
  10.                 for (Entry<String, String> entry : map.entrySet()) {  
  11.                     value = "";  
  12.                     value = entry.getValue();  
  13.                     if (StringUtils.isEmpty(value)) {  
  14.                         value = "";  
  15.                     } else {  
  16.                         value = URLEncoder.encode(value, Models.Encoding.UTF8);  
  17.                     }  
  18.                     sb.append(entry.getKey()).append("=").append(value)  
  19.                             .append("&");  
  20.                 }  
  21.                 sb.deleteCharAt(sb.length() - 1);  
  22.             }  
  23.         } catch (UnsupportedEncodingException e) {  
  24.             e.printStackTrace();  
  25.         }  
  26.         return sb.toString();  
  27.     }  

 3>  发送请求,并将请求放进Requestqueue中,我把请求统一封装在一个

    AppRequest类中,这个类如下:

Java代码   收藏代码
  1. private void _get(final String url, final String tag, final Callback callback) {  
  2.   
  3.         LogUtils.e(TAG, url);  
  4.   
  5.         StringRequest req = new StringRequest(Request.Method.GET, url,  
  6.   
  7.                 new Listener<String>() {  
  8.   
  9.                     @Override  
  10.   
  11.                     public void onResponse(String text) {  
  12.   
  13.                         LogUtils.i(TAG, text);  
  14.   
  15.                         if (callback != null) {  
  16.   
  17.                             LightResponse res = new LightResponse();  
  18.   
  19.                             res.tag = tag;  
  20.   
  21.                             res.url = url;  
  22.   
  23.                             res.status = Models.StatusCode.NetworkSuccess;  
  24.   
  25.                             res.setData(text);  
  26.   
  27.                             callback.onFinish(res);  
  28.   
  29.                         }  
  30.   
  31.                     }  
  32.   
  33.                 }, new Response.ErrorListener() {  
  34.   
  35.             @Override  
  36.   
  37.             public void onErrorResponse(VolleyError error) {  
  38.   
  39.                 if (callback != null) {  
  40.   
  41.                     LightResponse res = new LightResponse();  
  42.   
  43.                     res.tag = tag;  
  44.   
  45.                     res.url = url;  
  46.   
  47.                     res.status = Models.StatusCode.NetworkError;  
  48.   
  49.                     res.message = error.getMessage();  
  50.   
  51.                     callback.onFinish(res);  
  52.   
  53.                 }  
  54.   
  55.             }  
  56.   
  57.         }) {  
  58.   
  59.             @Override  
  60.   
  61.             public Map<String, String> getHeaders() throws AuthFailureError {  
  62.   
  63.                 /* 此处是相对于实际业务配置的 */  
  64.   
  65.                 return new HashMap<String, String>();  
  66.   
  67.             }  
  68.   
  69.         };  
  70.   
  71.         mQueue.add(req);  
  72.   
  73.     }  

 

说明:Volley有2种回调一种是onResponse,一种是onErrorResponse,但是实际开发中可能需要更多的回调需要处理,例如:
1 请求服务器成功,数据无误
2 请求服务器成功,但是可能返回的JSON数据有问题
3 服务器请求成功,但是返回码有问题
4 无网络连接
5 网络正常,但是服务器没返给我们数据(刷新失败)
6 系统发生异常
......
当然我们可以根据实际业务需求来设定相应的回调。
上面我只做了一个CallBack来回调,不管请求失败还是成功,我都调用了onFinish()方法
 4>  回调处理

 

Java代码   收藏代码
  1. try {  
  2.            request.get(url, params, ""new CallbackLightRequest<LightResponse>() {  
  3.                @Override  
  4.                public void call(LightResponse response) {  
  5.                    if (response.isReplied()) {  
  6.                        ApiBeans.Followers2 bean = GsonUtil.getFromStr(ApiBeans.Followers2.class, response.getText());//json解析  
  7.                        if (bean != null && bean.data != null) {  
  8.                            httpCallback.onResponse(bean.data);//请求成功  
  9.                        } else {  
  10.                            httpCallback.onFailure(new RequestFailure(RequestFailure.Type.parse), null);//解析异常  
  11.                        }  
  12.                    } else {  
  13.                        httpCallback.onFailure(new RequestFailure(RequestFailure.Type.network), null);//网络异常  
  14.                    }  
  15.                }  
  16.            });  
  17.        } catch (Exception ex) {  
  18.     
  19.            httpCallback.onFailure(new RequestFailure(RequestFailure.Type.exception), ex);//异常处理  
  20.        }  

 

这里我做了2种类型的回调:
 一  成功
 二  失败(包含4种类型)
回调的接口如下:
Java代码   收藏代码
  1. public interface HttpCallback<T> {  
  2.     void onResponse(T response);//成功回调  
  3.     void onFailure(RequestFailure failure, Exception ex);//异常处理  
  4. }  
 错误的回调类型,这里当然也可以封装成一个泛型
Java代码   收藏代码
  1. public class RequestFailure {  
  2.     public enum Type {  
  3.         network,//网络问题  
  4.         responseCode,//错误的返回码处理  
  5.         exception,//系统异常  
  6.         parse, //解析  
  7.     }  
  8.     public RequestFailure(Type type) {  
  9.         this.what = type;  
  10.     }  
  11.     public Type what;  
  12. }  
 
network:代表网络连接异常
responseCode:错误的响应码处理
exception:系统异常处理
parse :Json解析异常

5> 再次封装
    当上面的代码写多了之后,你会发现你一直在写重复的代码,仔细看看除了回调的对象不一样之外,其它都参数都基本一样,很容易就想到用泛型来对上面的代码进行封装,代码如下:
Java代码   收藏代码
  1. public static void getFolowFansList(final Context context, final LightRequest request, String userId, String profileUserId,  
  2.                                         String timeSince, int count, final HttpCallback<ApiBeans.FollowerListObject<Fan>> httpCallback) {  
  3.         HashMap<String, String> params = new HashMap<String, String>();  
  4.         params.put("user_id", userId);  
  5.         params.put("count", count + "");  
  6.         params.put("since_time", timeSince);  
  7.         params.put("profile_user_id", profileUserId);  
  8.         String url = HttpConstant.DOMAIN_NORMAL + HttpConstant.FRIENDS_FANS_1;  
  9.         
  10.         doRequest(request,url, data, httpCallback, ApiBeans.FollowerListObject.class);  
  11.     }  
  把回调的代码用泛型进行封装
Java代码   收藏代码
  1. private static <T> void doRequest(LightRequest request,  
  2.                                       String url, HashMap<String, String> params,  
  3.                                       final HttpCallback callback, final Class<?> cls) {  
  4.         try {  
  5.             request.get(url, params, ""new CallbackLightRequest<LightResponse>() {  
  6.                 @Override  
  7.                 public void call(LightResponse response) {  
  8.                     if (response.isReplied()) {  
  9.                         T bean = GsonUtil.getFromStr(cls, response.getText());  
  10.                         if (bean != null) {  
  11.                             callback.onResponse(bean);  
  12.                         } else {  
  13.                             callback.onFailure(new RequestFailure(RequestFailure.Type.parse), null);  
  14.                         }  
  15.                     } else {  
  16.                         callback.onFailure(new RequestFailure(RequestFailure.Type.network), null);  
  17.                     }  
  18.                 }  
  19.             });  
  20.         } catch (Exception ex) {  
  21.             callback.onFailure(new RequestFailure(RequestFailure.Type.exception), null);  
  22.         }  
  23.     }  
 
6>  volley 在项目中进行业务处理
下面看看如何现在封装的Volley在项目中进行业务处理的?
Java代码   收藏代码
  1. model.getFasList(mContext, lightRequest, Global.userDetail.userId, profileUserId, thisTimeSince, count,  
  2.                new HttpCallback<ApiBeans.FollowerListObject<Fan>>() {  
  3.                
  4.                    @Override  
  5.                    public void onResponse(ApiBeans.FollowerListObject<Fan> data) {  
  6.                        try {  
  7.                             //数据返回成功  
  8.                            if (data.userList != null) {  
  9.                                adapter.setIsAllLoaded(data.infoCount < Constant.FEED_DEFAULT_NUM);  
  10.                                lv.setIfHasMore(data.infoCount, Constant.FEED_DEFAULT_NUM);  
  11.                                if (isRefresh) {  
  12.                                    adapter.setDataList(data.userList);  
  13.                                    lv.setAdapter(adapter);  
  14.                                } else {  
  15.                                    adapter.addDataList(data.userList);  
  16.                                    adapter.notifyDataSetChanged();  
  17.                                }  
  18.                            } else {  
  19.                            //刷新失败  
  20.                                if (isRefresh) {  
  21.                                    ToastModel.showRefreshFail(mContext);  
  22.                                } else {  
  23.                                    ToastModel.showLoadMoreError(mContext);  
  24.                                }  
  25.                            }  
  26.                        } catch (Exception e) {  
  27.                        } finally {  
  28.                            isDownloading = false;  
  29.                            if (loadingView != null) {  
  30.                                loadingView.setVisibility(View.GONE);  
  31.                                loadingView = null;  
  32.                            }  
  33.                            refreshEnd();  
  34.                        }  
  35.                    }  
  36.                    @Override  
  37.                    public void onFailure(RequestFailure failure, Exception ex) {  
  38.                          //网络异常  
  39.                        if (failure.what == RequestFailure.Type.network) {  
  40.                            ToastModel.showRed(mContext, R.string.tip_network_error);  
  41.                        } else if (failure.what == RequestFailure.Type.parse) {  
  42.                            //解析失败  
  43.                            ToastModel.showRed(mContext, "解析失败");  
  44.                        } else if (failure.what == RequestFailure.Type.exception) {  
  45.                            //系统异常  
  46.                            ToastModel.showRed(mContext, "系统异常");  
  47.                        }  
  48.                    }  
  49.                });  
  50.    }  
 
   代码方便简单,通熟易懂, 层次分明,逻辑清晰有没有呢? Volley不仅简化了我们的开发流程,而且可以让我们更专注于我们的业务处理。这是volley非常大的一个优点。
-----------------------------------------------------------------
使用Volery已经快整整一年了,下面我来总结一下,我使用Volley时踩到的坑

     (一) Volley的二次封装

    下面看看我是怎么对Volley的二次封装的:
Java代码   收藏代码
  1. protected <T> void doSimpleRequest(String url, HashMap<String, String> params, final Class<?> clazz, final SimpleCallback callback) {  
  2.       String requestUrl = urlBuilder(url, params);  
  3.       Logger.e("url", requestUrl);  
  4.       Response.Listener<String> successListener = new Response.Listener<String>() {  
  5.           @Override  
  6.           public void onResponse(String response) {  
  7.               try {  
  8.                   T bean = GsonUtils.getFromStr(clazz, response);  
  9.                   callback.onResponse(bean);  
  10.               } catch (Exception e) {  
  11.                   callback.onError();  
  12.               }  
  13.           }  
  14.       };  
  15.       Response.ErrorListener errorListener = new Response.ErrorListener() {  
  16.           @Override  
  17.           public void onErrorResponse(VolleyError error) {  
  18.               callback.onError();  
  19.           }  
  20.       };  
  21.       StringRequest requestRequest = new StringRequest(url, params, successListener, errorListener);  
  22.       LeSportsApplication.getApplication().addHttpRequest(requestRequest);  
  23.   }  
 
      请求的时候使用的StringRequest,请求成功后,会返回一个String类型,然后用Gson解析成JavaBean,我之前一直都这么使用,看似天衣无缝,其实你会发现JSon解析是在主线程中实现的,如果数据量大的话,很容易导致UI卡顿。优化方案就是在数据解析在子线程中进行
(1) 新建一个GsonRequest请求类,继承Request对象
(2) 重写parseNetworkResponse方法,这个方法其实是在CacheDispatch这个子线程中执行的
Java代码   收藏代码
  1. <span style="font-size: 18px;">package com.lesports.common.volley.toolbox;  
  2. import android.os.Looper;  
  3. import com.google.gson.Gson;  
  4. import com.google.gson.JsonSyntaxException;  
  5. import com.lesports.common.volley.NetworkResponse;  
  6. import com.lesports.common.volley.ParseError;  
  7. import com.lesports.common.volley.Request;  
  8. import com.lesports.common.volley.Response;  
  9. import com.letv.core.log.Logger;  
  10. import org.json.JSONException;  
  11. import org.json.JSONObject;  
  12. import java.io.UnsupportedEncodingException;  
  13. import java.util.HashMap;  
  14. /** 
  15.  * Created by liuyu8 on 2015/9/15. 
  16.  */  
  17. public class MyGsonRequest<T> extends Request<T>{  
  18.     private final Logger mLogger = new Logger("GsonRequest");  
  19.     private final Gson gson = new Gson();  
  20.     private final Class<T> clazz;  
  21.     private Response.Listener<T> listener;  
  22.     /** 
  23.      * Make a GET request and return a parsed object from JSON. Assumes 
  24.      * {@link Request.Method#GET}. 
  25.      * 
  26.      * @param url 
  27.      *            URL of the request to make 
  28.      * @param clazz 
  29.      *            Relevant class object, for Gson's reflection 
  30.      */  
  31.     public MyGsonRequest(String url,HashMap<String, String> params,Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  32.         super(Request.Method.GET, url, params,errorListener);  
  33.         this.clazz = clazz;  
  34.         this.listener = listener;  
  35.     }  
  36.     /** 
  37.      * Make a GET request and return a parsed object from JSON. Assumes 
  38.      * {@link Request.Method#GET}. 
  39.      * 
  40.      * @param url 
  41.      *            URL of the request to make 
  42.      * @param clazz 
  43.      *            Relevant class object, for Gson's reflection 
  44.      */  
  45.     public MyGsonRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  46.         super(Request.Method.GET, url, errorListener);  
  47.         this.clazz = clazz;  
  48.         this.listener = listener;  
  49.     }  
  50.     /** 
  51.      * Like the other, but allows you to specify which {@link Request.Method} you want. 
  52.      * 
  53.      * @param method 
  54.      * @param url 
  55.      * @param clazz 
  56.      * @param listener 
  57.      * @param errorListener 
  58.      */  
  59.     public MyGsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {  
  60.         super(method, url, errorListener);  
  61.         this.clazz = clazz;  
  62.         this.listener = listener;  
  63.     }  
  64.     @Override  
  65.     protected void deliverResponse(T response) {  
  66.         if(listener != null){  
  67.             listener.onResponse(response);  
  68.         }  
  69.     }  
  70.     @Override  
  71.     protected Response<T> parseNetworkResponse(NetworkResponse response) {  
  72.         try {  
  73.             if(Looper.myLooper() == Looper.getMainLooper()){  
  74.                 mLogger.e("数据是 ==>在主线程中解析的~");  
  75.             }else{  
  76.                 mLogger.e("数据不是 ==>在主线程中解析的~");  
  77.             }  
  78.             String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  
  79.             JSONObject jsonObject = new JSONObject(json);  
  80.             if(null != jsonObject && jsonObject.has("code") && jsonObject.getInt("code") == 200){  
  81.                 return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));  
  82.             }else{  
  83.                 return Response.error(new ParseError());  
  84.             }  
  85.         } catch (UnsupportedEncodingException e) {  
  86.             return Response.error(new ParseError(e));  
  87.         } catch (JsonSyntaxException e) {  
  88.             mLogger.e("JsonSyntaxException ==== ");  
  89.             return Response.error(new ParseError(e));  
  90.         } catch (JSONException e) {  
  91.             return Response.error(new ParseError(e));  
  92.         }  
  93.     }  
  94.     @Override  
  95.     public void finish(final String tag) {  
  96.         super.finish(tag);  
  97.         listener = null;  
  98.     }  
  99. }</span>  
 (3) 然后进行二次封装
Java代码   收藏代码
  1. /** 
  2.      * 优化: 
  3.      * (1)避免在主线程中解析json数据 
  4.      * (2)添加了取消请求方法 
  5.      * 
  6.      * @param tag 
  7.      * @param url 
  8.      * @param params 
  9.      * @param clazz 
  10.      * @param callback 
  11.      * @param <T> 
  12.      */  
  13.     protected <T> void doRequest(String tag, String url, HashMap<String, String> params, final Class<?> clazz, final HttpCallback callback) {  
  14.         String requestUrl = urlBuilder(url, params);  
  15.         Logger.e("BaseTVApi", requestUrl);  
  16.         callback.onLoading();  
  17.         Response.Listener<T> successListener = new Response.Listener<T>() {  
  18.             @Override  
  19.             public void onResponse(T bean) {  
  20.                 if (bean != null) {  
  21.                     callback.onResponse(bean);  
  22.                     Logger.e("BaseTVApi""request-->onResponse");  
  23.                 } else {  
  24.                     callback.onEmptyData();  
  25.                     Logger.e("BaseTVApi""request-->onEmptyData");  
  26.                 }  
  27.             }  
  28.         };  
  29.         Response.ErrorListener errorListener = new Response.ErrorListener() {  
  30.             @Override  
  31.             public void onErrorResponse(VolleyError error) {  
  32.                 callback.onError();  
  33.                 Logger.e("BaseTVApi""异常信息-->" + error.getMessage());  
  34.             }  
  35.         };  
  36.         GsonRequest requestRequest = new GsonRequest(url, params, clazz, successListener, errorListener);  
  37.         requestRequest.setTag(tag);  
  38.         LeSportsApplication.getApplication().addHttpRequest(requestRequest);  
  39.     }  
  打印log你会发现,你会发现数据解析是在子线程中执行的。

(二)使用Volley内存泄露

    在用MAT做内存泄露检查的时候,发现由于Volley的回调没有干掉导致的泄露问题,解决方法就是保证在Activity退出时,cancel掉请求,Request方法有一个cancel和finish方法并在这2个方法中把listener置为空。
 (1) 在Application中提供一个取消请求的方法,因为一般请求队列是在Application中初始化的。
Java代码   收藏代码
  1. /** 
  2.    * 网络请求优化,取消请求 
  3.    * @param tag 
  4.    */  
  5.   public  void  cancelRequest(String tag){  
  6.       try {  
  7.           mRequestQueue.cancelAll(tag);  
  8.       }catch (Exception e){  
  9.           Logger.e("LeSportsApplication","tag =="+ tag + "的请求取消失败");  
  10.       }  
  11.   }  
(2) 在onStop中调用cancelRequest方法,最终会调用request的finish方法
(3) 在GsonRequest中重写finish方法,并在该方法中把listener置为空
Java代码   收藏代码
  1. <span style="font-size: 18px;">  @Override  
  2.     public void finish(final String tag) {  
  3.         super.finish(tag);  
  4.         listener = null;  
  5.     }</span>  
  (4) 在再看看GsonRequest 的父类Request的finish方法,我已经把mErrorListener干掉了。
Java代码   收藏代码
  1. public void finish(final String tag) {  
  2.         if (mRequestQueue != null) {  
  3.             mRequestQueue.finish(this);  
  4.         }  
  5.         if (MarkerLog.ENABLED) {  
  6.             final long threadId = Thread.currentThread().getId();  
  7.             if (Looper.myLooper() != Looper.getMainLooper()) {  
  8.                 // If we finish marking off of the main thread, we need to  
  9.                 // actually do it on the main thread to ensure correct ordering.  
  10.                 Handler mainThread = new Handler(Looper.getMainLooper());  
  11.                 mainThread.post(new Runnable() {  
  12.                     @Override  
  13.                     public void run() {  
  14.                         mEventLog.add(tag, threadId);  
  15.                         mEventLog.finish(this.toString());  
  16.                     }  
  17.                 });  
  18.                 return;  
  19.             }  
  20.             mEventLog.add(tag, threadId);  
  21.             mEventLog.finish(this.toString());  
  22.         } else {  
  23.             long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;  
  24.             if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {  
  25.                 VolleyLog.d("%d ms: %s", requestTime, this.toString());  
  26.             }  
  27.         }  
  28.         mErrorListener = null;  
  29.     }  
     总结:请求取消最终都会调用Request的finish方法,我们只要在这个方法中,把2个回调给干掉,问题就解决了。

(三) 在onStop中取消请求

    QA妹子给我报了一个奇葩的bug,具体是第一次打开应用的时候APP一直在loading,找了半天发现原来是由于是取消请求不当引起的。
   原因是:我第一次进入应用的时候,立马弹出了一个升级对话框,这个时候刚好触发了onStop方法,自然请求就取消掉了,所以就一直在loading呗! 后来我就把该页面的请求放在onDestroy中cancel。

(四)嵌套请求

  一个页面中可能会有多个请求,有的请求要等到其它的请求完后才能进行。就像这样:
Java代码   收藏代码
  1. <span style="font-size: 18px;"private void requestUserSubscribesGame() {  
  2.         uid = LoginUtils.getUid();  
  3.         if (StringUtils.isStringEmpty(uid)) {  
  4.             return;  
  5.         }  
  6.         UserTVApi.getInstance().getUserSubscribeGameId(TAG, uid, new SimpleCallback<ApiBeans.SubScribeListModel>() {  
  7.             @Override  
  8.             public void onResponse(ApiBeans.SubScribeListModel bean) {  
  9.                 SubScribeModel subScribeModel = bean.data;  
  10.                 if (subScribeModel != null) {  
  11.                     scribeGameEntrys = subScribeModel.getEntities();  
  12.                     requestHallData();  
  13.                 }  
  14.             }  
  15.             @Override  
  16.             public void onError() {  
  17.                 Logger.e(TAG, "获取订阅信息失败...");  
  18.             }  
  19.         });  
  20.     }</span>  
 
      我之前也很喜欢这样写,后来我查看log的时候发现,第一个请求会超时请求,导致会重试一次。我设置的超时时间是2秒,可能是由于第二个请求太慢,导致请求第一次回调的时候太长导致的吧,具体原因还需要去查。
    解决方法:有嵌套请求的情况,建议第一个接口请求完后,用handler发送消息,然后第二个开始请求。
-------------------------------------------------------------------
>> Android Volley Https证书不信任的解决方案:(单向认证)
首先新建一个类FakeX509TrustManager:

public class FakeX509TrustManager implements X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new
            X509Certificate[] {};

    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers;
    }

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                // TODO Auto-generated method stub
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[] { new FakeX509TrustManager() };
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
    }

}

然后在进行StringRequest之前设置:
FakeX509TrustManager.allowAllSSL();

mStringRequest = new StringRequest(Request.Method.POST,
  url,
  getDefaultSuccessListener(),
  mErrorListener){
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return params;
            }
  };
mRequestQueue.add(mStringRequest);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值