先扯兩句
隔了這么長時間,深深的負罪感終于督促我寫下了這篇博客,當然,之前的時間也不全是在玩,參加了一個面試,進行了我人生中的第一次霸面,對方有個要求就是完成他們的demo,就可以得到面試機會,結果我完成以后單純的就去了,再然后就沒有然后了。
不過至少在這次demo中,我的基本框架得到了應用,還是讓自己很欣慰的,另外還使用了一些博客后續將要與大家分享的內容,算是一次提前的實戰吧,效果自認為還算滿意。
如果關注我博客的大家應該知道,我前段時間寫的就是Retrofit的內容,所以今天就從Retrofit的封裝開始寫起吧。
閑言少敘,老規矩還是先上我的Git庫,然后開始正文。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)
另外,也把我做的這個小demo也發到的了庫中,也沒太深的東西內容,大家如果感興趣的話也而已去看看:
https://github.com/BanShouWeng/IYuBaTestApplication
Retrofit的Header封裝部分放在了《一個Android工程的從零開始》階段總結與修改1-base,如有需要的朋友可以去其中查看。
正文
關于Retrofit的相關內容呢,如果大家有什么不太清楚的地方,可以看一下我的上一篇博客Android開發相關——Retrofit+RxJava+OkHttp(下)使用,或許有人會疑問,為什么給了下,沒給上,主要還是因為下才是使用,上具體是什么,好奇的也可以去看一下Android開發相關——Retrofit+RxJava+OkHttp(上)閑扯,雖然我估計有一部分人,看到“閑扯”二字,或許就沒興趣了,不過剛步入android世界中的大家還是可以去看看的,或許會有收獲也說不定。
下面就正是開始封裝的部分:
分析
其是封裝,說白了就是為了我們在運用的時候能夠更方便,從我個人的角度出發還是兩個字——偷懶!
從《Android開發相關——Retrofit+RxJava+OkHttp(下)使用》中,大家或許也知道了,Retrofit網絡訪問框架需要的東西:
- 訪問數據回調接口
- 訪問方法
所以想要封裝,我們需要做的事,自然就是從這兩大塊入手,看看其中有哪些是可以直接復用的內容,而這些內容就是我們可以拿來偷懶的點:
訪問數據回調接口
public interface Movie2Service {
@GET("top250")
Observable<ResponseBody> getTop250(@Query("start") int start, @Query("count")int count);
}
從上面的接口中,我們可以看得出來,其中我們可以操作的點,有三個:
- 尾址
- 參數
- 返回數據model(也就是Bean對象)
當然,如果你一定要說接口名也算的話,我也不反對。至少,在我一會說的“封裝方法一”中是不會反對的,具體為什么,我這里先賣個關子,一會再聊。而這三個可操作的點呢,在“封裝方法二”中才會用到,所以依然賣個關子,我們一會再聊,大家只需要暫時知道這三個點一會可以操作就行。
訪問方法
private void getMovie2() {
String baseUrl = "https://api.douban.com/v2/movie/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
Movie2Service movieService = retrofit.create(Movie2Service.class);
movieService.getTopMovie(10, 10)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
String responseString = responseBody.string();
Log.i("responseString", responseString);
LogUtil.info("response", responseString);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
這個訪問方法中,可以修改的點,同樣顯而易見:
- baseUrl
- 結果回調,也就是subscribe傳入的接口
至于其他的部分,比如解析方式是json還是xml啊,是否使用RxJava啊,又或者線程的方式之類的參數自然也可以動態設置,只是對于一個工程項目而言,除非十分必要的情況下,考慮到開發難度以及開發周期等等諸多因素,很少會故意難為開發人員,而是采用一種萬能的模式即可,所以這里我就將這些因素都忽略掉了,如果你真的遇到一個這么變態的產品,我只能在這里為你默默祈福了。
而這兩部分中,baseUrl也算是比較特殊的存在,一般情況下,比較小的項目中基本只適用一個baseUrl就可以結束戰斗,哪怕大的項目,最多也就是每個模塊一個baseUrl,如果再大的,暫時還沒接觸到,但是想來也不會多多少。畢竟涉及到域名、端口等等問題,當然,在我看來這些都不是原因,主要還是后臺的戰友們,也懶??!
好吧,以上都是玩笑話,不過baseUrl很少有在網絡封裝的方法中體現的,無腦一點的方法就是封裝Utils包下的Const類中的靜態常量中,而相對正式點的玩法則是封裝到app mudule的build.grade中,并且可以去BuildConfig文件中查找,具體的玩法說來也簡單,不過誰讓我懶,還是直接上代碼以及BuildConfig目錄位置。
build.grade配置信息
signingConfigs {
debug {
storeFile file("./bsw.keystore")
storePassword "bswbsw"
keyAlias "wsbsw"
keyPassword "wsbswhhh"
}
release {
storeFile file("./bsw.keystore")
storePassword "bswbsw"
keyAlias "wsbsw"
keyPassword "wsbswhhh"
}
}
buildTypes {
release {
debug{
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "String", "BASE_URL_NAME", "\"baseUrl地址\""
signingConfig signingConfigs.debug
}
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "String", "BASE_URL_NAME", "\"baseUrl地址\""
signingConfig signingConfigs.release
}
}
BuildConfig位置
BuildConfig內信息
package com.banshouweng.mybaseapplication;
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.banshouweng.mybaseapplication";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: debug
public static final String BASE_URL_NAME = "baseUrl地址";
}
// Fields from build type: debug下的內容是我們自行添加的信息
build.grade配置信息
其中minifyEnabled 與proguardFiles 是在創建項目的時候自帶的,分別代表是否混淆,而proguardFiles 太專業了,我也說不明白,大家找自己看一下吧
proguardFiles這部分有兩段,前一部分代表系統默認的android程序的混淆文件,該文件已經包含了基本的混淆聲明,免去了我們很多事,這個文件的目錄在 /tools/proguard/proguard-android.txt , 后一部分是我們項目里的自定義的混淆文件,目錄就在 app/proguard-rules.txt , 如果你用Studio 1.0創建的新項目默認生成的文件名是 proguard-rules.pro , 這個名字沒關系,在這個文件里你可以聲明一些第三方依賴的一些混淆規則。
而其他部分則是我們都需要添加的了,buildConfigField中就是我們要添加的baseUrl,格式已經給出,但是需要切記的一點是,這里添加的都是字符串,例如:
//輸入
buildConfigField "String", "BASE_URL_NAME", "\"baseUrl地址\""
buildConfigField "int", "COUNT", "0"
buildConfigField "boolean", "ISOK", "true"
//顯示
// Fields from build type: debug
public static final String BASE_URL_NAME = "baseUrl地址";
public static final boolean ISOK = true;
public static final int COUNT = 0;
//引用
String url = DebugConfig.BASE_URL_NAME;
int count = DebugConfig.COUNT;
boolean isOk= DebugConfig.ISOK;
可以看得出來,如果我們賦值的內容都需要傳遞的是字符串,顯示的才是我們需要的內容,或者說AS用的是更無腦的玩法,那就是去掉最外層的引號,所以當創建String類型的參數時,需要使用的是""baseUrl地址""的形式(加粗斜體的所有部分),,如果只加一層引號的話,就會出現如下效果:
當然,如果我們將int或boolean的引號去掉,又會是另一個效果:
ssigningConfig signingConfigs.release這行就是以上的內容,將會在何時觸發加載到BuildConfig文件中,我這里是分兩種情況:1、debug(自行調試的時候);2、release(發行的時候)。
而ssigningConfig 中則是對應兩種情況的簽名信息:
- storeFile file("./bsw.keystore"):簽名文件位置(../xxx.keystore(或者xxx.jks))
- storePassword "bswbsw"簽名文件密碼
- keyAlias "wsbsw" 簽名別名
- keyPassword "wsbswhhh" 別名密碼
這些全都配置好,我們點擊Sync Now的時候,編譯結束,在BuildConfig中才會有我們要的內容,如果只添加了release而不添加debug,那么就只有在發行包中才會在BuildConfig文件下生成對應靜態常量,而我們平時開所處的環境是debug狀態,所以這個時候,是我們是無法在BuildConfig中看到對應的靜態常量的,所以開發時也找不到,調試時也用也會報錯,所以開發時一定要對應添加debug和release才可以
關于build.gradle,文件自然還有許多其他的妙用,這里就先不列舉了,我們還是回歸到正文,也就是說,baseUrl通過這上述的Const或者DebugConfig這兩種方法集成即可,就不需要額外花時間了。
所以重頭戲也就都在結果回調的接口上了。
封裝方法1——最無腦的封裝
看了這哥標題一定會有人問,什么叫最無腦的封裝,很好理解啊,那就是封裝起來特別簡單,用起來,相當麻煩。又有人會問,既然麻煩為什么還要去這么封裝,其實很簡單,這種封裝的用途就是為了應付那些腦洞打開的產品的,萬一他們真想出來什么喪心病狂的需求,我們還無法不去完成的情況下,自然就需要用這種麻煩的封裝了。
至于為什么不直接使用Retrofit,一定還要封裝一下,自然是萬一產品下次又爆發了一個相類似的腦洞的情況下,我們可以稍做修改,便能直接拿來用,一旦直接使用Retrofit了,下次還得重新寫,或者再去找之前加到哪里了,麻煩!
而這種封裝所需要的包(對包的部分不太清楚的參見《一個Android工程的從零開始》-1前期準備)便是apimagager以及service。
service自不必說,看名字也能知道,它肯定是存放回調接口的,而回調接口我們自然也能加一定的處理,從Retrofit官網中,我們可以發現其中有兩種玩法很有趣,可以拿來用一下,我們先來看一下效果:
public interface Movie2Service {
@GET("{action}")
Observable<ResponseBody> getTopMovie(@Path("action") String action, @QueryMap Map<String, String> params);
}
顯而易見,就是@Path以及@QueryMap,先說@QueryMap,其實對比一下之前的@Query就會發現,它只不過是在后面加了一個Map,至于功能,還是傳遞的參數,只不過原本的玩法需要傳遞一個名字為name的String參數John,那就要先定義一個String變量,命名name,然后賦值。這樣原本來說也不麻煩,可是一旦讓傳遞大量參數的時候,就顯得有些不便了,所以就使用了Map去傳遞,同樣的傳遞John,只需要一個map.put("name", "john");即可,大量數據的時候,繼續put就好,什么時候都put好了,將這個map傳遞進來就達到了目的。
接下來就是@Path,先看看官方的說法:
A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is an alphanumeric string surrounded by { and }. A corresponding parameter must be annotated with @Path using the same string.
嗯,除了看不懂沒有別的缺點了!所以我也不打算逐字去翻譯了,其實看例子也能看出來,這就是一個占位符,用“{占位符名}”站好位,然后@Path("占位符名")按照其中的占位符名,將傳遞進來的內容內容放置在對應的占位符名所占的位置上。所以傳進來的action就會被當做我們的尾址來使用。而我們的返回Bean則依照要求取就好,我這里是使用的OKHttp的ResponseBody。如此,Service就完成了。
apimagager,這部分就完整看產品的需求了,基本框架在上面,自行添加修改一下就好,當然返回值是肯定需要處理一下的,不過這部分我會在下一個封裝方法中詳細說明,這里只管調用即可。
封裝方法2
這次沒給額外的說明,也是我最后的一種封裝方法,就是打算弄一個一招鮮吃遍天的玩法,雖然封裝起來會麻煩許多,但是有點也很明顯,用著方便,至于方便什么,必然是方便偷懶了。
先說說我給這個封裝方法找的位置,作為一個懶人,雖然這個方法也可以放置在ApiMageger中,但是調用的時候,竟然還得讓我new個對象,有這時間我給自己new個女朋友好不?;蛘哒f是用靜態方法,不過說實話,靜態的方法或者常量變量還是盡量減少使用,畢竟靜態內存也不富裕,再說不考慮這個,我們不還是需要ApiMageger.getXXX嗎。有這時間,我研究要追那個妹子好不。
總之,在偷懶心理作祟下,我選擇了將這個網絡訪問的封裝放置在了BaseActivity以及BaseFragment中,用的時候直接掉方法即可。
不過如果遇到個別的需要在封裝的Adapter中掉訪問網絡的方法時就比較尷尬,需要多費些周折,例如發個EvenBus之類的,說起來也不算麻煩。
具體是封裝在APIManager中還是封裝在BaseActivity/BaseFragment就看你個人的需求情況,我下面的內容就先按照封裝在BaseActivity/BaseFragment進行,相信提出來放在APIManager的操作,對大家來說還是小菜一碟的。
數據回調1
//網絡訪問
public <T extends BaseBean> void get1(final String action, final Class<T> cls) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
GetService1 getService = retrofit.create(GetService1.class);
if (params == null) {
params = new HashMap<>();
}
getService.get(action, params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
T t = new Gson().fromJson(responseBody.string(), cls);
success(action, t);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
error(action, e);
}
@Override
public void onComplete() {
}
});
}
//成功方法
public void success(String action, BaseBean baseBean) {
}
//失敗方法
public void error(String action, Throwable e) {
}
在上述方法之前,先創建和一個共有的Map類進行傳參,由于是繼承的我們的BaseActivity/BaseFragment,所以這里不需要我們傳遞Map參數了,直接調用即可。因為這里我們要使用一個萬能的封裝法,很遺憾的是,在使用Retrofit的時候,直接使用泛型T傳遞的時候,會報錯Observer< T >不能直接使用,所以我暫時的解決方法只能是傳遞OkHttp中的ResponseBody類,然后通過string()方法獲取其中的json串,再通過GSON解析成我們需要的對應類。并將其傳遞到success方法中,在訪問的Activity重寫該方法即可,至于為什么要將action也傳遞回來,主要是在進行多次網絡請求的時候,用來分辨對應區分是誰發出的請求,隨后做出對應的處理。
不過這個方法的缺點就是:1、我們需要額外重寫成功或者失敗的方法,還是有點麻煩;2、success方法中的參數是BaseBean(參見附錄1),而不是我們想要生成的Bean對象,還需要強轉一下。
當然以上屬于吹毛求疵,不過為了偷懶,所以我就想到了下面的方法2。
封裝方法2
這次沒給額外的說明,也是我最后的一種封裝方法,就是打算弄一個一招鮮吃遍天的玩法,雖然封裝起來會麻煩許多,但是有點也很明顯,用著方便,至于方便什么,必然是方便偷懶了。
先說說我給這個封裝方法找的位置,作為一個懶人,雖然這個方法也可以放置在ApiMageger中,但是調用的時候,竟然還得讓我new個對象,有這時間我給自己new個女朋友好不。或者說是用靜態方法,不過說實話,靜態的方法或者常量變量還是盡量減少使用,畢竟靜態內存也不富裕,再說不考慮這個,我們不還是需要ApiMageger.getXXX嗎。有這時間,我研究要追那個妹子好不。
總之,在偷懶心理作祟下,我選擇了將這個網絡訪問的封裝放置在了BaseNetActivity以及BaseNetFragment中,用的時候直接掉方法即可。
不過如果遇到個別的需要在封裝的Adapter中掉訪問網絡的方法時就比較尷尬,需要多費些周折,例如發個EvenBus之類的,說起來也不算麻煩。
具體是封裝在APIManager中還是封裝在BaseNetActivity/BaseNetFragment就看你個人的需求情況,我下面的內容就先按照封裝在BaseNetActivity/BaseNetFragment進行,相信提出來放在APIManager的操作,對大家來說還是小菜一碟的。
對于封裝,第一件事就是需要我們創建一個BaseNetActivity/BaseNetFragment,至于為什么沒有按照本篇博客修改之前放到BaseActivity/BaseFragment中,主要是有一些界面還是不需要網絡訪問的,雖然少一些,但是集成這些東西還是很好資源的,另外就是之前我們的BaseActivity中已經放了很多內容,如果網絡訪問的內容也放到其中難免有寫太冗雜了,所以網絡訪問的部分就單獨拿出來了一個BaseNetActivity/BaseNetFragment。當然,與之前的Activity/Fragment不同的是,BaseNetActivity/BaseNetFragment不需要我們關聯布局文件,因為它的工作只是網絡訪問而已,BaseNetActivity/BaseNetFragment繼承BaseActivity/BaseFragment這樣就可以使用到網絡與布局的雙封裝了。
看了前面的封裝方法一,很顯然,它的作用就是對應每一個創建對應Service,既然如此,大家一定猜到了,這部分的封裝就是一個通用的方法了,也就是無論你想要用什么樣子的Bean都隨你心情。
先上Service代碼:
public interface RetrofitGetService {
@GET("{action}")
Observable<ResponseBody> getResult(@Path("action") String action, @QueryMap Map<String, String> params);
}
大家可以看得出來,這個部分與我們上面的方法一舉例用的是完全相同的,而為什么值用ResponseBody,主要還是為了其提供的string()方法可以獲得JSON串,方便我們自行轉換。
再就是創建初始化retrofit的方法:
private void initBaseData() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(builder.build())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
其中OkHttpClient的作用就是connectTimeout方法,用于設置5s鏈接超時的處理。
隨后就是結果回調的接口,用于將得到的結果傳回去 ,這部分其實用單純的方法也可以,在Activity中重寫就可以得到結果參數,可是懶人我實在懶得去記需要重寫的方法名,用接口的就可以很好的回避掉這點了。
public interface ResultCallBack<T extends BaseBean> {
void success(String action, T t);
void error(String action, Throwable e);
}
萬事俱備,下面就該進行正式的封裝部分了
/**
* Get請求
*
* @param action 請求接口的尾址,如“top250”
* @param clazz 要轉換的Bean類型(需繼承BaseBean)
* @param callBack 結果回調接口
* @param <T> 用于繼承BaseBean的占位變量
*/
public <T extends BaseBean> void get(final String action, final Class<T> clazz, final ResultCallBack callBack) {
if (getService == null) {
getService = retrofit.create(RetrofitGetService.class);
}
if (params == null) {
params = new HashMap<>();
}
getService.getResult(action, params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
callBack.success(action, new Gson().fromJson(responseBody.string(), clazz));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.error(action, e);
}
@Override
public void onComplete() {
}
});
}
/**
* Post請求
*
* @param action 請求接口的尾址,如“top250”
* @param clazz 要轉換的Bean類型(需繼承BaseBean)
* @param callBack 結果回調接口
* @param <T> 用于繼承BaseBean的占位變量
*/
public <T extends BaseBean> void post(final String action, final Class<T> clazz, final ResultCallBack callBack) {
if (postService == null) {
postService = retrofit.create(RetrofitPostService.class);
}
if (params == null) {
params = new HashMap<>();
}
postService.postResult(action, params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
callBack.success(action, new Gson().fromJson(responseBody.string(), clazz));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.error(action, e);
}
@Override
public void onComplete() {
}
});
}
可以看到,JSON轉換成Bean使用的是Gson解析,其余沒有變化,如果有一些各個接口都需要添加的參數,就可以對應的方法中直接添加,以免重復操作。
疑問##
雖然我的封裝達到了目的,可是Retrofit本身不應該出現前面出現的問題,我這里也有一個疑問,究竟是我還沒有找到Retrofit的正確使用方式,還是Retrofit自身卻是存在這個漏洞,也希望大家有所發現能為我指點迷津,在此先謝過各位了。BaseNetActivity完整代碼參見附錄2
附錄
附錄1
BaseBean:
其實也沒什么特殊的部分,其中都是一些基礎的部分,比如網絡訪問是否成功之類的處理,具體大家可以參考各種errorCode,就比如打擊熟悉的404就是其中的一種,當然,這個錯誤碼肯定與404不同,而是后臺定義的錯誤碼,常用的場合就是登錄時,用戶沒有注冊、賬戶密碼不正確之類的錯誤情況判斷。
再有一點就是,將BaseBean序列號,便于Activity與Activity之間,或者Activity與Fragment之間傳值。
public class BaseBean implements Serializable {}
這里實現序列號的方法是實現Serializable 接口,當然還有一種玩法就是實現Parcelable接口,至于兩種的區別,請參見Serializable 和 Parcelable 兩種序列化
附錄2
public class BaseNetActivity extends BaseActivity {
private String baseUrl = "https://api.douban.com/v2/movie/";
private RetrofitGetService getService;
private RetrofitPostService postService;
private Retrofit retrofit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initBaseData();
}
private void initBaseData() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(builder.build())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
/**
* Get請求
*
* @param action 請求接口的尾址,如“top250”
* @param clazz 要轉換的Bean類型(需繼承BaseBean)
* @param callBack 結果回調接口
* @param <T> 用于繼承BaseBean的占位變量
*/
public <T extends BaseBean> void get(final String action, final Class<T> clazz, final ResultCallBack callBack) {
if (getService == null) {
getService = retrofit.create(RetrofitGetService.class);
}
if (params == null) {
params = new HashMap<>();
}
params.put("start", "0");
params.put("count", "10");
getService.getResult(action, null, params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
String responseString = responseBody.string();
Log.i("responseString", "responseString get " + responseString);
callBack.success(action, new Gson().fromJson(responseBody.string(), clazz));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.error(action, e);
}
@Override
public void onComplete() {
}
});
}
/**
* Post請求
*
* @param action 請求接口的尾址,如“top250”
* @param clazz 要轉換的Bean類型(需繼承BaseBean)
* @param callBack 結果回調接口
* @param <T> 用于繼承BaseBean的占位變量
*/
public <T extends BaseBean> void post(final String action, final Class<T> clazz, final ResultCallBack callBack) {
if (postService == null) {
postService = retrofit.create(RetrofitPostService.class);
}
if (params == null) {
params = new HashMap<>();
}
params.put("start", "0");
params.put("count", "10");
postService.postResult(action, null, params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull ResponseBody responseBody) {
try {
String responseString = responseBody.string();
Log.i("responseString", "responseString post " + responseString);
callBack.success(action, new Gson().fromJson(responseBody.string(), clazz));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.error(action, e);
}
@Override
public void onComplete() {
}
});
}
public interface ResultCallBack<T extends BaseBean> {
void success(String action, T t);
void error(String action, Throwable e);
}
}