先扯两句
隔了这么长时间,深深的负罪感终于督促我写下了这篇博客,当然,之前的时间也不全是在玩,参加了一个面试,进行了我人生中的第一次霸面,对方有个要求就是完成他们的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个女朋友好不。或者说是用静态方法,不过说实话,静态的方法或者常量变量还是尽量减少使用,毕竟静态内存也不富裕,再说不考虑这个,我们不还是需要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);
}
}