使用Google Volley及遇到的坑
2016-06-12 10:50
330 查看
可参考:http://blog.csdn.net/yanbober/article/details/45307217http://blog.csdn.net/guolin_blog/article/details/17482095http://blog.csdn.net/airk000/article/details/39003587下载Volley的源码:gitclonehttps://android.googlesource.com/platform/frameworks/volley
1
>Volley的介绍
Volley从服务器端请求字符串数据,图片数据,和JSON格式数据。Volley可以写出标准化的模板代码,并让我们更加关注于我们的业务逻辑,这对于我们的App来说是非常重要的。
Volley的优势:
自动的调度网络请求
多并发的网络请求
可以缓存http请求
支持请求的优先级
支持取消请求的API,可以取消单个请求,可以设置取消请求的范围域。
代码标准化,使开发者更容易专注于我们的业务的逻辑处理
更容易给UI填充来自网络请求的数据
Volley可以是作为调试和跟踪的工具
Volley非常不适合大的文件流操作,例如上传和下载。因为Volley会把所有的服务器端返回的数据在解析期间缓存进内存。大量的下载操作可以考虑用DownLoaderManager和异步加载来实现。
Volley库的地址如下:
gitclonehttps://android.googlesource.com/platform/frameworks/volley
为了发送一个请求,你可以通过构造方法new出来一个Request,然后调用add()把请求添加进RequestQueue中,当调用add方法时,Volley会运行一个缓存处理线程和一个网络调度线程池.如果请求被缓存线程已经缓存的话,请求将不会放进请求队列,也就是说不会发出Http请求。而会直接复用请求,并将数据返回到主线程。如果缓存线程中没有缓存到请求的话,请求将会放进请求队列,网络请求成功后,请求将会被缓存进cache,接着网络调度线程将处理该请求,并解析数据。
Volley的工作原理图如下:
Volley提供了一个便利的方法Volley.newRequestQueue可以使用默认的设置创建一个RequestQueue,例如:
Java代码
finalTextViewmTextView=(TextView)findViewById(R.id.text);
...
//InstantiatetheRequestQueue.
RequestQueuequeue=Volley.newRequestQueue(this);
Stringurl="http://www.google.com";
//RequestastringresponsefromtheprovidedURL.
StringRequeststringRequest=newStringRequest(Request.Method.GET,url,
newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
//Displaythefirst500charactersoftheresponsestring.
mTextView.setText("Responseis:"+response.substring(0,500));
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
mTextView.setText("Thatdidn'twork!");
}
});
//AddtherequesttotheRequestQueue.
queue.add(stringRequest);
Java代码
publicclassMainActivityextendsActivity{
privateImageViewmImageView;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView=(ImageView)findViewById(R.id.iv_image);
}
publicvoidrequestImage(){
//RetrievesanimagespecifiedbytheURL,displaysitintheUI.
Stringurl="http://i.imgur.com/7spzG.png";
ImageRequestrequest=newImageRequest(url,
newResponse.Listener<Bitmap>(){
@Override
publicvoidonResponse(Bitmapbitmap){
Toast.makeText(MainActivity.this,"success",1).show();
mImageView.setImageBitmap(bitmap);
}
},0,0,null,newResponse.ErrorListener(){
publicvoidonErrorResponse(VolleyErrorerror){
/*mImageView
.setImageResource(R.drawable.image_load_error);*/
}
});
Volley.newRequestQueue(getApplicationContext()).add(request);
}
}
Java代码
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="150dp"
android:layout_height="170dp"
android:layout_centerHorizontal="true"/>
Java代码
ImageLoadermImageLoader;
ImageViewmImageView;
//TheURLfortheimagethatisbeingloaded.
privatestaticfinalStringIMAGE_URL=
"http://developer.android.com/images/training/system-ui.png";
...
mImageView=(ImageView)findViewById(R.id.regularImageView);
//GettheImageLoaderthroughyoursingletonclass.
mImageLoader=MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL,ImageLoader.getImageListener(mImageView,R.drawable.def_image,R.drawable.err_image));
Java代码
TextViewmTxtDisplay;
ImageViewmImageView;
mTxtDisplay=(TextView)findViewById(R.id.txtDisplay);
Stringurl="http://my-json-feed";
JsonObjectRequestjsObjRequest=newJsonObjectRequest
(Request.Method.GET,url,null,newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
mTxtDisplay.setText("Response:"+response.toString());
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//TODOAuto-generatedmethodstub
}
});
//AccesstheRequestQueuethroughyoursingletonclass.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
Java代码
TextViewmTxtDisplay;
ImageViewmImageView;
mTxtDisplay=(TextView)findViewById(R.id.txtDisplay);
Stringurl="http://my-json-feed";
JsonObjectRequestjsObjRequest=newJsonObjectRequest
(Request.Method.GET,url,null,newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
mTxtDisplay.setText("Response:"+response.toString());
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//TODOAuto-generatedmethodstub
}
});
//AccesstheRequestQueuethroughyoursingletonclass.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
Volley通过调用cancel()方法取消一个请求,并保证请求不会在Response中回调处理,你可以在activity中的onStop中取消所有你想取消的请求,最简单的方法就是给所有想取消的请求调用setTag(TAG)设置标记,然后放进请求队列,在Activity的onStop方法中调用cancelAll(TAG)方法取消请求。
代码如下:
给请求设置TAG,并放进请求队列
Java代码
publicstaticfinalStringTAG="MyTag";
StringRequeststringRequest;//Assumethisexists.
RequestQueuemRequestQueue;//Assumethisexists.
//给请求设置tag
stringRequest.setTag(TAG);
//把请求放进请求队列
mRequestQueue.add(stringRequest);
在onstop方法中取消所有设置Tag的请求
Java代码
@Override
protectedvoidonStop(){
super.onStop();
if(mRequestQueue!=null){
mRequestQueue.cancelAll(TAG);
}
}
注意:当取消请求的时候,onResponse和onErrorResponse方法将不会执行
Volley.newRequestQueue可以创建一个请求队列,但那只能使用Volley
的默认配置,下面来讲讲如何来创建自己的一个请求队列。每次去new一个请求队列是非常消耗内存的,我们可以利用单列模式来避免每次都去new一个请求队列。
Java代码
RequestQueuemRequestQueue;
//配置缓存大小
Cachecache=newDiskBasedCache(getCacheDir(),1024*1024);//1MBcap
//设置网络请求方式(UrlConnection和HttpClient)
Networknetwork=newBasicNetwork(newHurlStack());
//初始化RequestQueue
mRequestQueue=newRequestQueue(cache,network);
//开启RequestQueue
mRequestQueue.start();
Stringurl="http://www.myurl.com";
//Formulatetherequestandhandletheresponse.
StringRequeststringRequest=newStringRequest(Request.Method.GET,url,
newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
//Dosomethingwiththeresponse
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//Handleerror
}
});
//把请求添加进请求队列
mRequestQueue.add(stringRequest);
5.2RequestQueue中BasicNewWork(网络)的内部机制
查看volley源码可以知道:Volley一般联网的方式有HttpClient和HttpURLConnection,
如果API低于9的话可以调用HttpRULConnection请求网络,如果API高于9的话,会用HttpClient
请求网络,源码如下:
Java代码
HttpStackstack;
...
//Ifthedeviceisrunningaversion>=Gingerbread...
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
//...useHttpURLConnectionforstack.
}else{
//...useAndroidHttpClientforstack.
}
Networknetwork=newBasicNetwork(stack);
如果你的项目要进行频繁的网路请求,你可以在application的onCreate方法中创建一个RequestQueue,google官方文档强烈建议我们把RequestQueue设计成单列模式。
但是要注意,在RequestQueue中的Context对象为applicationContext,而不是Activity对应的context对象,可以避免在activity中每次都去消费使用activity中的context对象。
下面是使用单列设计模式创建的一个请求队列
Java代码
privatestaticMySingletonmInstance;
privateRequestQueuemRequestQueue;
privatestaticContextmCtx;
publicstaticsynchronizedMySingletongetInstance(Contextcontext){
if(mInstance==null){
mInstance=newMySingleton(context);
}
returnmInstance;
}
publicRequestQueuegetRequestQueue(){
if(mRequestQueue==null){
//getApplicationContext()iskey,itkeepsyoufromleakingthe
//ActivityorBroadcastReceiverifsomeonepassesonein.
mRequestQueue=Volley.newRequestQueue(mCtx.getApplicationContext());
}
returnmRequestQueue;
}
public<T>voidaddToRequestQueue(Request<T>req){
getRequestQueue().add(req);
}
publicImageLoadergetImageLoader(){
returnmImageLoader;
}
}
-----------------------------------------------------------------
在实际开发中可能并不那么实用,下面来总结我们项目中是如何封装使用的。
1>Volley是用来请求网络数据的,首先我们要准备好要传递的URL参数
Java代码
HashMap<String,String>params=newHashMap<String,String>();
params.put("user_id",userId);
params.put("count",count+"");
params.put("since_time",timeSince);
params.put("profile_user_id",profileUserId);
Stringurl=HttpConstant.DOMAIN_NORMAL+HttpConstant.FRIENDS_FANS_1;
2>
把URL拼凑成一个完整的链接
Java代码
publicvoidget(Stringurl,Map<String,String>data,Stringtag,finalCallbackLightRequest<LightResponse>callback){
if(!ConfigRequests.isNetworkOkWithAlert(mContext)){
callNetworkError(tag,url,data,callback);
return;
}
Map<String,String>defaultParams=ConfigRequests.getInstance().getDefaultParams();
if(defaultParams!=null&&defaultParams.size()>0){
data.putAll(defaultParams);
}
if(data!=null&&data.size()>0){
url=url+"?"+RequestUtils.map2QueryString(data);
}
this._get(url,tag,callback);
}
拼凑URl的方法:map2QueryString
Java代码
publicclassRequestUtils{
/**
*生成QueryString,以a=1&b=2形式返回
*/
publicstaticStringmap2QueryString(Map<String,String>map){
StringBuildersb=newStringBuilder();
Stringvalue;
try{
if(map!=null&&map.size()>0){
for(Entry<String,String>entry:map.entrySet()){
value="";
value=entry.getValue();
if(StringUtils.isEmpty(value)){
value="";
}else{
value=URLEncoder.encode(value,Models.Encoding.UTF8);
}
sb.append(entry.getKey()).append("=").append(value)
.append("&");
}
sb.deleteCharAt(sb.length()-1);
}
}catch(UnsupportedEncodingExceptione){
e.printStackTrace();
}
returnsb.toString();
}
3>发送请求,并将请求放进Requestqueue中,我把请求统一封装在一个
AppRequest类中,这个类如下:
Java代码
privatevoid_get(finalStringurl,finalStringtag,finalCallbackcallback){
LogUtils.e(TAG,url);
StringRequestreq=newStringRequest(Request.Method.GET,url,
newListener<String>(){
@Override
publicvoidonResponse(Stringtext){
LogUtils.i(TAG,text);
if(callback!=null){
LightResponseres=newLightResponse();
res.tag=tag;
res.url=url;
res.status=Models.StatusCode.NetworkSuccess;
res.setData(text);
callback.onFinish(res);
}
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
if(callback!=null){
LightResponseres=newLightResponse();
res.tag=tag;
res.url=url;
res.status=Models.StatusCode.NetworkError;
res.message=error.getMessage();
callback.onFinish(res);
}
}
}){
@Override
publicMap<String,String>getHeaders()throwsAuthFailureError{
/*此处是相对于实际业务配置的*/
returnnewHashMap<String,String>();
}
};
mQueue.add(req);
}
说明:Volley有2种回调一种是onResponse,一种是onErrorResponse,但是实际开发中可能需要更多的回调需要处理,例如:
1请求服务器成功,数据无误
2请求服务器成功,但是可能返回的JSON数据有问题
3服务器请求成功,但是返回码有问题
4无网络连接
5网络正常,但是服务器没返给我们数据(刷新失败)
6系统发生异常
......
当然我们可以根据实际业务需求来设定相应的回调。
上面我只做了一个CallBack来回调,不管请求失败还是成功,我都调用了onFinish()方法
4>回调处理
Java代码
try{
request.get(url,params,"",newCallbackLightRequest<LightResponse>(){
@Override
publicvoidcall(LightResponseresponse){
if(response.isReplied()){
ApiBeans.Followers2bean=GsonUtil.getFromStr(ApiBeans.Followers2.class,response.getText());//json解析
if(bean!=null&&bean.data!=null){
httpCallback.onResponse(bean.data);//请求成功
}else{
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.parse),null);//解析异常
}
}else{
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.network),null);//网络异常
}
}
});
}catch(Exceptionex){
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.exception),ex);//异常处理
}
这里我做了2种类型的回调:
一成功
二失败(包含4种类型)
回调的接口如下:
Java代码
publicinterfaceHttpCallback<T>{
voidonResponse(Tresponse);//成功回调
voidonFailure(RequestFailurefailure,Exceptionex);//异常处理
}
错误的回调类型,这里当然也可以封装成一个泛型
Java代码
publicclassRequestFailure{
publicenumType{
network,//网络问题
responseCode,//错误的返回码处理
exception,//系统异常
parse,//解析
}
publicRequestFailure(Typetype){
this.what=type;
}
publicTypewhat;
}
network:代表网络连接异常
responseCode:错误的响应码处理
exception:系统异常处理
parse:Json解析异常
5>再次封装
当上面的代码写多了之后,你会发现你一直在写重复的代码,仔细看看除了回调的对象不一样之外,其它都参数都基本一样,很容易就想到用泛型来对上面的代码进行封装,代码如下:
Java代码
publicstaticvoidgetFolowFansList(finalContextcontext,finalLightRequestrequest,StringuserId,StringprofileUserId,
StringtimeSince,intcount,finalHttpCallback<ApiBeans.FollowerListObject<Fan>>httpCallback){
HashMap<String,String>params=newHashMap<String,String>();
params.put("user_id",userId);
params.put("count",count+"");
params.put("since_time",timeSince);
params.put("profile_user_id",profileUserId);
Stringurl=HttpConstant.DOMAIN_NORMAL+HttpConstant.FRIENDS_FANS_1;
doRequest(request,url,data,httpCallback,ApiBeans.FollowerListObject.class);
}
把回调的代码用泛型进行封装
Java代码
privatestatic<T>voiddoRequest(LightRequestrequest,
Stringurl,HashMap<String,String>params,
finalHttpCallbackcallback,finalClass<?>cls){
try{
request.get(url,params,"",newCallbackLightRequest<LightResponse>(){
@Override
publicvoidcall(LightResponseresponse){
if(response.isReplied()){
Tbean=GsonUtil.getFromStr(cls,response.getText());
if(bean!=null){
callback.onResponse(bean);
}else{
callback.onFailure(newRequestFailure(RequestFailure.Type.parse),null);
}
}else{
callback.onFailure(newRequestFailure(RequestFailure.Type.network),null);
}
}
});
}catch(Exceptionex){
callback.onFailure(newRequestFailure(RequestFailure.Type.exception),null);
}
}
6>
volley在项目中进行业务处理
下面看看如何现在封装的Volley在项目中进行业务处理的?
Java代码
model.getFasList(mContext,lightRequest,Global.userDetail.userId,profileUserId,thisTimeSince,count,
newHttpCallback<ApiBeans.FollowerListObject<Fan>>(){
@Override
publicvoidonResponse(ApiBeans.FollowerListObject<Fan>data){
try{
//数据返回成功
if(data.userList!=null){
adapter.setIsAllLoaded(data.infoCount<Constant.FEED_DEFAULT_NUM);
lv.setIfHasMore(data.infoCount,Constant.FEED_DEFAULT_NUM);
if(isRefresh){
adapter.setDataList(data.userList);
lv.setAdapter(adapter);
}else{
adapter.addDataList(data.userList);
adapter.notifyDataSetChanged();
}
}else{
//刷新失败
if(isRefresh){
ToastModel.showRefreshFail(mContext);
}else{
ToastModel.showLoadMoreError(mContext);
}
}
}catch(Exceptione){
}finally{
isDownloading=false;
if(loadingView!=null){
loadingView.setVisibility(View.GONE);
loadingView=null;
}
refreshEnd();
}
}
@Override
publicvoidonFailure(RequestFailurefailure,Exceptionex){
//网络异常
if(failure.what==RequestFailure.Type.network){
ToastModel.showRed(mContext,R.string.tip_network_error);
}elseif(failure.what==RequestFailure.Type.parse){
//解析失败
ToastModel.showRed(mContext,"解析失败");
}elseif(failure.what==RequestFailure.Type.exception){
//系统异常
ToastModel.showRed(mContext,"系统异常");
}
}
});
}
代码方便简单,通熟易懂,层次分明,逻辑清晰有没有呢?Volley不仅简化了我们的开发流程,而且可以让我们更专注于我们的业务处理。这是volley非常大的一个优点。
-----------------------------------------------------------------
使用Volery已经快整整一年了,下面我来总结一下,我使用Volley时踩到的坑
(一)
Volley的二次封装
下面看看我是怎么对Volley的二次封装的:
Java代码
protected<T>voiddoSimpleRequest(Stringurl,HashMap<String,String>params,finalClass<?>clazz,finalSimpleCallbackcallback){
StringrequestUrl=urlBuilder(url,params);
Logger.e("url",requestUrl);
Response.Listener<String>successListener=newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
try{
Tbean=GsonUtils.getFromStr(clazz,response);
callback.onResponse(bean);
}catch(Exceptione){
callback.onError();
}
}
};
Response.ErrorListenererrorListener=newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
callback.onError();
}
};
StringRequestrequestRequest=newStringRequest(url,params,successListener,errorListener);
LeSportsApplication.getApplication().addHttpRequest(requestRequest);
}
请求的时候使用的StringRequest,请求成功后,会返回一个String类型,然后用Gson解析成JavaBean,我之前一直都这么使用,看似天衣无缝,其实你会发现JSon解析是在主线程中实现的,如果数据量大的话,很容易导致UI卡顿。优化方案就是在数据解析在子线程中进行
(1)新建一个GsonRequest请求类,继承Request对象
(2)重写parseNetworkResponse方法,这个方法其实是在CacheDispatch这个子线程中执行的
Java代码
<spanstyle="font-size:18px;">packagecom.lesports.common.volley.toolbox;
importandroid.os.Looper;
importcom.google.gson.Gson;
importcom.google.gson.JsonSyntaxException;
importcom.lesports.common.volley.NetworkResponse;
importcom.lesports.common.volley.ParseError;
importcom.lesports.common.volley.Request;
importcom.lesports.common.volley.Response;
importcom.letv.core.log.Logger;
importorg.json.JSONException;
importorg.json.JSONObject;
importjava.io.UnsupportedEncodingException;
importjava.util.HashMap;
/**
*Createdbyliuyu8on2015/9/15.
*/
publicclassMyGsonRequest<T>extendsRequest<T>{
privatefinalLoggermLogger=newLogger("GsonRequest");
privatefinalGsongson=newGson();
privatefinalClass<T>clazz;
privateResponse.Listener<T>listener;
/**
*MakeaGETrequestandreturnaparsedobjectfromJSON.Assumes
*{@linkRequest.Method#GET}.
*
*@paramurl
*URLoftherequesttomake
*@paramclazz
*Relevantclassobject,forGson'sreflection
*/
publicMyGsonRequest(Stringurl,HashMap<String,String>params,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(Request.Method.GET,url,params,errorListener);
this.clazz=clazz;
this.listener=listener;
}
/**
*MakeaGETrequestandreturnaparsedobjectfromJSON.Assumes
*{@linkRequest.Method#GET}.
*
*@paramurl
*URLoftherequesttomake
*@paramclazz
*Relevantclassobject,forGson'sreflection
*/
publicMyGsonRequest(Stringurl,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(Request.Method.GET,url,errorListener);
this.clazz=clazz;
this.listener=listener;
}
/**
*Liketheother,butallowsyoutospecifywhich{@linkRequest.Method}youwant.
*
*@parammethod
*@paramurl
*@paramclazz
*@paramlistener
*@paramerrorListener
*/
publicMyGsonRequest(intmethod,Stringurl,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(method,url,errorListener);
this.clazz=clazz;
this.listener=listener;
}
@Override
protectedvoiddeliverResponse(Tresponse){
if(listener!=null){
listener.onResponse(response);
}
}
@Override
protectedResponse<T>parseNetworkResponse(NetworkResponseresponse){
try{
if(Looper.myLooper()==Looper.getMainLooper()){
mLogger.e("数据是==>在主线程中解析的~");
}else{
mLogger.e("数据不是==>在主线程中解析的~");
}
Stringjson=newString(response.data,HttpHeaderParser.parseCharset(response.headers));
JSONObjectjsonObject=newJSONObject(json);
if(null!=jsonObject&&jsonObject.has("code")&&jsonObject.getInt("code")==200){
returnResponse.success(gson.fromJson(json,clazz),HttpHeaderParser.parseCacheHeaders(response));
}else{
returnResponse.error(newParseError());
}
}catch(UnsupportedEncodingExceptione){
returnResponse.error(newParseError(e));
}catch(JsonSyntaxExceptione){
mLogger.e("JsonSyntaxException====");
returnResponse.error(newParseError(e));
}catch(JSONExceptione){
returnResponse.error(newParseError(e));
}
}
@Override
publicvoidfinish(finalStringtag){
super.finish(tag);
listener=null;
}
}</span>
(3)然后进行二次封装
Java代码
/**
*优化:
*(1)避免在主线程中解析json数据
*(2)添加了取消请求方法
*
*@paramtag
*@paramurl
*@paramparams
*@paramclazz
*@paramcallback
*@param<T>
*/
protected<T>voiddoRequest(Stringtag,Stringurl,HashMap<String,String>params,finalClass<?>clazz,finalHttpCallbackcallback){
StringrequestUrl=urlBuilder(url,params);
Logger.e("BaseTVApi",requestUrl);
callback.onLoading();
Response.Listener<T>successListener=newResponse.Listener<T>(){
@Override
publicvoidonResponse(Tbean){
if(bean!=null){
callback.onResponse(bean);
Logger.e("BaseTVApi","request-->onResponse");
}else{
callback.onEmptyData();
Logger.e("BaseTVApi","request-->onEmptyData");
}
}
};
Response.ErrorListenererrorListener=newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
callback.onError();
Logger.e("BaseTVApi","异常信息-->"+error.getMessage());
}
};
GsonRequestrequestRequest=newGsonRequest(url,params,clazz,successListener,errorListener);
requestRequest.setTag(tag);
LeSportsApplication.getApplication().addHttpRequest(requestRequest);
}
打印log你会发现,你会发现数据解析是在子线程中执行的。
(1)在Application中提供一个取消请求的方法,因为一般请求队列是在Application中初始化的。
Java代码
/**
*网络请求优化,取消请求
*@paramtag
*/
publicvoidcancelRequest(Stringtag){
try{
mRequestQueue.cancelAll(tag);
}catch(Exceptione){
Logger.e("LeSportsApplication","tag=="+tag+"的请求取消失败");
}
}
(2)在onStop中调用cancelRequest方法,最终会调用request的finish方法
(3)在GsonRequest中重写finish方法,并在该方法中把listener置为空
Java代码
<spanstyle="font-size:18px;">@Override
publicvoidfinish(finalStringtag){
super.finish(tag);
listener=null;
}</span>
(4)在再看看GsonRequest
的父类Request的finish方法,我已经把mErrorListener干掉了。
Java代码
publicvoidfinish(finalStringtag){
if(mRequestQueue!=null){
mRequestQueue.finish(this);
}
if(MarkerLog.ENABLED){
finallongthreadId=Thread.currentThread().getId();
if(Looper.myLooper()!=Looper.getMainLooper()){
//Ifwefinishmarkingoffofthemainthread,weneedto
//actuallydoitonthemainthreadtoensurecorrectordering.
HandlermainThread=newHandler(Looper.getMainLooper());
mainThread.post(newRunnable(){
@Override
publicvoidrun(){
mEventLog.add(tag,threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag,threadId);
mEventLog.finish(this.toString());
}else{
longrequestTime=SystemClock.elapsedRealtime()-mRequestBirthTime;
if(requestTime>=SLOW_REQUEST_THRESHOLD_MS){
VolleyLog.d("%dms:%s",requestTime,this.toString());
}
}
mErrorListener=null;
}
总结:请求取消最终都会调用Request的finish方法,我们只要在这个方法中,把2个回调给干掉,问题就解决了。
原因是:我第一次进入应用的时候,立马弹出了一个升级对话框,这个时候刚好触发了onStop方法,自然请求就取消掉了,所以就一直在loading呗!后来我就把该页面的请求放在onDestroy中cancel。
Java代码
<spanstyle="font-size:18px;">privatevoidrequestUserSubscribesGame(){
uid=LoginUtils.getUid();
if(StringUtils.isStringEmpty(uid)){
return;
}
UserTVApi.getInstance().getUserSubscribeGameId(TAG,uid,newSimpleCallback<ApiBeans.SubScribeListModel>(){
@Override
publicvoidonResponse(ApiBeans.SubScribeListModelbean){
SubScribeModelsubScribeModel=bean.data;
if(subScribeModel!=null){
scribeGameEntrys=subScribeModel.getEntities();
requestHallData();
}
}
@Override
publicvoidonError(){
Logger.e(TAG,"获取订阅信息失败...");
}
});
}</span>
我之前也很喜欢这样写,后来我查看log的时候发现,第一个请求会超时请求,导致会重试一次。我设置的超时时间是2秒,可能是由于第二个请求太慢,导致请求第一次回调的时候太长导致的吧,具体原因还需要去查。
解决方法:有嵌套请求的情况,建议第一个接口请求完后,用handler发送消息,然后第二个开始请求。
-------------------------------------------------------------------
>>AndroidVolleyHttps证书不信任的解决方案:(单向认证)
首先新建一个类FakeX509TrustManager:
publicclassFakeX509TrustManagerimplementsX509TrustManager{
privatestaticTrustManager[]trustManagers;
privatestaticfinalX509Certificate[]_AcceptedIssuers=new
X509Certificate[]{};
@Override
publicvoidcheckClientTrusted(java.security.cert.X509Certificate[]x509Certificates,Strings)throwsjava.security.cert.CertificateException{
//TochangebodyofimplementedmethodsuseFile|Settings|FileTemplates.
}
@Override
publicvoidcheckServerTrusted(java.security.cert.X509Certificate[]x509Certificates,Strings)throwsjava.security.cert.CertificateException{
//TochangebodyofimplementedmethodsuseFile|Settings|FileTemplates.
}
publicbooleanisClientTrusted(X509Certificate[]chain){
returntrue;
}
publicbooleanisServerTrusted(X509Certificate[]chain){
returntrue;
}
@Override
publicX509Certificate[]getAcceptedIssuers(){
return_AcceptedIssuers;
}
publicstaticvoidallowAllSSL(){
HttpsURLConnection.setDefaultHostnameVerifier(newHostnameVerifier(){
@Override
publicbooleanverify(Stringarg0,SSLSessionarg1){
//TODOAuto-generatedmethodstub
returntrue;
}
});
SSLContextcontext=null;
if(trustManagers==null){
trustManagers=newTrustManager[]{newFakeX509TrustManager()};
}
try{
context=SSLContext.getInstance("TLS");
context.init(null,trustManagers,newSecureRandom());
}catch(NoSuchAlgorithmExceptione){
e.printStackTrace();
}catch(KeyManagementExceptione){
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
}
}
然后在进行StringRequest之前设置:
FakeX509TrustManager.allowAllSSL();
mStringRequest=newStringRequest(Request.Method.POST,
url,
getDefaultSuccessListener(),
mErrorListener){
@Override
protectedMap<String,String>getParams()throwsAuthFailureError{
returnparams;
}
};
mRequestQueue.add(mStringRequest);
1
>Volley的介绍
Volley从服务器端请求字符串数据,图片数据,和JSON格式数据。Volley可以写出标准化的模板代码,并让我们更加关注于我们的业务逻辑,这对于我们的App来说是非常重要的。
Volley的优势:
自动的调度网络请求
多并发的网络请求
可以缓存http请求
支持请求的优先级
支持取消请求的API,可以取消单个请求,可以设置取消请求的范围域。
代码标准化,使开发者更容易专注于我们的业务的逻辑处理
更容易给UI填充来自网络请求的数据
Volley可以是作为调试和跟踪的工具
Volley非常不适合大的文件流操作,例如上传和下载。因为Volley会把所有的服务器端返回的数据在解析期间缓存进内存。大量的下载操作可以考虑用DownLoaderManager和异步加载来实现。
Volley库的地址如下:
gitclone
2>Volley的工作原理
为了发送一个请求,你可以通过构造方法new出来一个Request,然后调用add()把请求添加进RequestQueue中,当调用add方法时,Volley会运行一个缓存处理线程和一个网络调度线程池.如果请求被缓存线程已经缓存的话,请求将不会放进请求队列,也就是说不会发出Http请求。而会直接复用请求,并将数据返回到主线程。如果缓存线程中没有缓存到请求的话,请求将会放进请求队列,网络请求成功后,请求将会被缓存进cache,接着网络调度线程将处理该请求,并解析数据。Volley的工作原理图如下:
3>使用volley请求数据
3.1利用StringRequest请求字符串数据
Volley提供了一个便利的方法Volley.newRequestQueue可以使用默认的设置创建一个RequestQueue,例如:Java代码
finalTextViewmTextView=(TextView)findViewById(R.id.text);
...
//InstantiatetheRequestQueue.
RequestQueuequeue=Volley.newRequestQueue(this);
Stringurl="http://www.google.com";
//RequestastringresponsefromtheprovidedURL.
StringRequeststringRequest=newStringRequest(Request.Method.GET,url,
newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
//Displaythefirst500charactersoftheresponsestring.
mTextView.setText("Responseis:"+response.substring(0,500));
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
mTextView.setText("Thatdidn'twork!");
}
});
//AddtherequesttotheRequestQueue.
queue.add(stringRequest);
3.2利用ImageRequest请求图片
Java代码
publicclassMainActivityextendsActivity{
privateImageViewmImageView;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView=(ImageView)findViewById(R.id.iv_image);
}
publicvoidrequestImage(){
//RetrievesanimagespecifiedbytheURL,displaysitintheUI.
Stringurl="http://i.imgur.com/7spzG.png";
ImageRequestrequest=newImageRequest(url,
newResponse.Listener<Bitmap>(){
@Override
publicvoidonResponse(Bitmapbitmap){
Toast.makeText(MainActivity.this,"success",1).show();
mImageView.setImageBitmap(bitmap);
}
},0,0,null,newResponse.ErrorListener(){
publicvoidonErrorResponse(VolleyErrorerror){
/*mImageView
.setImageResource(R.drawable.image_load_error);*/
}
});
Volley.newRequestQueue(getApplicationContext()).add(request);
}
}
[b]3.3使用NetworkImageView结合imageLoader加载网络图片[/b]
Java代码
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/networkImageView"
android:layout_width="150dp"
android:layout_height="170dp"
android:layout_centerHorizontal="true"/>
Java代码
ImageLoadermImageLoader;
ImageViewmImageView;
//TheURLfortheimagethatisbeingloaded.
privatestaticfinalStringIMAGE_URL=
"http://developer.android.com/images/training/system-ui.png";
...
mImageView=(ImageView)findViewById(R.id.regularImageView);
//GettheImageLoaderthroughyoursingletonclass.
mImageLoader=MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL,ImageLoader.getImageListener(mImageView,R.drawable.def_image,R.drawable.err_image));
Java代码
TextViewmTxtDisplay;
ImageViewmImageView;
mTxtDisplay=(TextView)findViewById(R.id.txtDisplay);
Stringurl="http://my-json-feed";
JsonObjectRequestjsObjRequest=newJsonObjectRequest
(Request.Method.GET,url,null,newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
mTxtDisplay.setText("Response:"+response.toString());
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//TODOAuto-generatedmethodstub
}
});
//AccesstheRequestQueuethroughyoursingletonclass.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
3.4使用JsonObjectRequest请求json格式数据(个人感觉和StringRequest一样)
Java代码TextViewmTxtDisplay;
ImageViewmImageView;
mTxtDisplay=(TextView)findViewById(R.id.txtDisplay);
Stringurl="http://my-json-feed";
JsonObjectRequestjsObjRequest=newJsonObjectRequest
(Request.Method.GET,url,null,newResponse.Listener<JSONObject>(){
@Override
publicvoidonResponse(JSONObjectresponse){
mTxtDisplay.setText("Response:"+response.toString());
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//TODOAuto-generatedmethodstub
}
});
//AccesstheRequestQueuethroughyoursingletonclass.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
4>如何取消请求
Volley通过调用cancel()方法取消一个请求,并保证请求不会在Response中回调处理,你可以在activity中的onStop中取消所有你想取消的请求,最简单的方法就是给所有想取消的请求调用setTag(TAG)设置标记,然后放进请求队列,在Activity的onStop方法中调用cancelAll(TAG)方法取消请求。代码如下:
给请求设置TAG,并放进请求队列
Java代码
publicstaticfinalStringTAG="MyTag";
StringRequeststringRequest;//Assumethisexists.
RequestQueuemRequestQueue;//Assumethisexists.
//给请求设置tag
stringRequest.setTag(TAG);
//把请求放进请求队列
mRequestQueue.add(stringRequest);
在onstop方法中取消所有设置Tag的请求
Java代码
@Override
protectedvoidonStop(){
super.onStop();
if(mRequestQueue!=null){
mRequestQueue.cancelAll(TAG);
}
}
注意:当取消请求的时候,onResponse和onErrorResponse方法将不会执行
5>如何新建一个RequestQueue
5.1RequestQueue的配置
Volley.newRequestQueue可以创建一个请求队列,但那只能使用Volley的默认配置,下面来讲讲如何来创建自己的一个请求队列。每次去new一个请求队列是非常消耗内存的,我们可以利用单列模式来避免每次都去new一个请求队列。
Java代码
RequestQueuemRequestQueue;
//配置缓存大小
Cachecache=newDiskBasedCache(getCacheDir(),1024*1024);//1MBcap
//设置网络请求方式(UrlConnection和HttpClient)
Networknetwork=newBasicNetwork(newHurlStack());
//初始化RequestQueue
mRequestQueue=newRequestQueue(cache,network);
//开启RequestQueue
mRequestQueue.start();
Stringurl="http://www.myurl.com";
//Formulatetherequestandhandletheresponse.
StringRequeststringRequest=newStringRequest(Request.Method.GET,url,
newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
//Dosomethingwiththeresponse
}
},
newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
//Handleerror
}
});
//把请求添加进请求队列
mRequestQueue.add(stringRequest);
5.2RequestQueue中BasicNewWork(网络)的内部机制
查看volley源码可以知道:Volley一般联网的方式有HttpClient和HttpURLConnection,
如果API低于9的话可以调用HttpRULConnection请求网络,如果API高于9的话,会用HttpClient
请求网络,源码如下:
Java代码
HttpStackstack;
...
//Ifthedeviceisrunningaversion>=Gingerbread...
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
//...useHttpURLConnectionforstack.
}else{
//...useAndroidHttpClientforstack.
}
Networknetwork=newBasicNetwork(stack);
5.3使用requestQueue的注意事项
如果你的项目要进行频繁的网路请求,你可以在application的onCreate方法中创建一个RequestQueue,google官方文档强烈建议我们把RequestQueue设计成单列模式。但是要注意,在RequestQueue中的Context对象为applicationContext,而不是Activity对应的context对象,可以避免在activity中每次都去消费使用activity中的context对象。
下面是使用单列设计模式创建的一个请求队列
Java代码
privatestaticMySingletonmInstance;
privateRequestQueuemRequestQueue;
privatestaticContextmCtx;
publicstaticsynchronizedMySingletongetInstance(Contextcontext){
if(mInstance==null){
mInstance=newMySingleton(context);
}
returnmInstance;
}
publicRequestQueuegetRequestQueue(){
if(mRequestQueue==null){
//getApplicationContext()iskey,itkeepsyoufromleakingthe
//ActivityorBroadcastReceiverifsomeonepassesonein.
mRequestQueue=Volley.newRequestQueue(mCtx.getApplicationContext());
}
returnmRequestQueue;
}
public<T>voidaddToRequestQueue(Request<T>req){
getRequestQueue().add(req);
}
publicImageLoadergetImageLoader(){
returnmImageLoader;
}
}
-----------------------------------------------------------------
在实际开发中可能并不那么实用,下面来总结我们项目中是如何封装使用的。
1>Volley是用来请求网络数据的,首先我们要准备好要传递的URL参数
Java代码
HashMap<String,String>params=newHashMap<String,String>();
params.put("user_id",userId);
params.put("count",count+"");
params.put("since_time",timeSince);
params.put("profile_user_id",profileUserId);
Stringurl=HttpConstant.DOMAIN_NORMAL+HttpConstant.FRIENDS_FANS_1;
2>
把URL拼凑成一个完整的链接
Java代码
publicvoidget(Stringurl,Map<String,String>data,Stringtag,finalCallbackLightRequest<LightResponse>callback){
if(!ConfigRequests.isNetworkOkWithAlert(mContext)){
callNetworkError(tag,url,data,callback);
return;
}
Map<String,String>defaultParams=ConfigRequests.getInstance().getDefaultParams();
if(defaultParams!=null&&defaultParams.size()>0){
data.putAll(defaultParams);
}
if(data!=null&&data.size()>0){
url=url+"?"+RequestUtils.map2QueryString(data);
}
this._get(url,tag,callback);
}
拼凑URl的方法:map2QueryString
Java代码
publicclassRequestUtils{
/**
*生成QueryString,以a=1&b=2形式返回
*/
publicstaticStringmap2QueryString(Map<String,String>map){
StringBuildersb=newStringBuilder();
Stringvalue;
try{
if(map!=null&&map.size()>0){
for(Entry<String,String>entry:map.entrySet()){
value="";
value=entry.getValue();
if(StringUtils.isEmpty(value)){
value="";
}else{
value=URLEncoder.encode(value,Models.Encoding.UTF8);
}
sb.append(entry.getKey()).append("=").append(value)
.append("&");
}
sb.deleteCharAt(sb.length()-1);
}
}catch(UnsupportedEncodingExceptione){
e.printStackTrace();
}
returnsb.toString();
}
3>发送请求,并将请求放进Requestqueue中,我把请求统一封装在一个
AppRequest类中,这个类如下:
Java代码
privatevoid_get(finalStringurl,finalStringtag,finalCallbackcallback){
LogUtils.e(TAG,url);
StringRequestreq=newStringRequest(Request.Method.GET,url,
newListener<String>(){
@Override
publicvoidonResponse(Stringtext){
LogUtils.i(TAG,text);
if(callback!=null){
LightResponseres=newLightResponse();
res.tag=tag;
res.url=url;
res.status=Models.StatusCode.NetworkSuccess;
res.setData(text);
callback.onFinish(res);
}
}
},newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
if(callback!=null){
LightResponseres=newLightResponse();
res.tag=tag;
res.url=url;
res.status=Models.StatusCode.NetworkError;
res.message=error.getMessage();
callback.onFinish(res);
}
}
}){
@Override
publicMap<String,String>getHeaders()throwsAuthFailureError{
/*此处是相对于实际业务配置的*/
returnnewHashMap<String,String>();
}
};
mQueue.add(req);
}
说明:Volley有2种回调一种是onResponse,一种是onErrorResponse,但是实际开发中可能需要更多的回调需要处理,例如:
1请求服务器成功,数据无误
2请求服务器成功,但是可能返回的JSON数据有问题
3服务器请求成功,但是返回码有问题
4无网络连接
5网络正常,但是服务器没返给我们数据(刷新失败)
6系统发生异常
......
当然我们可以根据实际业务需求来设定相应的回调。
上面我只做了一个CallBack来回调,不管请求失败还是成功,我都调用了onFinish()方法
4>回调处理
Java代码
try{
request.get(url,params,"",newCallbackLightRequest<LightResponse>(){
@Override
publicvoidcall(LightResponseresponse){
if(response.isReplied()){
ApiBeans.Followers2bean=GsonUtil.getFromStr(ApiBeans.Followers2.class,response.getText());//json解析
if(bean!=null&&bean.data!=null){
httpCallback.onResponse(bean.data);//请求成功
}else{
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.parse),null);//解析异常
}
}else{
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.network),null);//网络异常
}
}
});
}catch(Exceptionex){
httpCallback.onFailure(newRequestFailure(RequestFailure.Type.exception),ex);//异常处理
}
这里我做了2种类型的回调:
一成功
二失败(包含4种类型)
回调的接口如下:
Java代码
publicinterfaceHttpCallback<T>{
voidonResponse(Tresponse);//成功回调
voidonFailure(RequestFailurefailure,Exceptionex);//异常处理
}
错误的回调类型,这里当然也可以封装成一个泛型
Java代码
publicclassRequestFailure{
publicenumType{
network,//网络问题
responseCode,//错误的返回码处理
exception,//系统异常
parse,//解析
}
publicRequestFailure(Typetype){
this.what=type;
}
publicTypewhat;
}
network:代表网络连接异常
responseCode:错误的响应码处理
exception:系统异常处理
parse:Json解析异常
5>再次封装
当上面的代码写多了之后,你会发现你一直在写重复的代码,仔细看看除了回调的对象不一样之外,其它都参数都基本一样,很容易就想到用泛型来对上面的代码进行封装,代码如下:
Java代码
publicstaticvoidgetFolowFansList(finalContextcontext,finalLightRequestrequest,StringuserId,StringprofileUserId,
StringtimeSince,intcount,finalHttpCallback<ApiBeans.FollowerListObject<Fan>>httpCallback){
HashMap<String,String>params=newHashMap<String,String>();
params.put("user_id",userId);
params.put("count",count+"");
params.put("since_time",timeSince);
params.put("profile_user_id",profileUserId);
Stringurl=HttpConstant.DOMAIN_NORMAL+HttpConstant.FRIENDS_FANS_1;
doRequest(request,url,data,httpCallback,ApiBeans.FollowerListObject.class);
}
把回调的代码用泛型进行封装
Java代码
privatestatic<T>voiddoRequest(LightRequestrequest,
Stringurl,HashMap<String,String>params,
finalHttpCallbackcallback,finalClass<?>cls){
try{
request.get(url,params,"",newCallbackLightRequest<LightResponse>(){
@Override
publicvoidcall(LightResponseresponse){
if(response.isReplied()){
Tbean=GsonUtil.getFromStr(cls,response.getText());
if(bean!=null){
callback.onResponse(bean);
}else{
callback.onFailure(newRequestFailure(RequestFailure.Type.parse),null);
}
}else{
callback.onFailure(newRequestFailure(RequestFailure.Type.network),null);
}
}
});
}catch(Exceptionex){
callback.onFailure(newRequestFailure(RequestFailure.Type.exception),null);
}
}
6>
volley在项目中进行业务处理
下面看看如何现在封装的Volley在项目中进行业务处理的?
Java代码
model.getFasList(mContext,lightRequest,Global.userDetail.userId,profileUserId,thisTimeSince,count,
newHttpCallback<ApiBeans.FollowerListObject<Fan>>(){
@Override
publicvoidonResponse(ApiBeans.FollowerListObject<Fan>data){
try{
//数据返回成功
if(data.userList!=null){
adapter.setIsAllLoaded(data.infoCount<Constant.FEED_DEFAULT_NUM);
lv.setIfHasMore(data.infoCount,Constant.FEED_DEFAULT_NUM);
if(isRefresh){
adapter.setDataList(data.userList);
lv.setAdapter(adapter);
}else{
adapter.addDataList(data.userList);
adapter.notifyDataSetChanged();
}
}else{
//刷新失败
if(isRefresh){
ToastModel.showRefreshFail(mContext);
}else{
ToastModel.showLoadMoreError(mContext);
}
}
}catch(Exceptione){
}finally{
isDownloading=false;
if(loadingView!=null){
loadingView.setVisibility(View.GONE);
loadingView=null;
}
refreshEnd();
}
}
@Override
publicvoidonFailure(RequestFailurefailure,Exceptionex){
//网络异常
if(failure.what==RequestFailure.Type.network){
ToastModel.showRed(mContext,R.string.tip_network_error);
}elseif(failure.what==RequestFailure.Type.parse){
//解析失败
ToastModel.showRed(mContext,"解析失败");
}elseif(failure.what==RequestFailure.Type.exception){
//系统异常
ToastModel.showRed(mContext,"系统异常");
}
}
});
}
代码方便简单,通熟易懂,层次分明,逻辑清晰有没有呢?Volley不仅简化了我们的开发流程,而且可以让我们更专注于我们的业务处理。这是volley非常大的一个优点。
-----------------------------------------------------------------
使用Volery已经快整整一年了,下面我来总结一下,我使用Volley时踩到的坑
(一)
Volley的二次封装
下面看看我是怎么对Volley的二次封装的:
Java代码
protected<T>voiddoSimpleRequest(Stringurl,HashMap<String,String>params,finalClass<?>clazz,finalSimpleCallbackcallback){
StringrequestUrl=urlBuilder(url,params);
Logger.e("url",requestUrl);
Response.Listener<String>successListener=newResponse.Listener<String>(){
@Override
publicvoidonResponse(Stringresponse){
try{
Tbean=GsonUtils.getFromStr(clazz,response);
callback.onResponse(bean);
}catch(Exceptione){
callback.onError();
}
}
};
Response.ErrorListenererrorListener=newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
callback.onError();
}
};
StringRequestrequestRequest=newStringRequest(url,params,successListener,errorListener);
LeSportsApplication.getApplication().addHttpRequest(requestRequest);
}
请求的时候使用的StringRequest,请求成功后,会返回一个String类型,然后用Gson解析成JavaBean,我之前一直都这么使用,看似天衣无缝,其实你会发现JSon解析是在主线程中实现的,如果数据量大的话,很容易导致UI卡顿。优化方案就是在数据解析在子线程中进行
(1)新建一个GsonRequest请求类,继承Request对象
(2)重写parseNetworkResponse方法,这个方法其实是在CacheDispatch这个子线程中执行的
Java代码
<spanstyle="font-size:18px;">packagecom.lesports.common.volley.toolbox;
importandroid.os.Looper;
importcom.google.gson.Gson;
importcom.google.gson.JsonSyntaxException;
importcom.lesports.common.volley.NetworkResponse;
importcom.lesports.common.volley.ParseError;
importcom.lesports.common.volley.Request;
importcom.lesports.common.volley.Response;
importcom.letv.core.log.Logger;
importorg.json.JSONException;
importorg.json.JSONObject;
importjava.io.UnsupportedEncodingException;
importjava.util.HashMap;
/**
*Createdbyliuyu8on2015/9/15.
*/
publicclassMyGsonRequest<T>extendsRequest<T>{
privatefinalLoggermLogger=newLogger("GsonRequest");
privatefinalGsongson=newGson();
privatefinalClass<T>clazz;
privateResponse.Listener<T>listener;
/**
*MakeaGETrequestandreturnaparsedobjectfromJSON.Assumes
*{@linkRequest.Method#GET}.
*
*@paramurl
*URLoftherequesttomake
*@paramclazz
*Relevantclassobject,forGson'sreflection
*/
publicMyGsonRequest(Stringurl,HashMap<String,String>params,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(Request.Method.GET,url,params,errorListener);
this.clazz=clazz;
this.listener=listener;
}
/**
*MakeaGETrequestandreturnaparsedobjectfromJSON.Assumes
*{@linkRequest.Method#GET}.
*
*@paramurl
*URLoftherequesttomake
*@paramclazz
*Relevantclassobject,forGson'sreflection
*/
publicMyGsonRequest(Stringurl,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(Request.Method.GET,url,errorListener);
this.clazz=clazz;
this.listener=listener;
}
/**
*Liketheother,butallowsyoutospecifywhich{@linkRequest.Method}youwant.
*
*@parammethod
*@paramurl
*@paramclazz
*@paramlistener
*@paramerrorListener
*/
publicMyGsonRequest(intmethod,Stringurl,Class<T>clazz,Response.Listener<T>listener,Response.ErrorListenererrorListener){
super(method,url,errorListener);
this.clazz=clazz;
this.listener=listener;
}
@Override
protectedvoiddeliverResponse(Tresponse){
if(listener!=null){
listener.onResponse(response);
}
}
@Override
protectedResponse<T>parseNetworkResponse(NetworkResponseresponse){
try{
if(Looper.myLooper()==Looper.getMainLooper()){
mLogger.e("数据是==>在主线程中解析的~");
}else{
mLogger.e("数据不是==>在主线程中解析的~");
}
Stringjson=newString(response.data,HttpHeaderParser.parseCharset(response.headers));
JSONObjectjsonObject=newJSONObject(json);
if(null!=jsonObject&&jsonObject.has("code")&&jsonObject.getInt("code")==200){
returnResponse.success(gson.fromJson(json,clazz),HttpHeaderParser.parseCacheHeaders(response));
}else{
returnResponse.error(newParseError());
}
}catch(UnsupportedEncodingExceptione){
returnResponse.error(newParseError(e));
}catch(JsonSyntaxExceptione){
mLogger.e("JsonSyntaxException====");
returnResponse.error(newParseError(e));
}catch(JSONExceptione){
returnResponse.error(newParseError(e));
}
}
@Override
publicvoidfinish(finalStringtag){
super.finish(tag);
listener=null;
}
}</span>
(3)然后进行二次封装
Java代码
/**
*优化:
*(1)避免在主线程中解析json数据
*(2)添加了取消请求方法
*
*@paramtag
*@paramurl
*@paramparams
*@paramclazz
*@paramcallback
*@param<T>
*/
protected<T>voiddoRequest(Stringtag,Stringurl,HashMap<String,String>params,finalClass<?>clazz,finalHttpCallbackcallback){
StringrequestUrl=urlBuilder(url,params);
Logger.e("BaseTVApi",requestUrl);
callback.onLoading();
Response.Listener<T>successListener=newResponse.Listener<T>(){
@Override
publicvoidonResponse(Tbean){
if(bean!=null){
callback.onResponse(bean);
Logger.e("BaseTVApi","request-->onResponse");
}else{
callback.onEmptyData();
Logger.e("BaseTVApi","request-->onEmptyData");
}
}
};
Response.ErrorListenererrorListener=newResponse.ErrorListener(){
@Override
publicvoidonErrorResponse(VolleyErrorerror){
callback.onError();
Logger.e("BaseTVApi","异常信息-->"+error.getMessage());
}
};
GsonRequestrequestRequest=newGsonRequest(url,params,clazz,successListener,errorListener);
requestRequest.setTag(tag);
LeSportsApplication.getApplication().addHttpRequest(requestRequest);
}
打印log你会发现,你会发现数据解析是在子线程中执行的。
(二)使用Volley内存泄露
在用MAT做内存泄露检查的时候,发现由于Volley的回调没有干掉导致的泄露问题,解决方法就是保证在Activity退出时,cancel掉请求,Request方法有一个cancel和finish方法并在这2个方法中把listener置为空。(1)在Application中提供一个取消请求的方法,因为一般请求队列是在Application中初始化的。
Java代码
/**
*网络请求优化,取消请求
*@paramtag
*/
publicvoidcancelRequest(Stringtag){
try{
mRequestQueue.cancelAll(tag);
}catch(Exceptione){
Logger.e("LeSportsApplication","tag=="+tag+"的请求取消失败");
}
}
(2)在onStop中调用cancelRequest方法,最终会调用request的finish方法
(3)在GsonRequest中重写finish方法,并在该方法中把listener置为空
Java代码
<spanstyle="font-size:18px;">@Override
publicvoidfinish(finalStringtag){
super.finish(tag);
listener=null;
}</span>
(4)在再看看GsonRequest
的父类Request的finish方法,我已经把mErrorListener干掉了。
Java代码
publicvoidfinish(finalStringtag){
if(mRequestQueue!=null){
mRequestQueue.finish(this);
}
if(MarkerLog.ENABLED){
finallongthreadId=Thread.currentThread().getId();
if(Looper.myLooper()!=Looper.getMainLooper()){
//Ifwefinishmarkingoffofthemainthread,weneedto
//actuallydoitonthemainthreadtoensurecorrectordering.
HandlermainThread=newHandler(Looper.getMainLooper());
mainThread.post(newRunnable(){
@Override
publicvoidrun(){
mEventLog.add(tag,threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag,threadId);
mEventLog.finish(this.toString());
}else{
longrequestTime=SystemClock.elapsedRealtime()-mRequestBirthTime;
if(requestTime>=SLOW_REQUEST_THRESHOLD_MS){
VolleyLog.d("%dms:%s",requestTime,this.toString());
}
}
mErrorListener=null;
}
总结:请求取消最终都会调用Request的finish方法,我们只要在这个方法中,把2个回调给干掉,问题就解决了。
(三)在onStop中取消请求
QA妹子给我报了一个奇葩的bug,具体是第一次打开应用的时候APP一直在loading,找了半天发现原来是由于是取消请求不当引起的。原因是:我第一次进入应用的时候,立马弹出了一个升级对话框,这个时候刚好触发了onStop方法,自然请求就取消掉了,所以就一直在loading呗!后来我就把该页面的请求放在onDestroy中cancel。
(四)嵌套请求
一个页面中可能会有多个请求,有的请求要等到其它的请求完后才能进行。就像这样:Java代码
<spanstyle="font-size:18px;">privatevoidrequestUserSubscribesGame(){
uid=LoginUtils.getUid();
if(StringUtils.isStringEmpty(uid)){
return;
}
UserTVApi.getInstance().getUserSubscribeGameId(TAG,uid,newSimpleCallback<ApiBeans.SubScribeListModel>(){
@Override
publicvoidonResponse(ApiBeans.SubScribeListModelbean){
SubScribeModelsubScribeModel=bean.data;
if(subScribeModel!=null){
scribeGameEntrys=subScribeModel.getEntities();
requestHallData();
}
}
@Override
publicvoidonError(){
Logger.e(TAG,"获取订阅信息失败...");
}
});
}</span>
我之前也很喜欢这样写,后来我查看log的时候发现,第一个请求会超时请求,导致会重试一次。我设置的超时时间是2秒,可能是由于第二个请求太慢,导致请求第一次回调的时候太长导致的吧,具体原因还需要去查。
解决方法:有嵌套请求的情况,建议第一个接口请求完后,用handler发送消息,然后第二个开始请求。
-------------------------------------------------------------------
>>AndroidVolleyHttps证书不信任的解决方案:(单向认证)
首先新建一个类FakeX509TrustManager:
publicclassFakeX509TrustManagerimplementsX509TrustManager{
privatestaticTrustManager[]trustManagers;
privatestaticfinalX509Certificate[]_AcceptedIssuers=new
X509Certificate[]{};
@Override
publicvoidcheckClientTrusted(java.security.cert.X509Certificate[]x509Certificates,Strings)throwsjava.security.cert.CertificateException{
//TochangebodyofimplementedmethodsuseFile|Settings|FileTemplates.
}
@Override
publicvoidcheckServerTrusted(java.security.cert.X509Certificate[]x509Certificates,Strings)throwsjava.security.cert.CertificateException{
//TochangebodyofimplementedmethodsuseFile|Settings|FileTemplates.
}
publicbooleanisClientTrusted(X509Certificate[]chain){
returntrue;
}
publicbooleanisServerTrusted(X509Certificate[]chain){
returntrue;
}
@Override
publicX509Certificate[]getAcceptedIssuers(){
return_AcceptedIssuers;
}
publicstaticvoidallowAllSSL(){
HttpsURLConnection.setDefaultHostnameVerifier(newHostnameVerifier(){
@Override
publicbooleanverify(Stringarg0,SSLSessionarg1){
//TODOAuto-generatedmethodstub
returntrue;
}
});
SSLContextcontext=null;
if(trustManagers==null){
trustManagers=newTrustManager[]{newFakeX509TrustManager()};
}
try{
context=SSLContext.getInstance("TLS");
context.init(null,trustManagers,newSecureRandom());
}catch(NoSuchAlgorithmExceptione){
e.printStackTrace();
}catch(KeyManagementExceptione){
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
}
}
然后在进行StringRequest之前设置:
FakeX509TrustManager.allowAllSSL();
mStringRequest=newStringRequest(Request.Method.POST,
url,
getDefaultSuccessListener(),
mErrorListener){
@Override
protectedMap<String,String>getParams()throwsAuthFailureError{
returnparams;
}
};
mRequestQueue.add(mStringRequest);
相关文章推荐
- Golang 环境的安装
- django主键的使用
- Django session机制
- Algorithm: 移动盒子
- go text/templete模板
- 关于Goroutine的原理
- Django安装
- Go语言并发之美
- Goroutine(协程)为何能处理大并发?
- 不是闹鬼,是病毒!全球首个Golem(傀儡)病毒感染数万手机
- Django权限系统auth
- Yandex.Algorithm 2016 Online Round 2 题解(待补)
- 1003 Hangover
- windows上安装golang1.7的编译环境
- GoodView的使用(一个点赞的动画)
- DjangoWeb 开发相关资料收集
- Google类VR设备知识
- cgo -rpath指定动态库路径
- POJ 2762 Going from u to v or from v to u?【强连通Kosaraju+拓扑排序】
- 关于django开发遇到的问题