之前公司的項(xiàng)目用到了MVP+Retrofit+RxJava的框架進(jìn)行網(wǎng)絡(luò)請求宏蛉,所以今天特此寫一篇文章以做總結(jié)肯污。相信很多人都聽說過MVP颁独、Retrofit彩届、以及RxJava,有的人已經(jīng)開始用了奖唯,有的人可能還不知道這是什么惨缆,以及到底怎么用。不過沒關(guān)系丰捷,接下來我將為你一一揭開他們的神秘面紗坯墨,然后利用這三個(gè)家伙搭建一個(gè)網(wǎng)絡(luò)請求框架
1.什么是MVP?
MVP(Model View Presenter)其實(shí)就是一種項(xiàng)目的整體框架病往,能讓你的代碼變得更加簡潔捣染,說起框架大家可能還會想到MVC、MVVM停巷。由于篇幅原因耍攘,這里我們先不講MVVM,先來看一下MVC畔勤。其實(shí)Android本身就采用的是MVC(Model View Controllor)模式蕾各、其中Model指的是數(shù)據(jù)邏輯和實(shí)體模型;View指的是布局文件庆揪、Controllor指的是Activity式曲。對于很多Android初學(xué)者可能會有這樣的經(jīng)歷,寫代碼的時(shí)候缸榛,不管三七二十一都往Activity中寫吝羞,當(dāng)然我當(dāng)初也是這么干的,根本就沒有什么框架的概念内颗,只要能實(shí)現(xiàn)某一個(gè)功能就很開心了钧排,沒有管這么多。當(dāng)然項(xiàng)目比較小還好均澳,一旦項(xiàng)目比較大恨溜,你會發(fā)現(xiàn),Activity所承擔(dān)的任務(wù)其實(shí)是很重的负懦,它既要負(fù)責(zé)頁面的展示和交互筒捺,還得負(fù)責(zé)數(shù)據(jù)的請求和業(yè)務(wù)邏輯之類的工作,相當(dāng)于既要打理家庭纸厉,又要教育自己調(diào)皮的孩子,真是又當(dāng)?shù)之?dāng)媽五嫂。颗品。肯尺。那該怎么辦呢?這時(shí)候Presenter這個(gè)繼父來到了這個(gè)家庭躯枢。Presenter對Activity說则吟,我來了,以后你就別這么辛苦了锄蹂,你就好好打理好View這個(gè)家氓仲,我專門來負(fù)責(zé)教育Model這孩子,有什么情況我會向你反映的得糜。這時(shí)Activity流下了幸福的眼淚敬扛,從此,Model朝抖、View(Activity)啥箭、Presenter一家三口過上了幸福的生活。治宣。急侥。好了磕個(gè)藥繼續(xù),由于Presenter(我們自己建的類)的出現(xiàn)侮邀,可以使View(Activity)不用直接和Model打交道坏怪,View(Activity)只用負(fù)責(zé)頁面的顯示和交互,剩下的和Model交互的事情都交給Presenter做绊茧,比如一些網(wǎng)絡(luò)請求铝宵、數(shù)據(jù)的獲取等,當(dāng)Presenter獲取到數(shù)據(jù)后再交給View(Activity)進(jìn)行展示按傅,這樣捉超,Activity的任務(wù)就大大減小了。這便是MVP(Model 還是指的數(shù)據(jù)邏輯和實(shí)體模型唯绍,View指的是Activity拼岳,P就是Presenter)框架的工作方式。
2.什么是Retrofit况芒?
接下來我們看一下什么是Retrofit惜纸。在官網(wǎng)對Retrofit的描述是這樣的
A type-safe HTTP client for Android and Java說人話就是“一個(gè)類型安全的用于Android和Java網(wǎng)絡(luò)請求的客戶端”,其實(shí)就是一個(gè)封裝好的網(wǎng)絡(luò)請求庫绝骚。接下來就來看一下這個(gè)庫該怎么用耐版。首先我在網(wǎng)上找了一個(gè)API接口用于測試:https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1這是一個(gè)用于查詢一本書詳細(xì)信息的一個(gè)請求接口。如果直接用瀏覽器打開的話會返回以下內(nèi)容:
瀏覽器中返回內(nèi)容
接下來我們來看看如何用Retrofit將上面的請求下來压汪。為了在Android Studio中添加Retrofit庫粪牲,我們需要添加如下依賴:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
好了,添加完該庫止剖,我們再來看看如何使用腺阳,首先我們來建一個(gè)實(shí)體類Book落君,用于裝網(wǎng)絡(luò)請求后返回的數(shù)據(jù)。這里順帶說一下亭引,有的人建一個(gè)實(shí)體類時(shí)可能會根據(jù)瀏覽器中返回中的數(shù)據(jù)一行一行敲绎速,其實(shí)這樣非常麻煩,這里教大家一個(gè)簡單的方法焙蚓,瞬間生成一個(gè)實(shí)體類纹冤。沒錯(cuò)有的人可能用過,我們需要一個(gè)插件GsonFormat购公。它的使用也很簡單萌京,首先需要在Android Studio中下載,點(diǎn)擊左上角菜單欄中的File,然后點(diǎn)擊Settings君丁,在彈窗中選擇Plugins,然后點(diǎn)擊下方的Browse repositories...
然后在新打開的窗口中搜索GsonFormat枫夺,點(diǎn)擊右側(cè)綠色按鈕就可以下載安裝了,安裝完需要重啟下studio绘闷,就可以用了橡庞。
它的用法也很簡單,比如你先建立一個(gè)新的空類取名Book印蔗,然后在里面按Alt+insert,會有個(gè)小彈窗選擇GsonFormat,之后在彈出的編輯框中拷入在瀏覽器中請求下來的那一坨東西扒最,然后一直點(diǎn)ok就會自動(dòng)生成字段,以及set和get方法华嘹,一會兒我們用Retrofit請求下來的數(shù)據(jù)都會保存在這個(gè)實(shí)體類中吧趣,還是挺方便的。最后我們里面添加一個(gè)toString()方法耙厚,用于后面顯示方便强挫。
接下來,回到我們的Retrofit中上薛躬,實(shí)體類已經(jīng)建好了俯渤,我們來看看這個(gè)Retrofit如何進(jìn)行網(wǎng)絡(luò)請求,其實(shí)代碼也很簡單型宝。首先我們需要定義一個(gè)接口八匠,取名RetrofitService :
public interface RetrofitService {
@GET("book/search")
Observable getSearchBooks(@Query("q") String name,
@Query("tag") String tag,@Query("start")intstart,
@Query("count")intcount);
}
額。趴酣。想必有人要問了梨树,這是什么玩意?跟我們平時(shí)定義的接口類很像岖寞,但又不一樣抡四。別心急,我來一一解釋下仗谆,和別的接口類一樣床嫌,我們在其中定義了一個(gè)方法getSearchBook跨释,那么這個(gè)方法是做什么的呢胸私?其實(shí)它干的事很簡單厌处,就是拼接一個(gè)URL然后進(jìn)行網(wǎng)絡(luò)請求。這里我們拼接的URL就是上文提到的測試URL:https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1岁疼。聰明的你一定看出來了阔涉,在這個(gè)URL中book/search就是GET后的值,而?后的q捷绒、tag瑰排、start、count等入?yún)⒕褪沁@個(gè)方法的入?yún)⑴取S械呐笥芽赡芤獑柫送肿。琱ttps://api.douban.com/v2/這么一大串跑哪去了?其實(shí)我們在進(jìn)行網(wǎng)絡(luò)請求時(shí)字逗,在URL中前一部分是相對不變的京郑。什么意思呢,比如你打開間書網(wǎng)站葫掉,在間書中你打開不同的網(wǎng)頁些举,雖然它的URL不同,但你會發(fā)現(xiàn)俭厚,每個(gè)URL前面都是以http://www.reibang.com/開頭,我們把這個(gè)不變的部分户魏,也叫做baseUrl提出來,放到另一個(gè)地方,在下面我們會提到挪挤。這樣我們一個(gè)完整的URL就拼接好了叼丑。在方法的開頭我們可以看到有個(gè)GET的注釋,說明這個(gè)請求是GET方法扛门,當(dāng)然你也可以根據(jù)具體需要用POST鸠信、PUT、DELETE以及HEAD尖飞。他們的區(qū)別如下:
GET ----------查找資源(查)
POST --------修改資源(改)
PUT ----------上傳文件(增)
DELETE ----刪除文件(刪)
HEAD--------只請求頁面的首部
然后我們來看一下這個(gè)方法的返回值症副,它返回Call實(shí)體,一會我們要用它進(jìn)行具體的網(wǎng)絡(luò)請求政基,我們需要為它指定泛型為Book也就是我們數(shù)據(jù)的實(shí)體類贞铣。接下來,你會發(fā)現(xiàn)這個(gè)方法的入?yún)⒑臀覀兤綍r(shí)方法的入?yún)⑦€不大一樣沮明。在每個(gè)入?yún)⑶斑€多了一個(gè)注解辕坝。比如第一個(gè)入?yún)Query("q") String name,Query表示把你傳入的字段拼接起來荐健,比如在測試url中我們可以看到q=金瓶梅的入?yún)⒔闯敲碤uery后面的值必須是q琳袄,要和url中保持不變,然后我們定義了String類型的name纺酸,當(dāng)調(diào)用這個(gè)方法是窖逗,用于傳入字符串餐蔬,比如可以傳入“金瓶梅”碎紊。那么這個(gè)方法就會自動(dòng)在q后面拼上這個(gè)字符串進(jìn)行網(wǎng)絡(luò)請求樊诺。以此類推仗考,這個(gè)url需要幾個(gè)入?yún)⒛憔驮谶@個(gè)方法中定義幾個(gè)入?yún)⒋逝溃總€(gè)入?yún)⑶岸家由螿uery注解秃嗜。當(dāng)然Retrofit除了Query這個(gè)注解外,還有其他幾個(gè)比如:@QueryMap顿膨、@Path锅锨、@Body虽惭、@FormUrlEncoded/@Field橡类、@Header/@Headers芽唇。我們來看一下他們的區(qū)別:
@Query(GET請求):
用于在url后拼接上參數(shù)顾画,例如:
@GET("book/search")
Call getSearchBook(@Query("q") String name);//name由調(diào)用者傳入
相當(dāng)于:
@GET("book/search?q=name")
Call getSearchBook();
@QueryMap(GET請求):
當(dāng)然如果入?yún)⒈容^多,就可以把它們都放在Map中匆笤,例如:
@GET("book/search")
Call getSearchBook(@QueryMapMap options);
@Path(GET請求):
用于替換url中某個(gè)字段研侣,例如:
@GET("group/{id}/users")
Call groupList(@Path("id") int groupId);
像這種請求接口,在group和user之間有個(gè)不確定的id值需要傳入庶诡,就可以這種方法。我們把待定的值字段用{}括起來咆课,當(dāng)然{}里的名字不一定就是id,可以任取书蚪,但需和@Path后括號里的名字一樣。如果在user后面還需要傳入?yún)?shù)的話殊校,就可以用Query拼接上晴玖,比如:
@GET("group/{id}/users")
Call groupList(@Path("id") int groupId,@Query("sort") String sort);
當(dāng)我們調(diào)用這個(gè)方法時(shí),假設(shè)我們groupId傳入1呕屎,sort傳入“2”,那么它拼接成的url就是group/1/users?sort=2秀睛,當(dāng)然最后請求的話還會加上前面的baseUrl尔当。
@Body(POST請求):
可以指定一個(gè)對象作為HTTP請求體,比如:
@POST("users/new")
Call createUser(@BodyUser user);
它會把我們傳入的User實(shí)體類轉(zhuǎn)換為用于傳輸?shù)腍TTP請求體琅催,進(jìn)行網(wǎng)絡(luò)請求居凶。
@Field(POST請求):
用于傳送表單數(shù)據(jù):
@FormUrlEncoded@POST("user/edit")
Call updateUser(@Field("first_name") String first,@Field("last_name") String last);
注意開頭必須多加上@FormUrlEncoded這句注釋藤抡,不然會報(bào)錯(cuò)。表單自然是有多組鍵值對組成缠黍,這里的first_name就是鍵,而具體傳入的first就是值啦药蜻。
@Header/@Headers(POST請求):
用于添加請求頭部:
@GET("user")
Call getUser(@Header("Authorization") String authorization)
表示將頭部Authorization屬性設(shè)置為你傳入的authorization;當(dāng)然你還可以用@Headers表示,作用是一樣的比如:
@Headers("Cache-Control: max-age=640000")
@GET("user")
Call getUser()
當(dāng)然你可以多個(gè)設(shè)置:
@Headers({"Accept: application/vnd.github.v3.full+json","User-Agent: Retrofit-Sample-App"})
@GET("user")
Call getUser()
好了语泽,這樣我們就把上面這個(gè)RetrofitService 接口類解釋的差不多了。我覺得踱卵,Retrofit最主要的也就是這個(gè)接口類的定義了。好了惋砂,有了這個(gè)接口類妒挎,我們來看一下西饵,到底如何使用這個(gè)我們定義的接口來進(jìn)行網(wǎng)絡(luò)請求酝掩。代碼如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.build();
RetrofitService service = retrofit.create(RetrofitService.class);
Call call =? service.getSearchBook("金瓶梅", null, 0, 1);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
text.setText(response.body()+"");
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
這里我們可以看到眷柔,先新建了一個(gè)Retrofit對象,然后給它設(shè)置一個(gè)我們前面說的baseUrl:https://api.douban.com/v2/.因?yàn)榻涌诜祷氐臄?shù)據(jù)不是我們需要的實(shí)體類驯嘱,我們需要調(diào)用addConverterFactory方法進(jìn)行轉(zhuǎn)換镶苞。由于返回的數(shù)據(jù)為json類型宙拉,所以在這個(gè)方法中傳入Gson轉(zhuǎn)換工廠GsonConverterFactory.create(new GsonBuilder().create())宾尚,這里我們需要在studio中添加Gson的依賴:
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
然后我們調(diào)用retrofit的create方法并傳入上面我們定義的接口的文件名RetrofitService.class,就可以得到RetrofitService 的實(shí)體對象。有了這個(gè)對象煌贴,我們就可以調(diào)用里面之前定義好的請求方法了御板。比如:
Callcall=? service.getSearchBook("金瓶梅",null,0,1);
它會返回一個(gè)Call實(shí)體類,然后就可以調(diào)用Call的enqueue方法進(jìn)行異步請求牛郑,在enqueue方法中傳入一個(gè)回調(diào)CallBack怠肋,重寫里面的onResponse和
onFailure方法,也就是請求成功和失敗的回調(diào)方法淹朋。當(dāng)成功時(shí)笙各,它會返回Response,里邊封裝了請求結(jié)果的所有信息础芍,包括報(bào)頭杈抢,返回碼,還有主體等仑性。比如調(diào)用它的body()方法就可獲得Book對象惶楼,也就是我們需要的數(shù)據(jù)。這里我們就把返回的Book诊杆,顯示屏幕上歼捐。如下圖:
Book中的數(shù)據(jù)
好了,到這里我們就基本了解了Retrofit的整個(gè)工作流程晨汹。
3.RxJava
我們這篇文章主要介紹搭建整體網(wǎng)絡(luò)請求框架豹储,所以關(guān)于RxJava的基礎(chǔ)知識淘这,我這就不再詳細(xì)介紹了,網(wǎng)上也有很多文章慨灭,對RxJava還不是很了解的同學(xué)朦乏,推薦你看一下拋物線的這篇文章給 Android 開發(fā)者的 RxJava 詳解
感興趣可以接著看下一篇:
http://www.reibang.com/p/d62fd00dfd76