聲明:原創(chuàng)作品,轉(zhuǎn)載請注明出處http://www.lxweimin.com/p/7b839b7c5884
之前公司的項目用到了MVP+Retrofit+RxJava的框架進行網(wǎng)絡(luò)請求,所以今天特此寫一篇文章以做總結(jié)。相信很多人都聽說過MVP、Retrofit、以及RxJava,有的人已經(jīng)開始用了,有的人可能還不知道這是什么,以及到底怎么用。不過沒關(guān)系,接下來我將為你一一揭開他們的神秘面紗,然后利用這三個家伙搭建一個網(wǎng)絡(luò)請求框架
1.什么是MVP?
MVP(Model View Presenter)其實就是一種項目的整體框架,能讓你的代碼變得更加簡潔,說起框架大家可能還會想到MVC、MVVM。由于篇幅原因,這里我們先不講MVVM,先來看一下MVC。其實Android本身就采用的是MVC(Model View Controllor)模式、其中Model指的是數(shù)據(jù)邏輯和實體模型;View指的是布局文件、Controllor指的是Activity。對于很多Android初學(xué)者可能會有這樣的經(jīng)歷,寫代碼的時候,不管三七二十一都往Activity中寫,當(dāng)然我當(dāng)初也是這么干的,根本就沒有什么框架的概念,只要能實現(xiàn)某一個功能就很開心了,沒有管這么多。當(dāng)然項目比較小還好,一旦項目比較大,你會發(fā)現(xiàn),Activity所承擔(dān)的任務(wù)其實是很重的,它既要負責(zé)頁面的展示和交互,還得負責(zé)數(shù)據(jù)的請求和業(yè)務(wù)邏輯之類的工作,相當(dāng)于既要打理家庭,又要教育自己調(diào)皮的孩子,真是又當(dāng)?shù)之?dāng)媽。。。那該怎么辦呢?這時候Presenter這個繼父來到了這個家庭。Presenter對Activity說,我來了,以后你就別這么辛苦了,你就好好打理好View這個家,我專門來負責(zé)教育Model這孩子,有什么情況我會向你反映的。這時Activity流下了幸福的眼淚,從此,Model、View(Activity)、Presenter一家三口過上了幸福的生活。。。好了磕個藥繼續(xù),由于Presenter(我們自己建的類)的出現(xiàn),可以使View(Activity)不用直接和Model打交道,View(Activity)只用負責(zé)頁面的顯示和交互,剩下的和Model交互的事情都交給Presenter做,比如一些網(wǎng)絡(luò)請求、數(shù)據(jù)的獲取等,當(dāng)Presenter獲取到數(shù)據(jù)后再交給View(Activity)進行展示,這樣,Activity的任務(wù)就大大減小了。這便是MVP(Model 還是指的數(shù)據(jù)邏輯和實體模型,View指的是Activity,P就是Presenter)框架的工作方式。
2.什么是Retrofit?
接下來我們看一下什么是Retrofit。在官網(wǎng)對Retrofit的描述是這樣的
A type-safe HTTP client for Android and Java
說人話就是“一個類型安全的用于Android和Java網(wǎng)絡(luò)請求的客戶端”,其實就是一個封裝好的網(wǎng)絡(luò)請求庫。接下來就來看一下這個庫該怎么用。首先我在網(wǎng)上找了一個API接口用于測試:https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1
這是一個用于查詢一本書詳細信息的一個請求接口。如果直接用瀏覽器打開的話會返回以下內(nèi)容:
接下來我們來看看如何用Retrofit將上面的請求下來。為了在Android Studio中添加Retrofit庫,我們需要添加如下依賴:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
好了,添加完該庫,我們再來看看如何使用,首先我們來建一個實體類Book,用于裝網(wǎng)絡(luò)請求后返回的數(shù)據(jù)。這里順帶說一下,有的人建一個實體類時可能會根據(jù)瀏覽器中返回中的數(shù)據(jù)一行一行敲,其實這樣非常麻煩,這里教大家一個簡單的方法,瞬間生成一個實體類。沒錯有的人可能用過,我們需要一個插件GsonFormat
。它的使用也很簡單,首先需要在Android Studio中下載,點擊左上角菜單欄中的File
,然后點擊Settings
,在彈窗中選擇Plugins
,然后點擊下方的Browse repositories...
然后在新打開的窗口中搜索GsonFormat
,點擊右側(cè)綠色按鈕就可以下載安裝了,安裝完需要重啟下studio,就可以用了。
它的用法也很簡單,比如你先建立一個新的空類取名Book,然后在里面按Alt+insert
,會有個小彈窗選擇GsonFormat
,之后在彈出的編輯框中拷入在瀏覽器中請求下來的那一坨東西,然后一直點ok就會自動生成字段,以及set和get方法,一會兒我們用Retrofit請求下來的數(shù)據(jù)都會保存在這個實體類中,還是挺方便的。最后我們里面添加一個toString()方法,用于后面顯示方便。
接下來,回到我們的Retrofit中上,實體類已經(jīng)建好了,我們來看看這個Retrofit如何進行網(wǎng)絡(luò)請求,其實代碼也很簡單。首先我們需要定義一個接口,取名RetrofitService :
public interface RetrofitService {
@GET("book/search")
Call<Book> getSearchBook(@Query("q") String name,
@Query("tag") String tag,
@Query("start") int start,
@Query("count") int count);
}
額。。想必有人要問了,這是什么玩意?跟我們平時定義的接口類很像,但又不一樣。別心急,我來一一解釋下,和別的接口類一樣,我們在其中定義了一個方法getSearchBook
,那么這個方法是做什么的呢?其實它干的事很簡單,就是拼接一個URL然后進行網(wǎng)絡(luò)請求。這里我們拼接的URL就是上文提到的測試URL:https://api.douban.com/v2/book/search?q=金瓶梅&tag=&start=0&count=1
。聰明的你一定看出來了,在這個URL中book/search就是GET后的值,而?后的q、tag、start、count等入?yún)⒕褪沁@個方法的入?yún)ⅰS械呐笥芽赡芤獑柫耍?code>https://api.douban.com/v2/這么一大串跑哪去了?其實我們在進行網(wǎng)絡(luò)請求時,在URL中前一部分是相對不變的。什么意思呢,比如你打開間書
網(wǎng)站,在間書
中你打開不同的網(wǎng)頁,雖然它的URL不同,但你會發(fā)現(xiàn),每個URL前面都是以http://www.lxweimin.com/
開頭,我們把這個不變的部分,也叫做baseUrl提出來,放到另一個地方,在下面我們會提到。這樣我們一個完整的URL就拼接好了。在方法的開頭我們可以看到有個GET的注釋,說明這個請求是GET方法,當(dāng)然你也可以根據(jù)具體需要用POST、PUT、DELETE以及HEAD。他們的區(qū)別如下:
- GET ----------查找資源(查)
- POST --------修改資源(改)
- PUT ----------上傳文件(增)
- DELETE ----刪除文件(刪)
- HEAD--------只請求頁面的首部
然后我們來看一下這個方法的返回值,它返回Call實體,一會我們要用它進行具體的網(wǎng)絡(luò)請求,我們需要為它指定泛型為Book也就是我們數(shù)據(jù)的實體類。接下來,你會發(fā)現(xiàn)這個方法的入?yún)⒑臀覀兤綍r方法的入?yún)⑦€不大一樣。在每個入?yún)⑶斑€多了一個注解。比如第一個入?yún)?code>@Query("q") String name,Query
表示把你傳入的字段拼接起來,比如在測試url中我們可以看到q=金瓶梅
的入?yún)ⅲ敲?code>Query后面的值必須是q,要和url中保持不變,然后我們定義了String類型的name,當(dāng)調(diào)用這個方法是,用于傳入字符串,比如可以傳入“金瓶梅”。那么這個方法就會自動在q后面拼上這個字符串進行網(wǎng)絡(luò)請求。以此類推,這個url需要幾個入?yún)⒛憔驮谶@個方法中定義幾個入?yún)ⅲ總€入?yún)⑶岸家由?code>Query注解。當(dāng)然Retrofit除了Query這個注解外,還有其他幾個比如:@QueryMap、@Path、@Body、@FormUrlEncoded/@Field、@Header/@Headers。我們來看一下他們的區(qū)別:
@Query(GET請求):
用于在url后拼接上參數(shù),例如:
@GET("book/search")
Call<Book> getSearchBook(@Query("q") String name);//name由調(diào)用者傳入
相當(dāng)于:
@GET("book/search?q=name")
Call<Book> getSearchBook();
@QueryMap(GET請求):
當(dāng)然如果入?yún)⒈容^多,就可以把它們都放在Map中,例如:
@GET("book/search")
Call<Book> getSearchBook(@QueryMap Map<String, String> options);
@Path(GET請求):
用于替換url中某個字段,例如:
@GET("group/{id}/users")
Call<Book> groupList(@Path("id") int groupId);
像這種請求接口,在group和user之間有個不確定的id值需要傳入,就可以這種方法。我們把待定的值字段用{}
括起來,當(dāng)然 {}
里的名字不一定就是id,可以任取,但需和@Path
后括號里的名字一樣。如果在user后面還需要傳入?yún)?shù)的話,就可以用Query拼接上,比如:
@GET("group/{id}/users")
Call<Book> groupList(@Path("id") int groupId,@Query("sort") String sort);
當(dāng)我們調(diào)用這個方法時,假設(shè)我們groupId傳入1,sort傳入“2”,那么它拼接成的url就是group/1/users?sort=2
,當(dāng)然最后請求的話還會加上前面的baseUrl。
@Body(POST請求):
可以指定一個對象作為HTTP請求體,比如:
@POST("users/new")
Call<User> createUser(@Body User user);
它會把我們傳入的User實體類轉(zhuǎn)換為用于傳輸?shù)腍TTP請求體,進行網(wǎng)絡(luò)請求。
@Field(POST請求):
用于傳送表單數(shù)據(jù):
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
注意開頭必須多加上@FormUrlEncoded
這句注釋,不然會報錯。表單自然是有多組鍵值對組成,這里的first_name就是鍵,而具體傳入的first就是值啦。
@Header/@Headers(POST請求):
用于添加請求頭部:
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
表示將頭部Authorization屬性設(shè)置為你傳入的authorization;當(dāng)然你還可以用@Headers表示,作用是一樣的比如:
@Headers("Cache-Control: max-age=640000")
@GET("user")
Call<User> getUser()
當(dāng)然你可以多個設(shè)置:
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("user")
Call<User> getUser()
好了,這樣我們就把上面這個RetrofitService 接口類解釋的差不多了。我覺得,Retrofit最主要的也就是這個接口類的定義了。好了,有了這個接口類,我們來看一下,到底如何使用這個我們定義的接口來進行網(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<Book> call = service.getSearchBook("金瓶梅", null, 0, 1);
call.enqueue(new Callback<Book>() {
@Override
public void onResponse(Call<Book> call, Response<Book> response) {
text.setText(response.body()+"");
}
@Override
public void onFailure(Call<Book> call, Throwable t) {
}
});
這里我們可以看到,先新建了一個Retrofit對象,然后給它設(shè)置一個我們前面說的baseUrlhttps://api.douban.com/v2/
.因為接口返回的數(shù)據(jù)不是我們需要的實體類,我們需要調(diào)用addConverterFactory方法進行轉(zhuǎn)換。由于返回的數(shù)據(jù)為json類型,所以在這個方法中傳入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 的實體對象。有了這個對象,我們就可以調(diào)用里面之前定義好的請求方法了。比如:
Call<Book> call = service.getSearchBook("金瓶梅", null, 0, 1);
它會返回一個Call實體類,然后就可以調(diào)用Call的enqueue方法進行異步請求,在enqueue方法中傳入一個回調(diào)CallBack,重寫里面的onResponse和
onFailure方法,也就是請求成功和失敗的回調(diào)方法。當(dāng)成功時,它會返回Response,里邊封裝了請求結(jié)果的所有信息,包括報頭,返回碼,還有主體等。比如調(diào)用它的body()方法就可獲得Book對象,也就是我們需要的數(shù)據(jù)。這里我們就把返回的Book,顯示屏幕上。如下圖:
好了,到這里我們就基本了解了Retrofit的整個工作流程。
3.RxJava
我們這篇文章主要介紹搭建整體網(wǎng)絡(luò)請求框架,所以關(guān)于RxJava的基礎(chǔ)知識,我這就不再詳細介紹了,網(wǎng)上也有很多文章,對RxJava還不是很了解的同學(xué),推薦你看一下扔物線的這篇文章給 Android 開發(fā)者的 RxJava 詳解
下面我們來看一下RxJava和retrofit的結(jié)合使用,為了使Rxjava與retrofit結(jié)合,我們需要在Retrofit對象建立的時候添加一句代碼addCallAdapterFactory(RxJavaCallAdapterFactory.create())
,當(dāng)然你還需要在build.gradle文件中添加如下依賴:
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
完整的代碼如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//支持RxJava
.build();
然后我們還需要修改RetrofitService 中的代碼:
public interface RetrofitService {
@GET("book/search")
Observable<Book> getSearchBook(@Query("q") String name,
@Query("tag") String tag, @Query("start") int start,
@Query("count") int count);
可以看到,在原來的RetrofitService 中我們把getSearchBook方法返回的類型Call改為了Observable,也就是被觀察者。其他都沒變。然后就是創(chuàng)建RetrofitService 實體類:
RetrofitService service = retrofit.create(RetrofitService.class);
和上面一樣,創(chuàng)建完RetrofitService ,就可以調(diào)用里面的方法了:
Observable<Book> observable = service.getSearchBook("金瓶梅", null, 0, 1);
其實這一步,就是創(chuàng)建了一個rxjava中observable,即被觀察者,有了被觀察者,就需要一個觀察者,且訂閱它:
observable.subscribeOn(Schedulers.io())//請求數(shù)據(jù)的事件發(fā)生在io線程
.observeOn(AndroidSchedulers.mainThread())//請求完成后在主線程更顯UI
.subscribe(new Observer<Book>() {//訂閱
@Override
public void onCompleted() {
//所有事件都完成,可以做些操作。。。
}
@Override
public void onError(Throwable e) {
e.printStackTrace(); //請求過程中發(fā)生錯誤
}
@Override
public void onNext(Book book) {//這里的book就是我們請求接口返回的實體類
}
}
在上面中我們可以看到,事件的消費在Android主線程,所以我們還要在build.gradle中添加如下依賴:
compile 'io.reactivex:rxandroid:1.2.0'
這樣我們就引入了RxAndroid,RxAndroid其實就是對RxJava的擴展。比如上面這個Android主線程在RxJava中就沒有,因此要使用的話就必須得引用RxAndroid。
4.實踐
接下來我們就看看,在一個項目中上面三者是如何配合的。我們打開Android Studio,新建一個項目取名為MVPDemo。這個demo的功能也很簡單,就是點擊按鈕調(diào)用上面的那個測試接口,將請求下來書的信息顯示在屏幕上。首先我們來看一下這個工程的目錄結(jié)構(gòu):
我們可以看到,在項目的包名下,我們建了三個主要的文件夾:app、service、ui。當(dāng)然根據(jù)項目的需要你也可以添加更多其他的文件夾,比如一些工具類等。其中app文件夾中可以建一個Application類,用于設(shè)置應(yīng)用全局的一些屬性,這里為了使項目更加簡單就沒有添加;然后,我們再來看看ui文件夾下,這個文件夾下主要放一些關(guān)于界面的東西。在里面我們又建了三個文件夾:activity、adapter、fragment,我想看名字你就清楚里面要放什么了。最后我們在重點看看service文件夾中的東西。首先我們來看看里面重要的兩個類:RetrofitHelper和RetrofitService。RetrofitHelper主要用于Retrofit的初始化:
public class RetrofitHelper {
private Context mCntext;
OkHttpClient client = new OkHttpClient();
GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder().create());
private static RetrofitHelper instance = null;
private Retrofit mRetrofit = null;
public static RetrofitHelper getInstance(Context context){
if (instance == null){
instance = new RetrofitHelper(context);
}
return instance;
}
private RetrofitHelper(Context mContext){
mCntext = mContext;
init();
}
private void init() {
resetApp();
}
private void resetApp() {
mRetrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.client(client)
.addConverterFactory(factory)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public RetrofitService getServer(){
return mRetrofit.create(RetrofitService.class);
}
}
代碼并不復(fù)雜,其中resetApp方法,就是前面介紹的Retrofit的創(chuàng)建,getServer方法就是為了獲取RetrofitService接口類的實例化。然后定義了一個靜態(tài)方法getInstance用于獲取自身RetrofitHelper的實例化,并且只會實例化一次。
接下來,看一下RetrofitService,其中代碼還是上面一樣:
public interface RetrofitService {
@GET("book/search")
Observable<Book> getSearchBooks(@Query("q") String name,
@Query("tag") String tag, @Query("start") int start,
@Query("count") int count);
}
然后我們依次來看一下service文件夾下的四個文件夾:entity、manager、presenter和view。其中entity下放我們請求的實體類,這里就是Book。接下來我們來看一下manager中DataManager。這個類其實就是為了讓你更方便的調(diào)用RetrofitService 中定義的方法:
public class DataManager {
private RetrofitService mRetrofitService;
public DataManager(Context context){
this.mRetrofitService = RetrofitHelper.getInstance(context).getServer();
}
public Observable<Book> getSearchBooks(String name,String tag,int start,int count){
return mRetrofitService.getSearchBooks(name,tag,start,count);
}
}
可以看到,在它的構(gòu)造方法中,我們得到了RetrofitService 的實例化,然后定義了一個和RetrofitService 中同名的方法,里面其實就是調(diào)用RetrofitService 中的這個方法。這樣,把RetrofitService 中定義的方法都封裝到DataManager 中,以后無論在哪個要調(diào)用方法時直接在DataManager 中調(diào)用就可以了,而不是重復(fù)建立RetrofitService 的實例化,再調(diào)用其中的方法。
好了,我們再來看一下presenter和view,我們在前面說過,presenter主要用于網(wǎng)絡(luò)的請求以及數(shù)據(jù)的獲取,view就是將presenter獲取到的數(shù)據(jù)進行展示。首先我們先來看view,我們看到我們建了兩個接口類View和BookView,其中View是空的,主要用于和Android中的View區(qū)別開來:
public interface View {
}
然后讓BookView繼承自我們自己定義的View :
public interface BookView extends View {
void onSuccess(Book mBook);
void onError(String result);
}
可以看到在里面定義兩個方法,一個onSuccess,如果presenter請求成功,將向該方法傳入請求下來的實體類,也就是Book,view拿到這個數(shù)據(jù)實體類后,就可以進行關(guān)于這個數(shù)據(jù)的展示或其他的一些操作。如果請求失敗,就會向這個view傳入失敗信息,你可以彈個Toast來提示請求失敗。通常這兩個方法比較常用,當(dāng)然你可以根據(jù)項目需要來定義一些其他的方法。接下來我們看看presenter是如何進行網(wǎng)絡(luò)請求的 。我們也定義了一個基礎(chǔ)Presenter:
public interface Presenter {
void onCreate();
void onStart();//暫時沒用到
void onStop();
void pause();//暫時沒用到
void attachView(View view);
void attachIncomingIntent(Intent intent);//暫時沒用到
}
里面我們可以看到,定義了一些方法,前面幾個onCreate、onStart等方法對應(yīng)著Activity中生命周期的方法,當(dāng)然沒必要寫上Activity生命周期中所有回調(diào)方法,通常也就用到了onCreate和onStop,除非需求很復(fù)雜,在Activity不同生命周期請求的情況不同。接著我們定義了一個attachView方法,用于綁定我們定義的View。也就是,你想把請求下來的數(shù)據(jù)實體類給哪個View就傳入哪個View。下面這個attachIncomingIntent暫且沒用到,就不說了。好了,我們來看一下BookPresenter具體是怎么實現(xiàn)的:
public class BookPresenter implements Presenter {
private DataManager manager;
private CompositeSubscription mCompositeSubscription;
private Context mContext;
private BookView mBookView;
private Book mBook;
public BookPresenter (Context mContext){
this.mContext = mContext;
}
@Override
public void onCreate() {
manager = new DataManager(mContext);
mCompositeSubscription = new CompositeSubscription();
}
@Override
public void onStart() {
}
@Override
public void onStop() {
if (mCompositeSubscription.hasSubscriptions()){
mCompositeSubscription.unsubscribe();
}
}
@Override
public void pause() {
}
@Override
public void attachView(View view) {
mBookView = (BookView)view;
}
@Override
public void attachIncomingIntent(Intent intent) {
}
public void getSearchBooks(String name,String tag,int start,int count){
mCompositeSubscription.add(manager.getSearchBooks(name,tag,start,count)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Book>() {
@Override
public void onCompleted() {
if (mBook != null){
mBookView.onSuccess(mBook);
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
mBookView.onError("請求失敗!!");
}
@Override
public void onNext(Book book) {
mBook = book;
}
})
);
}
}
BookPresenter實現(xiàn)了我們定義的基礎(chǔ)Presenter,在onCreate中我們創(chuàng)建了DataManager的實體類,便于調(diào)用RetrofitService中的方法,還新建了一個CompositeSubscription對象,CompositeSubscription是用來存放RxJava中的訂閱關(guān)系的。注意請求完數(shù)據(jù)要及時清掉這個訂閱關(guān)系,不然會發(fā)生內(nèi)存泄漏。可在onStop中通過調(diào)用CompositeSubscription的unsubscribe方法來取消這個訂閱關(guān)系,不過一旦調(diào)用這個方法,那么這個CompositeSubscription也就無法再用了,要想再用只能重新new一個。然后我們可以看到在attachView中,我們把BookView傳進去。也就是說我們要把請求下來的實體類交給BookView來處理。接下來我們定義了一個方法getSearchBooks,名字和入?yún)⒍己驼埱蠼涌赗etrofitService中的方法相同。這里的這個方法也就是請求的具體實現(xiàn)過程。其實也很簡單,就是向CompositeSubscription添加一個訂閱關(guān)系。上面我們已經(jīng)說過manager.getSearchBooks就是調(diào)用RetrofitService的getSearchBooks方法,而這個方法返回的是一個泛型為Book的Observable,即被觀察者,然后通過subscribeOn(Schedulers.io())來定義請求事件發(fā)生在io線程,然后通過observeOn(AndroidSchedulers.mainThread())來定義事件在主線程消費,即在主線程進行數(shù)據(jù)的處理,最后通過subscribe使觀察者訂閱它。在觀察者中有三個方法:onNext、onCompleted、onError。當(dāng)請求成功話,就會調(diào)用onNext,并傳入請求返回的Book實體類,我們在onNext中,把請求下來的Book實體類存到內(nèi)存中,當(dāng)請求結(jié)束后會調(diào)用onCompleted,我們把請求下來的Book實體類交給BookView處理就可以了,如果請求失敗,那么不會調(diào)用onCompleted而調(diào)用onError,這樣我們可以向BookView傳遞錯誤消息。
好了,這樣我們我們就可以調(diào)用這個接口方法來進行網(wǎng)絡(luò)的請求了,我們先寫一下頁面的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:orientation="vertical"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:id="@+id/button"
android:onClick="getFollowers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="請求"/>
</LinearLayout>
界面很簡單,一共兩個控件,一個Button,點擊時進行網(wǎng)絡(luò)請求,一個TextView,用于顯示請求下來的數(shù)據(jù)。然后我么看一下Activity中代碼:
public class MainActivity extends AppCompatActivity {
private TextView text;
private Button button;
private BookPresenter mBookPresenter = new BookPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBookPresenter.getSearchBooks("金瓶梅", null, 0, 1);
}
});
mBookPresenter.onCreate();
mBookPresenter.attachView(mBookView);
}
private BookView mBookView = new BookView() {
@Override
public void onSuccess(Book mBook) {
text.setText(mBook.toString());
}
@Override
public void onError(String result) {
Toast.makeText(MainActivity.this,result, Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onDestroy(){
super.onDestroy();
mBookPresenter.onStop();
}
}
邏輯并不復(fù)雜,我們先創(chuàng)建了一個BookPresenter 對象,然后調(diào)用它的onCreate方法進行初始化,接著調(diào)用attachView來綁定BookView。BookView的實現(xiàn)也很簡單,在onSuccess方法中將Book 中內(nèi)容顯示在TextView上,在onError中彈出一個Toast提示。然后點擊按鈕的時候就調(diào)用BookPresenter中g(shù)etSearchBooks方法,同時傳入必要的入?yún)ⅰ_@樣網(wǎng)絡(luò)請求就開始了,如果請求成功就會回調(diào)BookView 中的onSuccess方法,失敗就回調(diào)onError方法。當(dāng)活動銷毀時記得調(diào)用BookPresenter的onStop方法來釋放訂閱關(guān)系,防止內(nèi)存泄漏。
最后別忘了在AndroidManifest中添加網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
好了,我們運行一下看一下效果:
代碼已上傳github:MVPDemo