您的位置:首页 > 编程语言 > Go语言

使用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


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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: