前言
在项目中一般使用使用volley方式如下,用起来给人一种很乱的感觉,于是一种盘它的想法油然而生。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public void get() { String url = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=......" ; StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String s) { Toast.makeText(MainActivity. this ,s,Toast.LENGTH_SHORT).show(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { Toast.makeText(MainActivity. this ,volleyError.toString(),Toast.LENGTH_SHORT).show(); } }); request.setTag( "abcGet" ); MyApplication.getHttpQueues().add(request); } |
首先看一下我封装后的使用例子:
1
2
3
4
5
6
7
8
9
10
11
|
private void initData() { NewsApi.getInfo( new NetCallback<News>() { @Override public void OnSuccess( final News result) { mAdapter.setData(result.getResult().getData()); } @Override public void OnError(RestfulError error) { } }); } |
有没有看起来很舒服的感觉。好吧,让我开始盘它吧!
1.首先我先去写了一个基类,用来创建一个新的request并把它加入到volley内部封装的请求队列中,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
public abstract class AuthenticatedRequestBase<T> extends Request<T> { private final static String TAG = "AuthenticatedRequestBase" ; private static final int TIME_OUT = 30000 ; private static final int MAX_RETRIES = 1 ; private static final float BACKOFF_MULT = 2f; protected Context mContext; protected RequestQueue mRequestQueue; /** * 创建新的请求,并把请求加入到请求队列requestQueue中 * * @param method * @param url * @param cache * @param errorListener */ @SuppressLint ( "LongLogTag" ) public AuthenticatedRequestBase( int method, String url, boolean cache, Response.ErrorListener errorListener) { super (method, url, errorListener); //this.setShouldCache(cache); this .setRetryPolicy( new DefaultRetryPolicy( TIME_OUT, MAX_RETRIES, BACKOFF_MULT)); mRequestQueue = YZ.getInstance().getRequestQueue(); if (mRequestQueue == null ) { throw new IllegalArgumentException( "mRequestQueue can't be null" ); } mContext = YZ.getInstance().getContext(); if (mContext == null ) { throw new IllegalArgumentException( "mContext can't be null" ); } //如果重新发出服务器请求的时候,需要清除之前的缓存。 if (!cache) { Cache volleyCache = mRequestQueue.getCache(); Cache.Entry cacheEntry = volleyCache.get(url); if (cacheEntry != null ) { volleyCache.remove(url); Log.d(TAG, "remove volley cache:" + url); } } mRequestQueue.add( this ); } /** * 重写这个方法,可以在http请求头里面加入token,客户端能接受的数据类型 * * @return * @throws AuthFailureError */ @CallSuper @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> headers = new HashMap<>(); String token = "............" ; //headers.put("Authorization", "bearer " + token); //针对Get方法,申明接受的enum类型 // headers.put("Accept", "charset=utf-8"); return headers; } /** * 网络错误问题统一处理 * * @param volleyError * @return */ @CallSuper @Override protected VolleyError parseNetworkError(VolleyError volleyError) { return super .parseNetworkError(volleyError); } } |
代码注释比较清晰,就不在赘述。
2.以get方法为例,新建一个GetRequest去继承这个基类,并出解析结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
public class GetRequest<TResponse> extends AuthenticatedRequestBase<TResponse> { private final Response.Listener<TResponse> listener; private final Class<TResponse> clazz; private final static String TAG = "GetRequest" ; private String mUrl; private NetCallback<TResponse> cb; private boolean cacheHit; public GetRequest(String url, Class<TResponse> clazz, boolean cache, NetCallback<TResponse> callback) { super (Request.Method.GET, url, cache, callback.getErrorListener()); this .listener = callback.getSuccessListener(); this .clazz = clazz; this .mUrl = url; this .cb = callback; //无网络时300ms后返回callback if (!NetUtils.isConnect(mContext) && mRequestQueue.getCache().get(url) == null ) { Handler handler = new Handler(); handler.postDelayed( new Runnable() { @Override public void run() { cb.OnNetworkOff(); } }, 300 ); } } /** * 这个是缓存的标记,与本地缓存相关 * @param tag */ @Override public void addMarker(String tag) { super .addMarker(tag); cacheHit = tag.equals( "cache-hit" ); } @Override protected Response<TResponse> parseNetworkResponse(NetworkResponse response) { Gson gson = new Gson(); //无网络时,使用本地缓存,通过url去匹配缓存,volley sdk是通过url创建不同的文件来实现缓存的 if (!NetUtils.isConnect(mContext) && mRequestQueue.getCache().get(mUrl) != null ) { String json = new String(mRequestQueue.getCache().get(mUrl).data); Log.d(TAG, "url==" + mUrl + ",json" + json); cb.fResponseCacheStatus = ResponseCacheStatus.StaleFromCache; return Response.success(gson.fromJson(json, clazz), parseCacheHeaders(response)); } //数据是否有更新 try { if (response.statusCode == 304 ) { //服务端返回缓存数据 cb.fResponseCacheStatus = ResponseCacheStatus.NotModifiedFromServer; } else if (response.statusCode == 200 ) { if (cacheHit) { //使用本地缓存 cb.fResponseCacheStatus = ResponseCacheStatus.FreshFromCache; } else { //使用服务端更新数据 cb.fResponseCacheStatus = ResponseCacheStatus.NewFromServer; } } else { cb.fResponseCacheStatus = ResponseCacheStatus.NewFromServer; } Log.d(TAG, "fResponseCacheStatus = " + cb.fResponseCacheStatus); String json = new String(response.data, parseCharset(response.headers)); return Response.success(gson.fromJson(json, clazz), parseCacheHeaders(response)); } catch (UnsupportedEncodingException | JsonSyntaxException e) { return Response.error( new ParseError(e)); } } @Override protected void deliverResponse(TResponse response) { listener.onResponse(response); } @Override protected VolleyError parseNetworkError(VolleyError volleyError) { return super .parseNetworkError(volleyError); } } |
3.上面只做了返回成功的处理方式,返回失败时由NetCallback内部统一处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
@UiThread public abstract class NetCallback<TResponse> { public ResponseCacheStatus fResponseCacheStatus = ResponseCacheStatus.NewFromServer; private String TAG = this .getClass().getSimpleName(); public boolean enableAutomaticToastOnError = true ; public NetCallback() { } public NetCallback( boolean enableAutomaticToastOnError) { this .enableAutomaticToastOnError = enableAutomaticToastOnError; } public abstract void OnSuccess(TResponse result); public abstract void OnError(RestfulError error); public void OnNetworkOff() { //do nothing ,use it according to requirement } public Response.Listener<TResponse> getSuccessListener() { return new Response.Listener<TResponse>() { @Override public void onResponse(TResponse result) { OnSuccess(result); } }; } public Response.ErrorListener getErrorListener() { return new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { if (volleyError instanceof TimeoutError) { Log.e(TAG, "networkResponse == null" ); //volley TimeoutError OnError( new RestfulError()); } if (volleyError.networkResponse != null ) { int statusCode = volleyError.networkResponse.statusCode; String errorMessage = new String(volleyError.networkResponse.data); switch (statusCode) { case 401 : //post a Permission authentication failed event break ; default : Log.d(TAG, "errorMessage =" + errorMessage); try { RestfulError error = new Gson().fromJson(errorMessage, RestfulError. class ); if (enableAutomaticToastOnError && error.getCode() != null ) { //toast(error.ExceptionMessage); //toast it in main thread } OnError(error); } catch (Exception e) { OnError( new RestfulError()); Log.d(TAG, "e =" + e.toString()); } break ; } } } }; } } |
4.注意到没有,在AuthenticatedRequestBase内部有一个环境类YZ,主要负责获取项目主程序中的context和请求队列:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public class YZ implements AppRequestQueue { private static final int DEFAULT_VOLLEY_CACHE_SIZE = 100 * 1024 * 1024 ; private Context context; private int cacheSize; private YZ() { } @Override public RequestQueue getRequestQueue() { return Volley.newRequestQueue(context, cacheSize); } public Context getContext() { return context; } private static class SingletonHolder { private static YZ instance = new YZ(); } public static YZ getInstance() { return SingletonHolder.instance; } /** * need a app context * * @param appContext */ public void init( final Context appContext) { init(appContext, DEFAULT_VOLLEY_CACHE_SIZE); } /** * @param appContext * @param cacheSize */ public void init( final Context appContext, final int cacheSize) { this .context = appContext; this .cacheSize = cacheSize; } } |
这个类需要在app的application中初始化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class BaseApp extends Application { public String TAG = this .getClass().getSimpleName(); public static Context applicationContext; public static Executor threadPool; public static final int THREAD_POOL_SIZE = 3 ; public static final boolean isDebug = BuildConfig.BUILD_TYPE.equals( "debug" ); @Override public void onCreate() { super .onCreate(); applicationContext = getApplicationContext(); threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE); initNet(); } private void initNet() { YZ.getInstance().init( this ); } public Context getInstance() { return applicationContext; } } |
4.现在可以开始外部封装啦。
1
2
3
4
5
6
|
public class NewsApi { public static void getInfo(NetCallback<News> callback) { new GetRequest<>(INetConstant.NEWS, News. class , true , callback); } } |
还有一点,volley的缓存实现需要服务端配合在http请求的Cache-control: max-age配置支持缓存,并设定好缓存时间,否则无法生效。
最后贴一张效果图:
图片发自简书App
到此结束,后期还会进行优化,代码在[github] (https://github.com/daydaydate/sample (本地下载))。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:https://www.jianshu.com/p/1f09e91bee66