一步步封装实现自己的网络请求框架
一、前言
现如今 android 领域流行的网络请求框架基本都是用 retrofit 加 rxjava 来搭配构建的,而以 viewmodel + livedata + retrofit + rxjava 来构建请求框架的例子要相对少得多。而本文就是以这四者作为基础组件,介绍如何一步步封装实现自己的网络请求框架(本文实现的例子不仅仅只是一个网络请求框架,同时也是在介绍应用的架构模式),希望对你有所帮助
目前已实现的功能或者说特色包含以下几点:
1、网络请求结果基于观察者模式进行传递,回调操作与 ui 层的生命周期相绑定,避免了内存泄漏
2、数据加载时的 startloading 与加载结束后的 dismissloading 操作都是自动调用的,具体实现都封装在基类中。当然,子类也可以实现自己的特定实现。例如,本文提供的例子中,baseactivity 实现的加载对话框是 progressdialog ,子 activity 可以自主实现其他弹窗形式
3、当网络请求结果为非成功状态时(网络请求失败或者业务请求失败),默认操作是用 toast 提示失败原因,支持自定义实现失败时的操作
4、逻辑操作与 ui 层相分离,基于观察者模式来实现消息驱动 ui 变化。提供了在 viewmodel 中操作 ui 变化的能力,包括使 activity / fragment 弹出对话框、toast 消息、finishactivity 等 ui 操作,但 viewmodel 不持有 activity / fragment 的引用,而是基于消息驱动实现,从而避免了内存泄漏
源码点击这里查看:viewmodel_retrofit_rxjava
apk 点击这里下载:viewmodel_retrofit_rxjava
二、封装 baseviewmodel 与 baseactivity
viewmodel 与 livedata 都是 android jetpack 架构组件之一。viewmodel 被设计用来存储和管理 ui 相关数据,以便数据能在界面销毁时(比如屏幕旋转)保存数据,而与 viewmodel 相挂钩的 livedata 是一个用于保存可以被观察的值的数据持有类,且遵循应用组件的生命周期,只有在组件的生命周期处于活跃状态时才会收到数据更新通知
既然是消息驱动,那么自然需要一个用于抽象消息类型的 event 类
/** * 作者:leavesc * 时间:2018/9/30 22:17 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public class baseevent { private int action; public baseevent(int action) { this.action = action; } public int getaction() { return action; } } public class baseactionevent extends baseevent { public static final int show_loading_dialog = 1; public static final int dismiss_loading_dialog = 2; public static final int show_toast = 3; public static final int finish = 4; public static final int finish_with_result_ok = 5; private string message; public baseactionevent(int action) { super(action); } public string getmessage() { return message; } public void setmessage(string message) { this.message = message; } }
baseactionevent 即用于向 view 层传递 action 的 model,在 viewmodel 通过向 view 层传递不同的消息类型,从而触发相对应的操作。因此,baseviewmodel 需要向子类提供默认的实现
public interface iviewmodelaction { void startloading(); void startloading(string message); void dismissloading(); void showtoast(string message); void finish(); void finishwithresultok(); mutablelivedata<baseactionevent> getactionlivedata(); }
/** * 作者:leavesc * 时间:2018/9/30 22:24 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public class baseviewmodel extends viewmodel implements iviewmodelaction { private mutablelivedata<baseactionevent> actionlivedata; protected lifecycleowner lifecycleowner; public baseviewmodel() { actionlivedata = new mutablelivedata<>(); } @override public void startloading() { startloading(null); } @override public void startloading(string message) { baseactionevent baseactionevent = new baseactionevent(baseactionevent.show_loading_dialog); baseactionevent.setmessage(message); actionlivedata.setvalue(baseactionevent); } @override public void dismissloading() { actionlivedata.setvalue(new baseactionevent(baseactionevent.dismiss_loading_dialog)); } @override public void showtoast(string message) { baseactionevent baseactionevent = new baseactionevent(baseactionevent.show_toast); baseactionevent.setmessage(message); actionlivedata.setvalue(baseactionevent); } @override public void finish() { actionlivedata.setvalue(new baseactionevent(baseactionevent.finish)); } @override public void finishwithresultok() { actionlivedata.setvalue(new baseactionevent(baseactionevent.finish_with_result_ok)); } @override public mutablelivedata<baseactionevent> getactionlivedata() { return actionlivedata; } void setlifecycleowner(lifecycleowner lifecycleowner) { this.lifecycleowner = lifecycleowner; } }
那作为消息发送方的 baseviewmodel 的具体实现就完成了,之后是消息的接收方 baseactivity / basefragment
baseactivity 通过监听 baseviewmodel 中 actionlivedata 的数据变化从而在网络请求开始加载时 startloading,在加载结束时 dismissloading
一般一个 activity 对应一个 viewmodel,少部分情况是会对应多个 viewmodel,因此 initviewmodel() 声明为了抽象方法,而 initviewmodellist() 默认返回了 null
/** * 作者:leavesc * 时间:2017/11/29 21:04 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ @suppresslint("registered") public abstract class baseactivity extends appcompatactivity { private progressdialog loadingdialog; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); initviewmodelevent(); } protected abstract viewmodel initviewmodel(); protected list<viewmodel> initviewmodellist() { return null; } private void initviewmodelevent() { list<viewmodel> viewmodellist = initviewmodellist(); if (viewmodellist != null && viewmodellist.size() > 0) { observeevent(viewmodellist); } else { viewmodel viewmodel = initviewmodel(); if (viewmodel != null) { list<viewmodel> modellist = new arraylist<>(); modellist.add(viewmodel); observeevent(modellist); } } } private void observeevent(list<viewmodel> viewmodellist) { for (viewmodel viewmodel : viewmodellist) { if (viewmodel instanceof iviewmodelaction) { iviewmodelaction viewmodelaction = (iviewmodelaction) viewmodel; viewmodelaction.getactionlivedata().observe(this, baseactionevent -> { if (baseactionevent != null) { switch (baseactionevent.getaction()) { case baseactionevent.show_loading_dialog: { startloading(baseactionevent.getmessage()); break; } case baseactionevent.dismiss_loading_dialog: { dismissloading(); break; } case baseactionevent.show_toast: { showtoast(baseactionevent.getmessage()); break; } case baseactionevent.finish: { finish(); break; } case baseactionevent.finish_with_result_ok: { setresult(result_ok); finish(); break; } } } }); } } } @override protected void ondestroy() { super.ondestroy(); dismissloading(); } protected void startloading() { startloading(null); } protected void startloading(string message) { if (loadingdialog == null) { loadingdialog = new progressdialog(this); loadingdialog.setcancelable(false); loadingdialog.setcanceledontouchoutside(false); } loadingdialog.settitle(message); loadingdialog.show(); } protected void dismissloading() { if (loadingdialog != null && loadingdialog.isshowing()) { loadingdialog.dismiss(); } } protected void showtoast(string message) { toast.maketext(this, message, toast.length_short).show(); } protected void finishwithresultok() { setresult(result_ok); finish(); } protected baseactivity getcontext() { return baseactivity.this; } protected void startactivity(class cl) { startactivity(new intent(this, cl)); } public void startactivityforresult(class cl, int requestcode) { startactivityforresult(new intent(this, cl), requestcode); } @requiresapi(api = build.version_codes.jelly_bean_mr1) protected boolean isfinishingordestroyed() { return isfinishing() || isdestroyed(); } }
三、封装 retrofit 与 rxjava
在前言中说了,框架默认实现了请求失败时的操作(toast 提示失败原因),也支持自定义回调接口。因此,需要两个回调接口,一个只包含请求成功时的回调接口,另一个多包含了一个请求失败时的回调接口
/** * 作者:leavesc * 时间:2018/10/27 20:53 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public interface requestcallback<t> { void onsuccess(t t); } public interface requestmultiplycallback<t> extends requestcallback<t> { void onfail(baseexception e); }
此外,为了在网络请求成功但业务逻辑请求失败时(例如,请求参数缺失、token失效等),可以抛出详细的失败信息,需要自定义 baseexception
public class baseexception extends runtimeexception { private int errorcode = httpcode.code_unknown; public baseexception() { } public baseexception(int errorcode, string errormessage) { super(errormessage); this.errorcode = errorcode; } public int geterrorcode() { return errorcode; } }
实现具体的异常类
public class paramterinvalidexception extends baseexception { public paramterinvalidexception() { super(httpcode.code_parameter_invalid, "参数有误"); } } public class tokeninvalidexception extends baseexception { public tokeninvalidexception() { super(httpcode.code_token_invalid, "token失效"); } } ···
为了提升性能,retrofit 一般是设计成单例模式。为了应对应用中 baseurl 可能有多个的情况(本文提供的demo就是如此),此处使用 map 来存储多个 retrofit 实例
/** * 作者:leavesc * 时间:2018/10/26 23:11 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public class retrofitmanagement { private static final long read_timeout = 6000; private static final long write_timeout = 6000; private static final long connect_timeout = 6000; private final map<string, object> servicemap = new concurrenthashmap<>(); private retrofitmanagement() { } public static retrofitmanagement getinstance() { return retrofitholder.retrofitmanagement; } private static class retrofitholder { private static final retrofitmanagement retrofitmanagement = new retrofitmanagement(); } private retrofit createretrofit(string url) { okhttpclient.builder builder = new okhttpclient.builder() .readtimeout(read_timeout, timeunit.milliseconds) .writetimeout(write_timeout, timeunit.milliseconds) .connecttimeout(connect_timeout, timeunit.milliseconds) .addinterceptor(new httpinterceptor()) .addinterceptor(new headerinterceptor()) .addinterceptor(new filterinterceptor()) .retryonconnectionfailure(true); if (buildconfig.debug) { httplogginginterceptor httplogginginterceptor = new httplogginginterceptor(); httplogginginterceptor.setlevel(httplogginginterceptor.level.body); builder.addinterceptor(httplogginginterceptor); builder.addinterceptor(new chuckinterceptor(contextholder.getcontext())); } okhttpclient client = builder.build(); return new retrofit.builder() .client(client) .baseurl(url) .addconverterfactory(gsonconverterfactory.create()) .addcalladapterfactory(rxjava2calladapterfactory.create()) .build(); } <t> observabletransformer<baseresponsebody<t>, t> applyschedulers() { return observable -> observable.subscribeon(schedulers.io()) .unsubscribeon(schedulers.io()) .observeon(androidschedulers.mainthread()) .flatmap(result -> { switch (result.getcode()) { case httpcode.code_success: { return createdata(result.getdata()); } case httpcode.code_token_invalid: { throw new tokeninvalidexception(); } case httpcode.code_account_invalid: { throw new accountinvalidexception(); } default: { throw new serverresultexception(result.getcode(), result.getmsg()); } } }); } private <t> observable<t> createdata(t t) { return observable.create(new observableonsubscribe<t>() { @override public void subscribe(observableemitter<t> emitter) { try { emitter.onnext(t); emitter.oncomplete(); } catch (exception e) { emitter.onerror(e); } } }); } <t> t getservice(class<t> clz) { return getservice(clz, httpconfig.base_url_weather); } <t> t getservice(class<t> clz, string host) { t value; if (servicemap.containskey(host)) { object obj = servicemap.get(host); if (obj == null) { value = createretrofit(host).create(clz); servicemap.put(host, value); } else { value = (t) obj; } } else { value = createretrofit(host).create(clz); servicemap.put(host, value); } return value; } }
此外还需要一个自定义的 observer 来对数据请求结果进行自定义回调
/** * 作者:leavesc * 时间:2018/10/27 20:52 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public class basesubscriber<t> extends disposableobserver<t> { private baseviewmodel baseviewmodel; private requestcallback<t> requestcallback; public basesubscriber(baseviewmodel baseviewmodel) { this.baseviewmodel = baseviewmodel; } basesubscriber(baseviewmodel baseviewmodel, requestcallback<t> requestcallback) { this.baseviewmodel = baseviewmodel; this.requestcallback = requestcallback; } @override public void onnext(t t) { if (requestcallback != null) { requestcallback.onsuccess(t); } } @override public void onerror(throwable e) { e.printstacktrace(); if (requestcallback instanceof requestmultiplycallback) { requestmultiplycallback callback = (requestmultiplycallback) requestcallback; if (e instanceof baseexception) { callback.onfail((baseexception) e); } else { callback.onfail(new baseexception(httpcode.code_unknown, e.getmessage())); } } else { if (baseviewmodel == null) { toast.maketext(contextholder.getcontext(), e.getmessage(), toast.length_short).show(); } else { baseviewmodel.showtoast(e.getmessage()); } } } @override public void oncomplete() { } }
四、baseremotedatasource 与 baserepo
上文所介绍的 requestcallback、retrofitmanagement 与 basesubscriber 还是一个个单独的个体,还需要一个链接器来将之串起来,这个链接器的实现类即 baseremotedatasource
在这里,对 baseremotedatasource 的定位是将之当成一个接口实现者,即在 remotedatasource 中实际调用各个请求接口,并通过 rxjava 来控制 loading 弹出以及销毁的时机
一般而言,baseremotedatasource 的实现类中声明的是具有相关逻辑的接口。例如,对于登录模块,可声明一个 logindatasource,对于设置模块,可以声明一个 settingsdatasource
/** * 作者:leavesc * 时间:2018/10/27 7:42 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public abstract class baseremotedatasource { private compositedisposable compositedisposable; private baseviewmodel baseviewmodel; public baseremotedatasource(baseviewmodel baseviewmodel) { this.compositedisposable = new compositedisposable(); this.baseviewmodel = baseviewmodel; } protected <t> t getservice(class<t> clz) { return retrofitmanagement.getinstance().getservice(clz); } protected <t> t getservice(class<t> clz, string host) { return retrofitmanagement.getinstance().getservice(clz, host); } private <t> observabletransformer<baseresponsebody<t>, t> applyschedulers() { return retrofitmanagement.getinstance().applyschedulers(); } protected <t> void execute(observable observable, requestcallback<t> callback) { execute(observable, new basesubscriber<>(baseviewmodel, callback), true); } protected <t> void execute(observable observable, requestmultiplycallback<t> callback) { execute(observable, new basesubscriber<>(baseviewmodel, callback), true); } public void executewithoutdismiss(observable observable, observer observer) { execute(observable, observer, false); } private void execute(observable observable, observer observer, boolean isdismiss) { disposable disposable = (disposable) observable .throttlefirst(500, timeunit.milliseconds) .subscribeon(schedulers.io()) .unsubscribeon(schedulers.io()) .observeon(androidschedulers.mainthread()) .compose(applyschedulers()) .compose(isdismiss ? loadingtransformer() : loadingtransformerwithoutdismiss()) .subscribewith(observer); adddisposable(disposable); } private void adddisposable(disposable disposable) { compositedisposable.add(disposable); } public void dispose() { if (!compositedisposable.isdisposed()) { compositedisposable.dispose(); } } private void startloading() { if (baseviewmodel != null) { baseviewmodel.startloading(); } } private void dismissloading() { if (baseviewmodel != null) { baseviewmodel.dismissloading(); } } private <t> observabletransformer<t, t> loadingtransformer() { return observable -> observable .subscribeon(androidschedulers.mainthread()) .unsubscribeon(androidschedulers.mainthread()) .observeon(androidschedulers.mainthread()) .doonsubscribe(disposable -> startloading()) .dofinally(this::dismissloading); } private <t> observabletransformer<t, t> loadingtransformerwithoutdismiss() { return observable -> observable .subscribeon(androidschedulers.mainthread()) .unsubscribeon(androidschedulers.mainthread()) .observeon(androidschedulers.mainthread()) .doonsubscribe(disposable -> startloading()); } }
除了 baseremotedatasource 外,还需要一个 baserepo。对 baserepo 的定位是将其当做一个接口调度器,其持有 baseremotedatasource 的实例并中转 viewmodel 的接口调用请求,并可以在 baserepo 分担一部分数据处理逻辑
/** * 作者:leavesc * 时间:2018/10/27 21:10 * 描述: * github:https://github.com/leavesc * blog:https://www.jianshu.com/u/9df45b87cfdf */ public class baserepo<t> { protected t remotedatasource; public baserepo(t remotedatasource) { this.remotedatasource = remotedatasource; } }
这样,viewmodel 不关心接口的实际调用实现,方便以后更换 baseremotedatasource 的实现方式,且将一部分的数据处理逻辑放到了 baserepo ,有利于逻辑的复用
五、实践操作(1)-请求天气数据
上文讲了一些基础组件的逻辑实现以及对其的定位,此小节就以一个请求天气数据的接口为例,来介绍如何具体实现一个网络请求的整体流程
首先是声明接口
public interface apiservice { @headers({httpconfig.http_request_type_key + ":" + httpconfig.http_request_weather}) @get("onebox/weather/query") observable<baseresponsebody<weather>> queryweather(@query("cityname") string cityname); }
增加的头部信息是为了标明该接口的请求类型,因为本文作为 demo 的几个接口所用到的 baseurl 以及 请求key 并不相同,因此通过声明头部来为接口动态指定请求参数,而这就需要用到 retrofit 的拦截器了
public class filterinterceptor implements interceptor { @nonnull @override public response intercept(@nonnull chain chain) throws ioexception { request originalrequest = chain.request(); httpurl.builder httpbuilder = originalrequest.url().newbuilder(); headers headers = originalrequest.headers(); if (headers != null && headers.size() > 0) { string requesttype = headers.get(httpconfig.http_request_type_key); if (!textutils.isempty(requesttype)) { switch (requesttype) { case httpconfig.http_request_weather: { httpbuilder.addqueryparameter(httpconfig.key, httpconfig.key_weather); break; } case httpconfig.http_request_qr_code: { httpbuilder.addqueryparameter(httpconfig.key, httpconfig.key_qr_code); break; } case httpconfig.http_request_news: { httpbuilder.addqueryparameter(httpconfig.key, httpconfig.key_news); break; } } } } request.builder requestbuilder = originalrequest.newbuilder() .removeheader(httpconfig.http_request_type_key) .url(httpbuilder.build()); return chain.proceed(requestbuilder.build()); } }
声明 baseremotedatasource 的实现类 weatherdatasource
public class weatherdatasource extends baseremotedatasource implements iweatherdatasource { public weatherdatasource(baseviewmodel baseviewmodel) { super(baseviewmodel); } @override public void queryweather(string cityname, requestcallback<weather> responsecallback) { execute(getservice(apiservice.class).queryweather(cityname), responsecallback); } }
声明 baserepo 的实现类 weatherrepo
public class weatherrepo extends baserepo<iweatherdatasource> { public weatherrepo(iweatherdatasource remotedatasource) { super(remotedatasource); } public mutablelivedata<weather> queryweather(string cityname) { mutablelivedata<weather> weathermutablelivedata = new mutablelivedata<>(); remotedatasource.queryweather(cityname, new requestcallback<weather>() { @override public void onsuccess(weather weather) { weathermutablelivedata.setvalue(weather); } }); return weathermutablelivedata; } }
还需要一个 weatherviewmodel,view 层通过调用 queryweather() 方法在请求成功时触发 weatherlivedata 更新数据,view 层已事先监听 weatherlivedata,并在数据更新时就可以立即收到最新数据
public class weatherviewmodel extends baseviewmodel { private mutablelivedata<weather> weatherlivedata; private weatherrepo weatherrepo; public weatherviewmodel() { weatherlivedata = new mutablelivedata<>(); weatherrepo = new weatherrepo(new weatherdatasource(this)); } public void queryweather(string cityname) { weatherrepo.queryweather(cityname).observe(lifecycleowner, new observer<weather>() { @override public void onchanged(@nullable weather weather) { weatherlivedata.setvalue(weather); } }); } public mutablelivedata<weather> getweatherlivedata() { return weatherlivedata; } }
在 queryweatheractivity 中打印出接口的请求结果
public class queryweatheractivity extends baseactivity { private static final string tag = "queryweatheractivity"; private weatherviewmodel weatherviewmodel; private edittext et_cityname; private textview tv_weather; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_query_weather); et_cityname = findviewbyid(r.id.et_cityname); tv_weather = findviewbyid(r.id.tv_weather); } @override protected viewmodel initviewmodel() { weatherviewmodel = lviewmodelproviders.of(this, weatherviewmodel.class); weatherviewmodel.getweatherlivedata().observe(this, this::handlerweather); return weatherviewmodel; } private void handlerweather(weather weather) { stringbuilder result = new stringbuilder(); for (weather.innerweather.nearestweather nearestweather : weather.getdata().getweather()) { result.append("\n\n").append(new gson().tojson(nearestweather)); } tv_weather.settext(result.tostring()); } public void queryweather(view view) { tv_weather.settext(null); weatherviewmodel.queryweather(et_cityname.gettext().tostring()); } }
也许有人会觉得为了请求一个接口需要建立三个实现类(weatherdatasource、weatherrepo、weatherviewmodel)以及一个接口(iqrcodedatasource)有点繁琐,但这是想要划分职责并实现逻辑与ui相隔离的必然结果。weatherdatasource 用来实现接口的实际调用,只负责请求数据并传递请求结果。weatherrepo 用来屏蔽 weatherviewmodel 对 weatherdatasource 的感知,并承担起一部分数据处理逻辑。weatherviewmodel 用于实现逻辑与 ui 的隔离,并保障数据不因为页面重建而丢失。这样,activity 就可以尽量只承担数据呈现的职责,而不必掺杂数据处理逻辑
六、实践操作(2)-请求生成二维码
此处再来看一个例子,用于生成指定内容的二维码
public class qrcodedatasource extends baseremotedatasource implements iqrcodedatasource { public qrcodedatasource(baseviewmodel baseviewmodel) { super(baseviewmodel); } @override public void createqrcode(string text, int width, requestcallback<qrcode> callback) { execute(getservice(apiservice.class, httpconfig.base_url_qr_code).createqrcode(text, width), callback); } }
此处接口请求回来的只是一段 base64 编码的字符串,而外部希望获取到的自然是一个可以直接使用的 bitmap ,因此可以在 repo 中先对数据进行转换后再传递到外部
public class qrcoderepo extends baserepo<iqrcodedatasource> { public qrcoderepo(iqrcodedatasource remotedatasource) { super(remotedatasource); } public mutablelivedata<qrcode> createqrcode(string text, int width) { mutablelivedata<qrcode> livedata = new mutablelivedata<>(); remotedatasource.createqrcode(text, width, new requestcallback<qrcode>() { @suppresslint("checkresult") @override public void onsuccess(qrcode qrcode) { observable.create(new observableonsubscribe<bitmap>() { @override public void subscribe(@nonnull observableemitter<bitmap> emitter) throws exception { bitmap bitmap = base64tobitmap(qrcode.getbase64_image()); emitter.onnext(bitmap); emitter.oncomplete(); } }).subscribeon(schedulers.io()) .observeon(androidschedulers.mainthread()) .subscribe(new consumer<bitmap>() { @override public void accept(@nonnull bitmap bitmap) throws exception { qrcode.setbitmap(bitmap); livedata.setvalue(qrcode); } }); } }); return livedata; } private static bitmap base64tobitmap(string base64string) { byte[] decode = base64.decode(base64string, base64.default); return bitmapfactory.decodebytearray(decode, 0, decode.length); } }
public class qrcodeviewmodel extends baseviewmodel { private mutablelivedata<qrcode> qrcodelivedata; private qrcoderepo qrcoderepo; public qrcodeviewmodel() { qrcodelivedata = new mutablelivedata<>(); qrcoderepo = new qrcoderepo(new qrcodedatasource(this)); } public void createqrcode(string text, int width) { qrcoderepo.createqrcode(text, width).observe(lifecycleowner, new observer<qrcode>() { @override public void onchanged(@nullable qrcode qrcode) { qrcodelivedata.setvalue(qrcode); } }); } public mutablelivedata<qrcode> getqrcodelivedata() { return qrcodelivedata; } }
七、实践操作(3)-请求失败示例
前言说了,本文封装的网络框架当网络请求结果为非成功状态时(网络请求失败或者业务请求失败),默认操作是用 toast 提示失败原因,也支持自定义实现失败时的操作。此处就来看当请求失败时如何进行处理
此处需要声明两个并不存在的接口
public interface apiservice { @get("leavesc/test1") observable<baseresponsebody<string>> test1(); @get("leavesc/test2") observable<baseresponsebody<string>> test2(); }
public class failexampledatasource extends baseremotedatasource implements ifailexampledatasource { public failexampledatasource(baseviewmodel baseviewmodel) { super(baseviewmodel); } @override public void test1(requestcallback<string> callback) { execute(getservice(apiservice.class).test1(), callback); } @override public void test2(requestcallback<string> callback) { execute(getservice(apiservice.class).test2(), callback); } }
public class failexamplerepo extends baserepo<ifailexampledatasource> { public failexamplerepo(ifailexampledatasource remotedatasource) { super(remotedatasource); } public mutablelivedata<string> test1() { mutablelivedata<string> newspackmutablelivedata = new mutablelivedata<>(); remotedatasource.test1(new requestcallback<string>() { @override public void onsuccess(string newspack) { newspackmutablelivedata.setvalue(newspack); } }); return newspackmutablelivedata; } public void test2(requestmultiplycallback<string> callback) { remotedatasource.test2(callback); } }
test1() 方法用的是基础类的默认失败回调,即直接 toast 提示失败信息。而 test2() 方法则是自定义了请求失败时的回调操作
public class failexampleviewmodel extends baseviewmodel { private mutablelivedata<string> test1livedata = new mutablelivedata<>(); private mutablelivedata<string> test2livedata = new mutablelivedata<>(); private failexamplerepo failexamplerepo = new failexamplerepo(new failexampledatasource(this)); public void test1() { failexamplerepo.test1().observe(lifecycleowner, new observer<string>() { @override public void onchanged(@nullable string s) { test1livedata.setvalue(s); } }); } public void test2() { failexamplerepo.test2(new requestmultiplycallback<string>() { @override public void onfail(baseexception e) { showtoast("test2方法请求失败:" + e.getmessage()); finish(); } @override public void onsuccess(string s) { test2livedata.setvalue(s); } }); } }
八、结束语
这就是整个请求框架的大体架构了,也经过了实际项目的考验了,目前运行良好,但里面可能还会包含一些不合理的地方,欢迎大家指正反馈,如果觉得对你有所帮助,也欢迎 star
源码点击这里查看:viewmodel_retrofit_rxjava
apk 点击这里下载:viewmodel_retrofit_rxjava
推荐阅读
-
RxJava+Retrofit实现网络请求封装的方法
-
一步步封装实现自己的网络请求框架
-
【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)
-
【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)
-
Android网络请求框架(四)Retrofit和RxJava的封装
-
RxJava+Retrofit实现网络请求封装的方法
-
ajax怎么实现网络请求的封装
-
带你一步步剖析Retrofit 源码解析:一款基于 OkHttp 实现的网络请求框架 android源码解析网络请求OKHTTPRetrofit
-
一步步封装实现自己的网络请求框架
-
ajax怎么实现网络请求的封装