本文為菜鳥窩作者劉婷的連載伦乔∮碌酰”商城項目實戰(zhàn)”系列來聊聊仿”京東淘寶的購物商城”如何實現(xiàn)辫愉。
140套Android優(yōu)秀開源項目源碼,領(lǐng)取地址:http://mp.weixin.qq.com/s/afPGHqfdiApALZqHsXbw-A
或歡迎勾搭運營小姐姐(微信 id:BT474849)免費領(lǐng)取哦~
在前面的文章《商城項目實戰(zhàn) | 6.1 OkHttp 的詳細(xì)介紹 網(wǎng)絡(luò)請求更加簡單》中已經(jīng)詳細(xì)介紹了 OkHttp 的基本屬性和使用方法了族铆,OkHttp 開源框架給開發(fā)者帶來了很多的便利,使用非常方便哭尝,但是在項目中網(wǎng)絡(luò)請求一般到處都要使用到哥攘,如果總是重復(fù)相同的代碼,首先代碼不夠簡潔,其次效率也變低了不少莉掂。代碼的簡潔性是一個好的開發(fā)者所不可缺少的開發(fā)特點胳岂,如何使得代碼更為的簡潔呢蚓耽?那就要學(xué)會封裝了,本文主要是針對 OkHttp 框架使用的封裝茉兰,學(xué)會了這個,封裝其他的也都不是問題了欣簇。
封裝的意義和好處
1. 封裝的概念
封裝從字面上來理解就是包裝的意思规脸,專業(yè)點就是信息隱藏,是指利用抽象數(shù)據(jù)類型將數(shù)據(jù)和基于數(shù)據(jù)的操作封裝在一起熊咽,使其構(gòu)成一個不可分割的獨立實體莫鸭,數(shù)據(jù)被保護(hù)在抽象數(shù)據(jù)類型的內(nèi)部,盡可能地隱藏內(nèi)部的細(xì)節(jié)横殴,只保留一些對外接口使之與外部發(fā)生聯(lián)系被因。系統(tǒng)的其他對象只能通過包裹在數(shù)據(jù)外面的已經(jīng)授權(quán)的操作來與這個封裝的對象進(jìn)行交流和交互。封裝是面向?qū)ο蟮娜筇卣髦簧缆兀褪菍㈩惖臓顟B(tài)信息隱藏在類的內(nèi)部梨与,不允許外部程序直接訪問,而通過該類提供的方法來實現(xiàn)對隱藏信息的操作和訪問文狱。
2. 封裝的好處
- 1.良好的封裝能夠減少耦合粥鞋。
- 2.不必關(guān)心具體的實現(xiàn)。
- 3.控制用戶對類的修改和訪問數(shù)據(jù)的程度如贷,提高安全性陷虎。
- 4.可以方便的加入存取控制語句,限制不合理操作杠袱。
- 5.代碼更加容易被理解和維護(hù)尚猿。
OkHttp 的封裝
已經(jīng)了解了封裝的意義和所帶來的好處,更加要把封裝應(yīng)用到自己的代碼中去楣富,讓代碼更為得具有簡潔性和易維護(hù)性凿掂,下面開始 OkHttp 的封裝。
1. 封裝 OkHttp 要實現(xiàn)什么
在做一件事情之前纹蝴,都要考慮為什么要這樣做庄萎,在寫一個項目之前,都要考慮這個項目的需求塘安,在寫一行代碼之前糠涛,所要想的是要實現(xiàn)怎么的功能,同樣的兼犯,在封裝之前忍捡,要思考封裝可以實現(xiàn)什么集漾,可以先列舉出來,下面是我列舉的希望封裝 OkHttp 能夠?qū)崿F(xiàn)的功能砸脊。
- 1.OkHttpClient 可以不重復(fù)寫具篇,直接簡單調(diào)用 Get 和 Post 方法,尤其是 post 調(diào)用時傳遞參數(shù)希望可以一步搞定凌埂。
- 2.返回的數(shù)據(jù)可以直接拿來使用驱显,不要自己再過多的處理和序列化。
- 3.有些時候加載數(shù)據(jù)希望顯示提示加載框瞳抓,數(shù)據(jù)加載完后顯示框 dismiss埃疫,但是有的時候又不希望提示加載框顯示出來。
- 4.網(wǎng)絡(luò)請求失敗后挨下,可以提示錯誤信息熔恢,同時對于請求成功和請求失敗后的操作可以自己再擴(kuò)展。
按照這樣的想法臭笆,一步一步來實現(xiàn) OkHttp 的封裝叙淌。
**2. 實現(xiàn)封裝 OkHttp **
在列舉的所要實現(xiàn)的功能中,希望可以返回的數(shù)據(jù)可以直接拿來使用愁铺,另外還要顯示加載的提示框鹰霍,那么在封裝的 OkHttp 中一定需要對數(shù)據(jù)進(jìn)行處理,比如 Json 類型的數(shù)據(jù)茵乱,我們就用 Gson 框架來處理了茂洒,而提示對話框的話,這里也使用一款開源的框架 SpotsDialog 瓶竭。
2.1 添加 Gradle 依賴
因為使用的工具為 Android Studio 2.3.0督勺,所使用的集成工具為 gradle,所以在使用第三方開源框架時斤贰,添加依賴是永遠(yuǎn)必不可少的智哀。
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support.constraint:constraint-layout:1.0.1'
testCompile 'junit:junit:4.12'
compile 'com.daimajia.slider:library:1.1.5@aar'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.android.support:support-v4:25.2.0'
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:cardview-v7:25.2.0'
compile 'com.squareup.okhttp3:okhttp:3.6.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.github.d-max:spots-dialog:0.7'
}
2.2 封裝 Request
在使用 OkHttp 的時候,所調(diào)用的 get 和 post 方法雖然有所不同荧恍,但是其實很多的代碼還是重復(fù)的瓷叫,有必要把重復(fù)的代碼提取出來,另外這里主要是講解異步請求的方法送巡,所以封裝的也是針對于異步網(wǎng)絡(luò)請求的摹菠。
創(chuàng)建好 OkHttpHelper 類,添加構(gòu)造函數(shù) OkHttpHelper()骗爆,首先就是在這里創(chuàng)建 OkHttpClient次氨,進(jìn)行簡單的設(shè)置,同時 OkHttpHelper 的調(diào)用也要寫好來摘投,后期要使用 OkHttpHelper 時煮寡,就直接調(diào) getInstance() 方法就好了屉佳。
private static OkHttpHelper mInstance;
static {
mInstance = new OkHttpHelper();
}
public static OkHttpHelper getInstance(){
return mInstance;
}
private OkHttpHelper(){
mHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10,TimeUnit.SECONDS)
.writeTimeout(30,TimeUnit.SECONDS)
.build();
}
至于 get 和 post 還是需要分區(qū)下的,直接使用枚舉洲押。
enum HttpMethodType{
GET,
POST,
}
然后就是 Request 的創(chuàng)建,根據(jù) get 和 post 的不同來相應(yīng)創(chuàng)建 Request 圆凰,后期我們可以直接傳入?yún)^(qū)分參數(shù) HttpMethodType 調(diào)用這一個函數(shù)就可以了杈帐。代碼如下。
private Request buildRequest(String url,HttpMethodType methodType,Map<String,Object> params){
Request.Builder builder = new Request.Builder()
.url(url);
if (methodType == HttpMethodType.POST){
RequestBody body = builderFormData(params);
builder.post(body);
}
else if(methodType == HttpMethodType.GET){
url = buildUrlParams(url,params);
builder.url(url);
builder.get();
}
return builder.build();
}
private RequestBody builderFormData(Map<String,Object> params){
FormBody.Builder builder = new FormBody.Builder();
if(params !=null){
for (Map.Entry<String,Object> entry :params.entrySet() ){
builder.add(entry.getKey(),entry.getValue()==null?"":entry.getValue().toString());
}
}
private String buildUrlParams(String url ,Map<String,Object> params) {
if(params == null)
params = new HashMap<>(1);
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, Object> entry : params.entrySet()) {
sb.append(entry.getKey() + "=" + (entry.getValue()==null?"":entry.getValue().toString()));
sb.append("&");
}
String s = sb.toString();
if (s.endsWith("&")) {
s = s.substring(0, s.length() - 1);
}
if(url.indexOf("?")>0){
url = url +"&"+s;
}else{
url = url +"?"+s;
}
return url;
}
2.3 封裝 Callback
前面已經(jīng)寫好了 Request 了专钉,下面就是要考慮網(wǎng)絡(luò)請求之后的回調(diào)了挑童,主要是針對于網(wǎng)絡(luò)請求成功、網(wǎng)絡(luò)請求失敗以及請求成功但是遇到錯誤了的情況跃须,定義 abstract 抽象類 BaseCallback站叼。
public abstract class BaseCallback <T> {
public Type mType;
static Type getSuperclassTypeParameter(Class<?> subclass)
{
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class)
{
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
public BaseCallback()
{
mType = getSuperclassTypeParameter(getClass());
}
public abstract void onBeforeRequest(Request request);
public abstract void onFailure(Request request, Exception e) ;
/**
*請求成功時調(diào)用此方法
* @param response
*/
public abstract void onResponse(Response response);
/**
*
* 狀態(tài)碼大于200,小于300 時調(diào)用此方法
* @param response
* @param t
* @throws IOException
*/
public abstract void onSuccess(Response response,T t) ;
/**
* 狀態(tài)碼400菇民,404尽楔,403,500等時調(diào)用此方法
* @param response
* @param code
* @param e
*/
public abstract void onError(Response response, int code,Exception e) ;
}
2.4 對獲取的數(shù)據(jù)處理和序列化
請求之后就是要處理獲取到的數(shù)據(jù)了第练,如果是 String 類型的數(shù)據(jù)阔馋,就不用過多處理,但是如果是 Json 格式的數(shù)據(jù)娇掏,就要序列化了呕寝,處理數(shù)據(jù)主要是請求成功并且沒有出現(xiàn)錯誤的時候?qū)Λ@取的數(shù)據(jù)進(jìn)行處理,所以也要用到之前寫好的 BaseCallback 了婴梧。
當(dāng)然先要在構(gòu)造函數(shù) OkHttpHelper() 中初始化 Gson下梢。
Gson mGson = new Gson();
public void request(final Request request,final BaseCallback callback){
callback.onBeforeRequest(request);
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callbackFailure(callback, request, e);
}
@Override
public void onResponse(Call call,Response response) throws IOException {
callbackResponse(callback,response);
if(response.isSuccessful()) {
String resultStr = response.body().string();
Log.d(TAG, "result=" + resultStr);
if (callback.mType == String.class){
callbackSuccess(callback,response,resultStr);
}
else {
try {
Object obj = mGson.fromJson(resultStr, callback.mType);
callbackSuccess(callback,response,obj);
}
catch (com.google.gson.JsonParseException e){ // Json解析的錯誤
callback.onError(response,response.code(),e);
}
}
}
else {
callbackError(callback,response,null);
}
}
});
}
2.5 Handler 來幫忙
在使用網(wǎng)絡(luò)請求開線程的時候一定要注意一個問題,那就是對 View 的操作塞蹭,必須在主線程中孽江,所以就需要 Handler 來幫忙了。
當(dāng)然先要在構(gòu)造函數(shù) OkHttpHelper() 中初始化 Handler浮还。
Handler mHandler = new Handler(Looper.getMainLooper());
private void callbackSuccess(final BaseCallback callback , final Response response, final Object obj ){
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(response, obj);
}
});
}
private void callbackError(final BaseCallback callback , final Response response, final Exception e ){
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onError(response,response.code(),e);
}
});
}
private void callbackFailure(final BaseCallback callback , final Request request, final IOException e ){
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(request,e);
}
});
}
private void callbackResponse(final BaseCallback callback , final Response response ){
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onResponse(response);
}
});
}
2.6 加入提示加載框
之前列舉的功能中希望有時候可以顯示加載框竟坛,有時候不顯示,所以這里就需要擴(kuò)展下之前的 BaseCallback钧舌,寫一個帶有提示加載框的 SpotsCallBack担汤,并且在里面處理提示框的顯示和隱藏。
public abstract class SpotsCallBack<T> extends SimpleCallback<T> {
private SpotsDialog mDialog;
public SpotsCallBack(Context context){
super(context);
initSpotsDialog();
}
private void initSpotsDialog(){
mDialog = new SpotsDialog(mContext,"拼命加載中...");
}
public void showDialog(){
mDialog.show();
}
public void dismissDialog(){
mDialog.dismiss();
}
public void setLoadMessage(int resId){
mDialog.setMessage(mContext.getString(resId));
}
@Override
public void onBeforeRequest(Request request) {
showDialog();
}
@Override
public void onResponse(Response response) {
dismissDialog();
}
@Override
public void onFailure(Request request, Exception e) {
dismissDialog();
super.onFailure(request, e);
}
@Override
public void onError(Response response, int code, Exception e) {
dismissDialog();
}
}
2.7 寫好 get 和 post 的調(diào)用方法
所需要封裝的都已經(jīng)封裝好了洼冻,現(xiàn)在就是要在 OkHttpHelper 中寫好相應(yīng)的 get 和 post 的調(diào)用方法崭歧,也就是函數(shù)。
public void get(String url,Map<String,Object> param,BaseCallback callback){
Request request = buildGetRequest(url,param);
request(request,callback);
}
public void get(String url,BaseCallback callback){
get(url,null,callback);
}
public void post(String url,Map<String,Object> param, BaseCallback callback){
Request request = buildPostRequest(url,param);
request(request,callback);
}
到這里就基本實現(xiàn)了我之前列舉的功能了撞牢,如果還需要其他的功能的話率碾,可以自行封裝其他的叔营。
使用封裝好的 OkHttp
已經(jīng)封裝好的 OkHttp 要實際運用起來,看下是不是優(yōu)化了很多所宰,在之前的文章《商城項目實戰(zhàn) | 6.1 OkHttp 的詳細(xì)介紹 網(wǎng)絡(luò)請求更加簡單》中使用 OkHttp 來實現(xiàn)炫酷輪播廣告绒尊,現(xiàn)在我們就用封裝好的 OkHttp 來實現(xiàn)同樣的功能。
下面是封裝前的網(wǎng)絡(luò)請求方法仔粥,這里使用的是 get 請求婴谱。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case INIT_SLIDER_TYPE:
initSlider();
break;
}
}
};
private void getBannerData() {
String url ="http://112.124.22.238:8081/course_api/banner/query?type=1";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Toast.makeText(getActivity(),e.getMessage().toString(),Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
Type type = new TypeToken<List<BannerInfo>>(){}.getType();
Gson gson = new Gson();
List<BannerInfo> list= gson.fromJson(response.body().string(),type);
for (BannerInfo bannerInfo:list)
{
listBanner.add(bannerInfo);
}
handler.sendEmptyMessage(INIT_SLIDER_TYPE);
}else {
Toast.makeText(getActivity(),"IOException",Toast.LENGTH_SHORT).show();
}
}
});
}
下面是封裝后的網(wǎng)絡(luò)請求處理。
private void getBannerData() {
String url ="http://112.124.22.238:8081/course_api/banner/query?type=1";
httpHelper.get(url, new SpotsCallBack<List<BannerInfo>>(getActivity()){
@Override
public void onSuccess(Response response, List<BannerInfo> banners) {
listBanner = banners;
initSlider();
}
@Override
public void onError(Response response, int code, Exception e) {
Toast.makeText(getActivity(),code+e.toString(),Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Request request, Exception e) {
super.onFailure(request, e);
Toast.makeText(getActivity(),e.toString(),Toast.LENGTH_SHORT).show();
}
});
}
還是使用的 get 請求方法躯泰,但是感覺代碼明顯簡潔了不少谭羔,也更加的規(guī)整,同時調(diào)用也方便了很多麦向。
效果圖
最后還是運行下代碼瘟裸,獲取到效果圖。
[圖片上傳失敗...(image-d9fec5-1565145693331)]
效果圖和之前是一樣的诵竭,但是方法已經(jīng)簡便了很多话告,封裝可以給我們帶來很多的便利,也讓我們更加靈活的使用 OkHttp 了秀撇。