Android中volley封裝實(shí)踐記錄
前言
在項(xiàng)目中一般使用使用volley方式如下,用起來給人一種很亂的感覺,于是一種盤它的想法油然而生。
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);
}
首先看一下我封裝后的使用例子:
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.首先我先去寫了一個(gè)基類,用來創(chuàng)建一個(gè)新的request并把它加入到volley內(nèi)部封裝的請(qǐng)求隊(duì)列中,代碼如下:
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;
/**
* 創(chuàng)建新的請(qǐng)求,并把請(qǐng)求加入到請(qǐng)求隊(duì)列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");
}
//如果重新發(fā)出服務(wù)器請(qǐng)求的時(shí)候,需要清除之前的緩存。
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);
}
/**
* 重寫這個(gè)方法,可以在http請(qǐng)求頭里面加入token,客戶端能接受的數(shù)據(jù)類型
*
* @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);
//針對(duì)Get方法,申明接受的enum類型
// headers.put("Accept", "charset=utf-8");
return headers;
}
/**
* 網(wǎng)絡(luò)錯(cuò)誤問題統(tǒng)一處理
*
* @param volleyError
* @return
*/
@CallSuper
@Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
return super.parseNetworkError(volleyError);
}
}
代碼注釋比較清晰,就不在贅述。
2.以get方法為例,新建一個(gè)GetRequest去繼承這個(gè)基類,并出解析結(jié)果:
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;
//無網(wǎng)絡(luò)時(shí)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);
}
}
/**
* 這個(gè)是緩存的標(biāo)記,與本地緩存相關(guān)
* @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();
//無網(wǎng)絡(luò)時(shí),使用本地緩存,通過url去匹配緩存,volley sdk是通過url創(chuàng)建不同的文件來實(shí)現(xiàn)緩存的
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));
}
//數(shù)據(jù)是否有更新
try {
if (response.statusCode == 304) {
//服務(wù)端返回緩存數(shù)據(jù)
cb.fResponseCacheStatus = ResponseCacheStatus.NotModifiedFromServer;
} else if (response.statusCode == 200) {
if (cacheHit) {
//使用本地緩存
cb.fResponseCacheStatus = ResponseCacheStatus.FreshFromCache;
} else {
//使用服務(wù)端更新數(shù)據(jù)
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.上面只做了返回成功的處理方式,返回失敗時(shí)由NetCallback內(nèi)部統(tǒng)一處理:
@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.注意到?jīng)]有,在AuthenticatedRequestBase內(nèi)部有一個(gè)環(huán)境類YZ,主要負(fù)責(zé)獲取項(xiàng)目主程序中的context和請(qǐng)求隊(duì)列:
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;
}
}
這個(gè)類需要在app的application中初始化:
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.現(xiàn)在可以開始外部封裝啦。
public class NewsApi {
public static void getInfo(NetCallback<News> callback) {
new GetRequest<>(INetConstant.NEWS, News.class, true, callback);
}
}
還有一點(diǎn),volley的緩存實(shí)現(xiàn)需要服務(wù)端配合在http請(qǐng)求的Cache-control: max-age配置支持緩存,并設(shè)定好緩存時(shí)間,否則無法生效。
最后貼一張效果圖:
圖片發(fā)自簡書App
到此結(jié)束,后期還會(huì)進(jìn)行優(yōu)化,代碼在[github] (https://github.com/daydaydate/sample (本地下載))。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Android框架Volley之利用Imageloader和NetWorkImageView加載圖片的方法
- Android框架Volley使用之Post請(qǐng)求實(shí)現(xiàn)方法
- Android框架Volley使用之Json請(qǐng)求實(shí)現(xiàn)
- Android框架Volley使用:ImageRequest請(qǐng)求實(shí)現(xiàn)圖片加載
- Android中volley封裝實(shí)踐記錄(二)
- Android Volley擴(kuò)展實(shí)現(xiàn)支持進(jìn)度條的文件上傳功能
- Android使用Volley框架定制PostUploadRequest上傳文件
- Android使用Volley實(shí)現(xiàn)上傳文件功能
- 解析Android框架之Volley源碼
相關(guān)文章
通過OpenGL ES混合模式縮放視頻緩沖區(qū)來適應(yīng)顯示尺寸
當(dāng)開發(fā)基于軟件模式的游戲時(shí),通過縮放視頻緩沖區(qū)來適應(yīng)顯示尺寸是最棘手的問題之一;作為開發(fā)人員,我們必須嘗試在性能與顯示質(zhì)量之間找到最佳平衡點(diǎn)2012-12-12
Android使用Fragment實(shí)現(xiàn)兼容手機(jī)和平板的程序
這篇文章主要介紹了Android使用Fragment實(shí)現(xiàn)兼容手機(jī)和平板的程序,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-04-04
Android 谷歌推薦的VR實(shí)現(xiàn)方式(分享)
下面小編就為大家分享一篇Android 谷歌推薦的VR實(shí)現(xiàn)方式。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
Android WebView實(shí)現(xiàn)頂部進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android WebView實(shí)現(xiàn)頂部進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼
本篇文章主要介紹了Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09
Android 使用PopupWindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解
最近想要做一個(gè)彈出更多的菜單,而原生的彈出菜單卻不是我們想要的效果,所以必然要自定義菜單。接下來通過本文給大家介紹android 使用popupwindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解,需要的朋友可以參考下2017-04-04
android實(shí)現(xiàn)添加耳機(jī)狀態(tài)圖標(biāo)的方法
這篇文章主要介紹了android實(shí)現(xiàn)添加耳機(jī)狀態(tài)圖標(biāo)的方法,較為詳細(xì)的分析了Android實(shí)現(xiàn)添加耳機(jī)圖標(biāo)的原理與相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10

