Leopard
《Android 巧妙封裝籍铁,基于Retrofit+RxJava網(wǎng)絡(luò)框架“Leopard”---完整淺析》
轉(zhuǎn)載請(qǐng)注明來(lái)自 傻小孩b_移動(dòng)開(kāi)發(fā)
(http://www.reibang.com/users/d388bcf9c4d3)
喜歡的可以關(guān)注我褂微,不定期總結(jié)文章称近!您的支持是我的動(dòng)力哈链韭!
前言
Leopard 意為獵豹特碳,在所有貓科動(dòng)物中始苇。獵豹體型最小搁拙,速度快云挟、最穩(wěn)定矫限。這也是筆者想用這個(gè)名字命名這個(gè)Kit的原因哺哼。希望這個(gè)Kit能對(duì)部分開(kāi)發(fā)者對(duì)于網(wǎng)絡(luò)框架封裝的一些思路有所幫助佩抹,筆者也在奮斗路上堅(jiān)持總結(jié)進(jìn)步中,共勉取董!最后棍苹,有任何問(wèn)題可以提issuse給我,或者直接聯(lián)系本人(QQ:708854877 傻小孩b)茵汰,喜歡可以為我點(diǎn)個(gè)star枢里,你們的支持是我最大的動(dòng)力~謝謝!
Leopard蹂午,目前實(shí)現(xiàn)功能
提供一個(gè)滿足日常需求的HTTP線程安全請(qǐng)求封裝Library栏豺,底層由Retrofit+Okhttp+RxJava支持,通過(guò)構(gòu)建者builder設(shè)計(jì)模式實(shí)現(xiàn)豆胸。目前實(shí)現(xiàn)POST奥洼、GET(支持自定義頭文件、表單鍵值對(duì)請(qǐng)求晚胡、自定義數(shù)據(jù)源等基本請(qǐng)求)灵奖、文件上傳管理(支持單文件上傳與多文件上傳,不限制文件類型)估盘、文件下載管理(支持單文件下載與多文件下載瓷患、不限制文件類型、支持大文件下載與斷點(diǎn)下載)
演示
Leopard忿檩,引用方法:
注:目前只支持Android Studio版本引用尉尾。
1.1 版本 使用方法:
一、在application中的build.gradle引入:
repositories {
maven { url = 'https://dl.bintray.com/yuancloud/maven/' }
...
}
compile 'cn.yuancloud.app:leopardkit:1.1'
詳情使用方法可以看kit里面的sample工程燥透,下面也會(huì)舉例使用方法沙咏。
Leopard,源碼淺析
一班套、構(gòu)建者模式
通常肢藐,大部分開(kāi)發(fā)者都會(huì)用單例模式去封裝網(wǎng)絡(luò)框架。的確吱韭,對(duì)于網(wǎng)絡(luò)請(qǐng)求嚴(yán)重消耗內(nèi)存的對(duì)象吆豹,單例模式很大程度減少了內(nèi)存開(kāi)銷啊。但是理盆,單例模式職責(zé)太單一痘煤,靈活性真的不高。所以在這里我強(qiáng)烈建議用構(gòu)建者模式猿规,需要什么資源只要通知單一職責(zé)構(gòu)建者Builder即可衷快,這樣不僅僅可以減少內(nèi)存開(kāi)銷、又可以靈活性構(gòu)建需要的對(duì)象姨俩,一舉兩得蘸拔。
二师郑、7個(gè)Factory支持
這里的Factory,包括Converter.Factory與Interceptor支持调窍。目前包括Retrofit底層已經(jīng)實(shí)現(xiàn)的GsonConverterFactory與RxJavaCallAdapterFactory宝冕,Leopard 添加了額外5個(gè)Factory,下面具體簡(jiǎn)單說(shuō)明下額外的5個(gè)Factory與Retrofit底層已經(jīng)實(shí)現(xiàn)的Factory邓萨。
(1)GsonConverterFactory
Retrofit底層支持Gson地梨,這個(gè)Factory提供當(dāng)你需要調(diào)整json里面的一些格式的時(shí)候可以使用。
(2)RxJavaCallAdapterFactory
當(dāng)你需要結(jié)合RxJava的時(shí)候先誉,而不是僅僅使用原生Retrofit請(qǐng)求響應(yīng)回調(diào)的Call湿刽。這時(shí)候你需要回調(diào)一個(gè)觀察者(Observable)的時(shí)候,必須構(gòu)造的時(shí)候添加這個(gè)Factory褐耳。
(3)RequestComFactory
默認(rèn)必須要添加的Factory诈闺,為了攔截請(qǐng)求時(shí)候的request與response。
(4)RequestJsonFactory
當(dāng)你需要向你的服務(wù)器請(qǐng)求非鍵值對(duì)铃芦,而是自定義對(duì)象轉(zhuǎn)后后的json數(shù)據(jù)的時(shí)候雅镊。必須構(gòu)造的時(shí)候添加這個(gè)Factory,底層會(huì)自動(dòng)識(shí)別幫你轉(zhuǎn)化刃滓。
(5)UploadFileFactory
顧名思義仁烹,當(dāng)你需要進(jìn)行上傳文件的時(shí)候,在構(gòu)造client的時(shí)需要添加這個(gè)Factory咧虎,底層文件類型頭信息會(huì)自動(dòng)生成卓缰。
(6)DownLoadFileFactory
顧名思義,當(dāng)你需要進(jìn)行下載文件的時(shí)候砰诵,在構(gòu)造client的時(shí)需要添加這個(gè)Factory征唬,底層會(huì)自動(dòng)根據(jù)文件類型自動(dòng)生成頭信息,默認(rèn)開(kāi)啟斷點(diǎn)續(xù)傳茁彭,下載過(guò)程可以通過(guò)DownLoadManager管理控制总寒。
(7)HeaderAddFactory
當(dāng)你需要自定義頭文件的時(shí)候,理肺,在構(gòu)造client的時(shí)需要添加這個(gè)Factory摄闸。底層會(huì)自動(dòng)幫你添加到請(qǐng)求頭。
三妹萨、關(guān)于基本請(qǐng)求
RxJava的好處年枕,可以保證線程執(zhí)行安全。由于網(wǎng)絡(luò)請(qǐng)求不能執(zhí)行在主線程乎完,因此在Leopard中熏兄,將所有網(wǎng)絡(luò)執(zhí)行都放在io線程中,確保線程執(zhí)行安全。例如以下配置:
final Observable.Transformer schedulersTransformer = new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable) observable)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
;
}
};
在Leopard中霍弹,有基本的已經(jīng)寫(xiě)好的6個(gè)入口,開(kāi)發(fā)者可以根據(jù)自己的需求去繼承自定義入口
@POST("{path}")
Observable<ResponseBody> post(
@Path(value = "path", encoded = true) String path,
@QueryMap Map<String, Object> map);
@GET("{path}")
Observable<ResponseBody> get(
@Path(value = "path", encoded = true) String path,
@QueryMap Map<String, Object> map);
@POST("{path}")
Observable<ResponseBody> postJSON(
@Path(value = "path", encoded = true) String path,
@Body RequestBody route);
@GET("{path}")
Observable<ResponseBody> getJSON(
@Path(value = "path", encoded = true) String path,
@Body RequestBody route);
@Multipart
@POST("{path}")
Observable<ResponseBody> uploadFile(
@Path(value = "path", encoded = true) String url,
@PartMap() Map<String, RequestBody> maps);
//支持大文件
// @Streaming
@GET
Observable<ResponseBody> downloadFile(
@Url String fileUrl);
詳細(xì)的源碼解析會(huì)在筆者博客簡(jiǎn)書(shū)分析娃弓。
四典格、關(guān)于下載
對(duì)于Leopard的下載管理模式,借鑒了Android原生DownLoadManager下載管理的模式--DownLoadManager與DownLoadTask結(jié)合的機(jī)制台丛。DownLoadTask負(fù)責(zé)單一職責(zé)耍缴,為每個(gè)下載 任務(wù)提供下載服務(wù)(緩存管理、開(kāi)始挽霉、暫停防嗡、停止等功能)。DownLoadManager作為多DownLoadTask管理者侠坎,為多任務(wù)提供所有的下載服務(wù)蚁趁。下面是下載的具體實(shí)現(xiàn)邏輯圖。
詳細(xì)的源碼解析會(huì)在筆者博客簡(jiǎn)書(shū)分析实胸。
五他嫡、關(guān)于上傳
常規(guī)做法,上傳與鍵值對(duì)Reqtuest有類似的操作邏輯庐完。將File做為鍵值對(duì)的value钢属,將上傳File的文件信息作為key,然后向服務(wù)器進(jìn)行g(shù)et請(qǐng)求即可门躯。這里要處理的是對(duì)文件上傳進(jìn)度的監(jiān)聽(tīng)淆党,對(duì)于okhttp3底層封裝的RequestBody,并沒(méi)有對(duì)進(jìn)度字節(jié)流作為緩存處理讶凉,因此在Leopard中需要重寫(xiě)RequestBody染乌,才能對(duì)上傳緩存做處理。下面是上傳具體邏輯圖缀遍。
詳細(xì)的源碼解析會(huì)在筆者博客簡(jiǎn)書(shū)分析慕匠。
Leopard,使用方法(自動(dòng)化構(gòu)建)
為了減少使用者使用復(fù)雜度域醇,leopard里面有集成好自動(dòng)構(gòu)建的對(duì)象台谊,采用的是單例模式。不過(guò)筆者還是推薦根據(jù)需求自定義譬挚,使用方法如下:
0.初始化
// 初始化主機(jī)域名與上下文
// 建議傳入getApplication
LeopardHttp.init("http://wxwusy.applinzi.com/leopardWeb/app/",this);
1.基本請(qǐng)求
首先在請(qǐng)求參數(shù)的時(shí)候必須定義一個(gè)model锅铅,并且繼承Leopard里面的類BaseEnetity,具體定義的model如下例子:
public class RequestPostModel extends BaseEnetity{
@Override
public String getRuqestURL() {
return "sample/post.php";//你的訪問(wèn)url
}
//請(qǐng)求參數(shù)
private String data;
private String time;
public RequestPostModel(String data, String time) {
this.data = data;
this.time = time;
}
//必須構(gòu)建get set方法
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
1.1 post與get鍵值對(duì)請(qǐng)求
1.1.1 POST
/**
*
* @param type 請(qǐng)求類型减宣,具體看HttpMethod盐须,提供了四種請(qǐng)求類型
* @param context 上下文
* @param enetity 請(qǐng)求model
* @param httpRespondResult 回調(diào)接口
*/
LeopardHttp.SEND(HttpMethod.POST,getActivity(),new RequestPostModel("leopard", Utils.getNowTime()), new HttpRespondResult(getActivity()) {
@Override
public void onSuccess(String content) {//json
resonseData.setText("onSuccess \n"+content);
}
@Override
public void onFailure(Throwable error, String content) {
resonseData.setText("onFailure \n"+content);
}
});
1.1.2 GET
/**
*
* @param type 請(qǐng)求類型,具體看HttpMethod漆腌,提供了四種請(qǐng)求類型
* @param context 上下文
* @param enetity 請(qǐng)求model
* @param httpRespondResult 回調(diào)接口
*/
LeopardHttp.SEND(HttpMethod.GET,getActivity(),new RequestGetModel("leopard", Utils.getNowTime()), new HttpRespondResult(getActivity()) {
@Override
public void onSuccess(String content) {//json
resonseData.setText("onSuccess \n"+content);
}
@Override
public void onFailure(Throwable error, String content) {
resonseData.setText("onFailure \n"+content);
}
});
1.2 post與get自定義Json對(duì)象請(qǐng)求
1.2.1 POST JSON
/**
*
* @param type 請(qǐng)求類型贼邓,具體看HttpMethod阶冈,提供了四種請(qǐng)求類型
* @param enetity 請(qǐng)求model
* @param httpRespondResult 回調(diào)接口
*/
LeopardHttp.SEND(HttpMethod.POST_JSON,getActivity(),new RequestPostJsonModel("leopard", Utils.getNowTime()), new HttpRespondResult(getActivity()) {
@Override
public void onSuccess(String content) {//json
resonseData.setText("onSuccess \n"+content);
}
@Override
public void onFailure(Throwable error, String content) {
resonseData.setText("onFailure \n"+content);
}
});
1.3post與get請(qǐng)求,帶頭部
POST 帶頭部請(qǐng)求
HashMap<String,String> headers = new HashMap<>();
headers.put("apikey","dqwijotfpgjweigowethiuhqwqpqeqp");
headers.put("apiSecret","ojtowejioweqwcxzcasdqweqrfrrqrqw");
headers.put("name","leopard");
/**
*
* @param type 請(qǐng)求類型塑径,具體看HttpMethod女坑,提供了四種請(qǐng)求類型
* @param context 上下文
* @param enetity 請(qǐng)求model
* @param header 請(qǐng)求頭文件
* @param httpRespondResult 回調(diào)接口
*/
LeopardHttp.SEND(HttpMethod.POST,getActivity(),new RequestPostModel("leopard", Utils.getNowTime()),headers, new HttpRespondResult(getActivity()) {
@Override
public void onSuccess(String content) {
resonseData.setText("onSuccess \n"+content);
}
@Override
public void onFailure(Throwable error, String content) {
resonseData.setText("onFailure \n"+content);
}
});
GET 帶頭部請(qǐng)求
HashMap<String,String> headers = new HashMap<>();
headers.put("apikey","dqwijotfpgjweigowethiuhqwqpqeqp");
headers.put("apiSecret","ojtowejioweqwcxzcasdqweqrfrrqrqw");
headers.put("name","leopard");
/**
*
* @param type 請(qǐng)求類型,具體看HttpMethod统舀,提供了四種請(qǐng)求類型
* @param context 上下文
* @param enetity 請(qǐng)求model
* @param header 請(qǐng)求頭文件
* @param httpRespondResult 回調(diào)接口
*/
LeopardHttp.SEND(HttpMethod.GET,getActivity(),new RequestPostModel("leopard", Utils.getNowTime()),headers, new HttpRespondResult(getActivity()) {
@Override
public void onSuccess(String content) {
resonseData.setText("onSuccess \n"+content);
}
@Override
public void onFailure(Throwable error, String content) {
resonseData.setText("onFailure \n"+content);
}
});
2.上傳管理
首先在上傳的時(shí)候必須先看下上傳封裝實(shí)體類FileUploadEnetity(部分代碼)
.....
private String url;
private List<File> files = new ArrayList<>();
public FileUploadEnetity(String url, File file) {
this.url = url;
this.files.add(file);
initSize();
}
public FileUploadEnetity(String url, List<File> files) {
this.url = url;
this.files = files;
initSize();
}
.....
在上傳的時(shí)候匆骗,允許開(kāi)發(fā)者上傳你一個(gè)或多個(gè)文件。如果是上傳一個(gè)文件的時(shí)候誉简,構(gòu)建的時(shí)候只需要傳入上傳地址與File類型的文件碉就;反之上傳多文件,構(gòu)建的時(shí)候只需要傳入上傳地址與List<File> 類型的文件隊(duì)列類型闷串。
2.1基本上傳
/**
* 上傳入口
*
* @param FileUploadEnetity 上傳封裝實(shí)體類
* @param IProgress 上傳進(jìn)度回調(diào)接口
*/
LeopardHttp.UPLOAD(new FileUploadEnetity("upload.php",fileList), new IProgress() {
@Override
public void onProgress(long progress, long total, boolean done) {
// progress 返回當(dāng)前上傳的進(jìn)度 瓮钥,total 返回當(dāng)前上傳的總文件大小
if (done){
progressDialog.dismiss();
Toast.makeText(getActivity(),"所有圖片上傳成功!烹吵!",Toast.LENGTH_SHORT).show();
}
}
});
3.下載管理
首先底層斷點(diǎn)信息數(shù)據(jù)庫(kù)存儲(chǔ)由Greendao數(shù)據(jù)庫(kù)框架支持骏庸。其次,在下載的時(shí)候必須看下實(shí)體封裝類DownloadInfo 年叮,在進(jìn)行添加下載Task的時(shí)候具被,在構(gòu)建DownloadInfo 對(duì)象的時(shí)候,至少初始化下載地址只损、存儲(chǔ)文件名一姿。例如以下代碼:
String url = "http://f1.market.xiaomi.com/download/AppStore/03f82a470d7ac44300d8700880584fe856387aac6/cn.wsy.travel.apk";
DownloadInfo info = new DownloadInfo();
info.setUrl(url);
info.setFileName("IRecord_" + i + ".apk");
3.1基本下載(添加任務(wù)Task與啟動(dòng)下載)
/**
* 下載入口
*
* @param downloadInfo
* @param iProgress
* @return 擁有Task的下載實(shí)體
*/
LeopardHttp.DWONLOAD(info, new IProgress() {
@Override
public void onProgress(long progress, long total, boolean done) {
// progress 返回當(dāng)前上傳的進(jìn)度 ,total 返回當(dāng)前上傳的總文件大小
//按鈕更新
if (info.getState() == DownLoadManager.STATE_WAITING) {
holder.downBtn.setText("下載");
}
if (info.getState() == DownLoadManager.STATE_PAUSE) {
holder.downBtn.setText("繼續(xù)");
}
if (info.getState() == DownLoadManager.STATE_DOWNLOADING) {
holder.downBtn.setText("暫停");
}
if (done) {
//下載完成...
}
}
});
3.2 DownLoadTask 單任務(wù)下載管理
3.2.1 開(kāi)始下載
// 傳入true表示從頭開(kāi)始下載
downloadInfo.getDownLoadTask().downLoad(true);
3.2.2 暫停下載
downloadInfo.getDownLoadTask().pause();
3.2.3 恢復(fù)下載
downloadInfo.getDownLoadTask().resume();
3.2.4 停止下載
downloadInfo.getDownLoadTask().stop();
3.2.5 重新下載
downloadInfo.getDownLoadTask().restart();
3.3 DownLoadManager 多任務(wù)下載管理
3.3.1 刪除所有下載任務(wù)
DownLoadManager.getManager().removeAllTask();
3.3.2 暫停所有任務(wù)
DownLoadManager.getManager().pauseAllTask();
3.3.3 停止所有任務(wù)
DownLoadManager.getManager().stopAllTask();
3.3.4 開(kāi)始所有任務(wù)
DownLoadManager.getManager().startAllTask();
Leopard跃惫,使用方法(手動(dòng)構(gòu)建)
前面封裝的單例模式LeopardHttp,是為了開(kāi)發(fā)者可以更加方便調(diào)用叮叹,自動(dòng)化構(gòu)建好了LeopardClient對(duì)象并且配置了相對(duì)應(yīng)需要的屬性。不過(guò)還是那句話爆存,為了能夠讓讀者可以更加靈活配置LeopardClient蛉顽,所以建議還是用構(gòu)建者模式構(gòu)建,可以根據(jù)需求再自定義攔截器或者自定義Factroy先较。
現(xiàn)在舉例子手動(dòng)構(gòu)建的部分代碼携冤,目前支持的功能有:
1.基本請(qǐng)求
1.1 POST與 GET 鍵值對(duì)請(qǐng)求
LeopardClient.Builder()
.addRequestComFactory(new RequestComFactory(respondResult))
.addGsonConverterFactory(GsonConverterFactory.create())
.addRxJavaCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(ADDRESS)
.build()
.GET(context, enetity, httpRespondResult);//get請(qǐng)求
// .POST(context, enetity, httpRespondResult);//post請(qǐng)求
1.2 POST與 GET 自定義Json請(qǐng)求
LeopardClient.Builder()
.addRequestComFactory(new RequestComFactory(respondResult))
.addGsonConverterFactory(GsonConverterFactory.create())
.addRequestJsonFactory(RequestJsonFactory.create())
.addRxJavaCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(ADDRESS)
.build()
.GET(context, enetity, httpRespondResult);//get請(qǐng)求
// .POST(context, enetity, httpRespondResult);//post請(qǐng)求
2.下載管理
LeopardClient.Builder()
.addRxJavaCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addDownLoadFileFactory(DownLoadFileFactory.create(this.fileRespondResult, this.downloadInfo))
.build()
.downLoadFile(this.downloadInfo, callBack.fileRespondResult, downLoadTask);
3.上傳管理
LeopardClient.Builder()
.addRequestComFactory(new RequestComFactory(respondResult))
.addGsonConverterFactory(GsonConverterFactory.create())
.addRxJavaCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(ADDRESS)
.addUploadFileFactory(UploadFileFactory.create())
.build()
.upLoadFiles(uploadEnetity,respondResult)
目前擴(kuò)展性,允許開(kāi)發(fā)者進(jìn)行繼承性擴(kuò)展的有:
1.自定義接入接口,允許開(kāi)發(fā)者根據(jù)需求接入新的接口闲勺。
繼承BaseServerApi接口即可
2.自定義擴(kuò)展Builder,允許開(kāi)發(fā)者自定義添加攔截器與Factory
繼承LeopardClient.Builder與LeopardClient即可
3.允許下載管理任務(wù)功能擴(kuò)展
允許開(kāi)發(fā)者可以擴(kuò)展下載管理任務(wù)功能曾棕,底層基本的開(kāi)始、暫停等方法不允許修改菜循。開(kāi)發(fā)者可以額外擴(kuò)展其他自定義功能翘地。
版本迭代中...
最后希望
后期我會(huì)針對(duì)基本請(qǐng)求、下載管理、上傳管理做深入分析衙耕。
希望有讀者閱讀的時(shí)候能夠看到筆者這句話:
“寫(xiě)框架的原因不在于有多少人能使用昧穿,希望是有更多人能夠參與進(jìn)來(lái),思考封裝抽象思維橙喘,提高代碼抽象編寫(xiě)能力粤咪。”
源碼地址:
Android 基于Retrofit+RxJava 封裝 Leopard 網(wǎng)絡(luò)框架-完整淺析
傻小孩b mark共勉渴杆,寫(xiě)給在成長(zhǎng)路上奮斗的你
喜歡就為我點(diǎn)下喜歡、給我個(gè)github的star吧~ 感謝各位讀者閱讀宪塔。