配置Retrofit网络框架及其使用

    科技2024-08-15  28

    **

    1.配置service接口

    网络请求需要哪些信息?

    ** 一般网络请求,会需要如下这些信息:

    请求的网址 请求方式;是GET请求,还是POST请求 请求参数 参数传递方式;是通过表单方式传递,还是通过JSON方式传递 请求头 如何配置? 将这些信息写到一个接口中。

    创建Model

    /** * 歌单详情包裹对象 * <p> * 只是用来测试 */ public class SheetDetailWrapper { /** * 歌单详情 */ private Sheet data; public Sheet getData() { return data; } public void setData(Sheet data) { this.data = data; } } /** * 歌单对象 */ public class Sheet { /** * 歌单Id */ private String id;//这个用字符串类型,防止以后id变为字符串了,不好搞 /** * 歌单标题 */ private String title; /** * 歌单封面 */ private String banner; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBanner() { return banner; } public void setBanner(String banner) { this.banner = banner; } } { "data": { "id": 1, "title": "这世上所有的歌zheshishangtest", "banner": "assets/list1.jpg", "description": "这是因为iOS9引入了新特性App Transport Security (ATS),他要求App内网络请求必须使用HTTPS协议。解决方法是要么改为HTTPS,要么声明可以使用HTTP,可以声明部分使用HTTP,也可以所有;但需要说明的是如果APP内所有请求都是HTTP,那么如果要上架App Store的时候基本都会被拒。", "clicks_count": 16773, "collections_count": 28, "comments_count": 172, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 }, "songs": [ { "id": 11, "title": "忘不了的温柔", "banner": "assets/yuanfengredehuo_andongyang.jpg", "uri": "assets/wangbiliaodewenrou_andongyang.mp3", "clicks_count": 0, "comments_count": 0, "created_at": "2019-09-17T05:52:50.000Z", "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg" }, "singer": { "id": 43, "nickname": "安东阳", "avatar": "assets/andongyang.jpg" } }, { "id": 10, "title": "伤心的站台", "banner": "assets/yuanfengredehuo_andongyang.jpg", "uri": "assets/shangxingzhantai_andongyang.mp3", "clicks_count": 0, "comments_count": 0, "created_at": "2019-09-17T05:50:50.000Z", "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg" }, "singer": { "id": 43, "nickname": "安东阳", "avatar": "assets/andongyang.jpg" } } ] }

    创建Service接口

    我们希望将项目中,所有请求的信息都放到Service接口中,名称可以随便写,每个方法,就代表一个接口,定义是歌单详情,只是我们这里用这种命名方式。

    /** * 网络接口配置 * <p> * 之所以调用接口还能返回数据 * 是因为Retrofit框架内部处理了 * 这里不讲解原理 * 在《详解Retrofit》课程中讲解 */ public interface Service { /** * 歌单详情 * * @param id {id} 这样表示id,表示的@Path("id")里面的id, * path里面的id其实就是接收后面String id 的值 * <p> * 一般情况下,三个名称都写成一样,比如3个都是id * <p> * //Retrofit如何知道我们传入的是id呢,其实通过Retrofit注解@Path("id")知道 * (应该是相等于限定了id,其他的应该会报错) * <p> * Observable<SheetDetailWrapper>:相等于把json数据转换成这个SheetDetailWrapper类型的对象 * Observable:rxjava里面的类 */ @GET("v1/sheets/{id}") Observable<SheetDetailWrapper> sheetDetail(@Path("id") String id); }

    如果需要更多接口,只需要在这里添加就行了;这里暂时只添加了一个接口,目的是后面封装网络框架的时候会用到,其他的接口,用到了在添加。

    2.如何使用Retrofit请求网络

    //测试网络请求 OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder(); //构建者模式 //初始化Retrofit Retrofit retrofit = new Retrofit.Builder() //让Retrofit使用okhttp .client(okhttpClientBuilder.build()) //api地址 .baseUrl(Constant.ENDPOINT)//这里使用的是地址的公共前缀 //适配Rxjava(就是所返回的对象以Rxjava这种方式来工作(比如我们使用了Observable这种方式,接口Service查看)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //使用gson解析json //包括请求参数和响应 // (比如使用Retrofit请求框架请求数据,发送对象,也会转换成json(使用gson转换)) .addConverterFactory(GsonConverterFactory.create()) //创建Retrofit .build(); //创建Service Service service = retrofit.create(Service.class); //请求歌单详情 service.sheetDetail("1") .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread())//在主线程观察(操作UI在主线程) //接口方法里面对应的对象:SheetDetailWrapper .subscribe(new Observer<SheetDetailWrapper>() {//订阅回来的数据 @Override public void onSubscribe(Disposable d) { } /** * 请求成功 * * @param sheetDetailWrapper 解析回来的对象 */ @Override public void onNext(SheetDetailWrapper sheetDetailWrapper) { LogUtil.d(TAG, "request sheet detail success:" + sheetDetailWrapper.getData().getTitle()); } /** * 请求失败 * * @param e Error */ @Override public void onError(Throwable e) { e.printStackTrace(); // LogUtil.d(TAG,"request sheet detail failed:" + e.getLocalizedMessage()); } /** * 请求结束 */ @Override public void onComplete() { } });

    测试 运行项目,点击按钮,就可以在日志中查看到歌单为1的JSON数据了。

    2.2 网络请求错误 处理

    代码演示:

    在onError方法可以判断,这样太麻烦,后面会封装

    //判断错误类型 if (e instanceof UnknownHostException) { ToastUtil.errorShortToast(R.string.error_network_unknown_host); } else if (e instanceof ConnectException) { ToastUtil.errorShortToast(R.string.error_network_connect); } else if (e instanceof SocketTimeoutException) { ToastUtil.errorShortToast(R.string.error_network_timeout); } else if (e instanceof HttpException) { HttpException exception = (HttpException) e; int code = exception.code(); if (code == 401) { ToastUtil.errorShortToast(R.string.error_network_not_auth); } else if (code == 403) { ToastUtil.errorShortToast(R.string.error_network_not_permission); } else if (code == 404) { ToastUtil.errorShortToast(R.string.error_network_not_found); } else if (code >= 500) { ToastUtil.errorShortToast(R.string.error_network_server); } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else { ToastUtil.errorShortToast(R.string.error_network_unknown); }

    测试错误(404和500)

    因为我们上面代码判断了,所以手机上报错404和500会爆出相应的提示。 为了方便,我们电脑上先查看下网络状态,然后再 运行到手机app上查看网络状态

    如果要404错误,只需要把http://dev-my-cloud-music-api-rails.ixuea.com/v1/sheets中的sheets改下,就变成404 (把URL地址更改为一个不存在的地址;就会提示“你访问内容不存在!”。) 如果要500错误,只要用户名不存在就会变成500错误(用户名不存在就会报500错误) 比如: http://dev-my-cloud-music-api-rails.ixuea.com/v1/users/-1?nickname=11111111这个地址就是500错误

    打开这个检查界面,然后输入网址就可以捕获到网络状态

    测试无网络

    我们这里使用的是模拟器,所以关闭电脑WiFi就可以模拟;如果是真实手机,也可以关闭WiFi;测试会发现,关闭网络,会产生一个UnknownHostException异常,所以在这里代码判断就行了。

    2.3 封装

    1. 封装网络请求Api

    可以把初始化okhttp,初始化retrofit,还要创建Service放到一个单独的类中,然后把这个类,实现为单例,因为前面的这些初始化,只需要执行一次就行了。

    Api 类

    public class Api { /** * Api单例字段 */ private static Api instance; /** * Service单例 */ private final Service service; /** * 返回当前对象的唯一实例 * <p> * 单例设计模式 * 由于移动端很少有高并发 * 所以这个就是简单判断 * * @return 本类单例 */ public static Api getInstance() { if (instance == null) { instance = new Api(); } return instance; } /** * 私有构造方法 */ private Api() { OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder(); //构建者模式 //初始化Retrofit Retrofit retrofit = new Retrofit.Builder() //让Retrofit使用okhttp .client(okhttpClientBuilder.build()) //api地址 .baseUrl(Constant.ENDPOINT)//这里使用的是地址的公共前缀 //适配Rxjava(就是所返回的对象以Rxjava这种方式来工作(比如我们使用了Observable这种方式,接口Service查看)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //使用gson解析json //包括请求参数和响应 // (比如使用Retrofit请求框架请求数据,发送对象,也会转换成json(使用gson转换)) .addConverterFactory(GsonConverterFactory.create()) //创建Retrofit .build(); //创建Service service = retrofit.create(Service.class); } /** * 歌单详情 * * @param id 传入的第几个歌曲Id * @return 返回Retrofit接口实例 里面的方法返回的对象 */ public Observable<SheetDetailWrapper> sheetDetail(String id) { return service.sheetDetail(id) .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread());//在主线程观察(操作UI在主线程) }

    2. 使用

    把之前的service去掉,然后用这个Api对象调用里面的方法即可

    //请求歌单详情 // service.sheetDetail("1") Api.getInstance().sheetDetail("1") .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread())//在主线程观察(操作UI在主线程) //接口方法里面对应的对象:SheetDetailWrapper .subscribe(new Observer<SheetDetailWrapper>() {//订阅回来的数据 @Override public void onSubscribe(Disposable d) { } /** * 请求成功 * * @param sheetDetailWrapper 解析回来的对象 */ @Override public void onNext(SheetDetailWrapper sheetDetailWrapper) { LogUtil.d(TAG, "request sheet detail success:" + sheetDetailWrapper.getData().getTitle()); } /** * 请求失败 * * @param e Error */ @Override public void onError(Throwable e) { e.printStackTrace(); // LogUtil.d(TAG,"request sheet detail failed:" + e.getLocalizedMessage()); //判断错误类型 if (e instanceof UnknownHostException) { ToastUtil.errorShortToast(R.string.error_network_unknown_host); } else if (e instanceof ConnectException) { ToastUtil.errorShortToast(R.string.error_network_connect); } else if (e instanceof SocketTimeoutException) { ToastUtil.errorShortToast(R.string.error_network_timeout); } else if (e instanceof HttpException) { HttpException exception = (HttpException) e; int code = exception.code(); if (code == 401) { ToastUtil.errorShortToast(R.string.error_network_not_auth); } else if (code == 403) { ToastUtil.errorShortToast(R.string.error_network_not_permission); } else if (code == 404) { ToastUtil.errorShortToast(R.string.error_network_not_found); } else if (code >= 500) { ToastUtil.errorShortToast(R.string.error_network_server); } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } /** * 请求结束 */ @Override public void onComplete() { } });

    2.4 加载对话框

    public class LoadingUtil { private static ProgressDialog progressDialog; /** * 使用一个加载对话框,使用默认提示文字 * * @param activity Activity */ public static void showLoading(Activity activity) { showLoading(activity, activity.getString(R.string.loading)); } /** * 显示一个加载对话框(可以输入任何的message) * * @param activity Activity * @param message Message */ private static void showLoading(Activity activity, String message) { //判断activity为空或者已经销毁了 if (activity == null || activity.isFinishing()) { return; } //判断是否显示了 if (progressDialog != null) { //已经显示了 不需要再次显示 //就不再显示了 return; } //创建一个进度对话框 progressDialog = new ProgressDialog(activity); progressDialog.setTitle("提示");//提示标题 progressDialog.setMessage(message);//提示信息 //点击外部弹窗不会自动隐藏 progressDialog.setCancelable(false); progressDialog.show(); } /** * 隐藏加载提示对话框 */ public static void hideLoading() { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.hide(); progressDialog = null; } } }

    如何使用

    //测试加载提示框 LoadingUtil.showLoading(getMainActivity()); //3秒中隐藏加载提示框 //因显示后无法点击后面的按钮(也就是当前页面点击的3s后关闭,在另一个页面关闭麻烦) new Handler().postDelayed(new Runnable() { @Override public void run() { LoadingUtil.hideLoading(); } }, 3000);

    完成网络请求加载显示

    前面已经学习了RxJava的回调方法,所以可以在onSubscribe方法显示加载提示; 在onNext和onError方法中隐藏加载提示。

    //请求歌单详情 // service.sheetDetail("1") Api.getInstance().sheetDetail("1") .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread())//在主线程观察(操作UI在主线程) //接口方法里面对应的对象:SheetDetailWrapper .subscribe(new Observer<SheetDetailWrapper>() {//订阅回来的数据 @Override public void onSubscribe(Disposable d) { Log.d(TAG, "onSubscribe: "); //显示加载对话框 LoadingUtil.showLoading(getMainActivity()); } /** * 请求成功 * * @param sheetDetailWrapper 解析回来的对象 */ @Override public void onNext(SheetDetailWrapper sheetDetailWrapper) { LogUtil.d(TAG, "onNext:" + sheetDetailWrapper.getData().getTitle()); LoadingUtil.hideLoading();//隐藏加载提示框 } /** * 请求失败 * * @param e Error */ @Override public void onError(Throwable e) { Log.d(TAG, "onError: "); LoadingUtil.hideLoading();//隐藏加载提示框 } }

    测试

    确认请求网络能显示,并隐藏。

    2.5 请求数据

    2.5.1 json数据分析

    { "data": [ { "id": 1, "title": "这世上所有的歌zheshishangtest", "banner": "assets/list1.jpg", "clicks_count": 16795, "collections_count": 28, "comments_count": 172, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 } }, { "id": 2, "title": "我向来做事十拿九不稳 不信可以试试woxianglaitest", "banner": "assets/list2.jpg", "clicks_count": 2099, "collections_count": 11, "comments_count": 2, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 } }, { "id": 3, "title": "网络离别歌曲最后还是离开了wangllibietest", "banner": "assets/list3.jpg", "clicks_count": 4433, "collections_count": 10, "comments_count": 0, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 } }, { "id": 9, "title": "伤心的人怎可愿意听慢歌", "banner": "assets/yuanfengredehuo_andongyang.jpg", "clicks_count": 620, "collections_count": 7, "comments_count": 4, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 } }, { "id": 10, "title": "你开始懂得了歌词", "banner": "assets/shengburusi.jpg", "clicks_count": 772, "collections_count": 9, "comments_count": 0, "user": { "id": 1, "nickname": "爱学啊dev666", "avatar": "67133b479b364e8c9bfceb58015cd71f.jpg", "gender": 0 } }, { "id": 92, "title": "2020", "banner": null, "clicks_count": 15, "collections_count": 0, "comments_count": 0, "user": { "id": 622, "nickname": "凌锴", "avatar": null, "gender": 0 } }, { "id": 91, "title": "最好听的歌单", "banner": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3489003925,2338759571&fm=26&gp=0.jpg", "clicks_count": 120, "collections_count": 1, "comments_count": 0, "user": { "id": 516, "nickname": "乐天gg", "avatar": "7f71228986a340a8830cb778d2c9037b.jpg", "gender": 10 } }, { "id": 90, "title": "2020年度金曲", "banner": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3489003925,2338759571&fm=26&gp=0.jpg", "clicks_count": 41, "collections_count": 1, "comments_count": 0, "user": { "id": 516, "nickname": "乐天gg", "avatar": "7f71228986a340a8830cb778d2c9037b.jpg", "gender": 10 } }, { "id": 89, "title": "最好听的粤语歌", "banner": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3489003925,2338759571&fm=26&gp=0.jpg", "clicks_count": 29, "collections_count": 1, "comments_count": 0, "user": { "id": 516, "nickname": "乐天gg", "avatar": "7f71228986a340a8830cb778d2c9037b.jpg", "gender": 10 } }, { "id": 88, "title": "sfsfdx", "banner": null, "clicks_count": 20, "collections_count": 0, "comments_count": 0, "user": { "id": 118, "nickname": "阿健", "avatar": "95a189d10eb94da4b795585af183c774.jpg", "gender": 10 } } ] }

    分析:

    可以当成一个大对象(也就是外围是一个类),然后对象里面的成员变量又是一个对象的话,按照对象的思维去解析;否则直接在大对象的类里面直接添加一个成员变量即可。

    我们先折叠json数据 我们可以创建一个类

    public class SheetListWrapper { }

    依次张开 这个data是个数组(当做集合处理),这个大对象里面的data成员变量是一个集合。

    那集合里面的item又是一个对象,所以我们又得定义一个item对象Sheet(这个之前定义的)

    //public class Sheet extends BaseModel { public class Sheet extends BaseMultiItemEntity { //id可以删除了,因为已经定义到父类了 // /** // * 歌单Id // */ // private String id;//这个用字符串类型,防止以后id变为字符串了,不好搞 /** * 歌单标题 */ private String title; /** * 歌单封面 */ private String banner; /** * 描述 */ private String description; }

    我们依次展开 可以看到

    所以最总的

    这里记得把set get方法加上

    public class SheetListWrapper { /** * 歌单列表 */ private List<Sheet> data; public List<Sheet> getData() { return data; } public void setData(List<Sheet> data) { this.data = data; } }

    请求单个item数据的model 方法同上。 这里顺便附上代码

    /** * 歌单详情包裹对象 * <p> * 只是用来测试 */ public class SheetDetailWrapper { /** * 歌单详情 */ private Sheet data; //这里返回的是单个歌单Sheet,//SheetListWrapper那边返回的是多个歌单,也就是list, public Sheet getData() { return data; } public void setData(Sheet data) { this.data = data; } }

    2.5.2 请求数据并简单使用

    1.创建模型数据,前面已经分析了

    /** * 歌单详情包裹对象 * <p> * 只是用来测试 */ //这里类这里还没有用到,先写上 这个是请求单个歌单详情时候用到的model类 public class SheetDetailWrapper { /** * 歌单详情 */ private Sheet data; //这里返回的是单个歌单Sheet,//SheetListWrapper那边返回的是多个歌单,也就是list, public Sheet getData() { return data; } public void setData(Sheet data) { this.data = data; } } /** * 歌单列表模型 * <p> * 只是用来测试 * <p> * 地址:http://dev-my-cloud-music-api-rails.ixuea.com/v1/sheets * 后面没有数字,有数字的是具体某个歌单 */ public class SheetListWrapper { /** * 歌单列表 */ private List<Sheet> data; public List<Sheet> getData() { return data; } public void setData(List<Sheet> data) { this.data = data; } }

    item里面的model类

    //public class Sheet extends BaseModel { public class Sheet extends BaseMultiItemEntity { //id可以删除了,因为已经定义到父类了 // /** // * 歌单Id // */ // private String id;//这个用字符串类型,防止以后id变为字符串了,不好搞 /** * 歌单标题 */ private String title; /** * 歌单封面 */ private String banner; /** * 描述 */ private String description; }

    2.接口配置

    /** * 网络接口配置 * <p> * 之所以调用接口还能返回数据 * 是因为Retrofit框架内部处理了 * 这里不讲解原理 * 在《详解Retrofit》课程中讲解 */ public interface Service { /** * 歌单列表 */ @GET("v1/sheets") Observable<SheetListWrapper> sheets(); }

    Api类

    public class Api { .... /** * 歌单列表 * * @return 返回Observable<SheetListWrapper> */ public Observable<SheetListWrapper> sheets() { return service.sheets() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); }

    3.使用

    //请求歌单列表 Api.getInstance() .sheets() .subscribe(new Observer<SheetListWrapper>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(SheetListWrapper sheetListWrapper) { LogUtil.d(TAG, "onNext:" + sheetListWrapper.getData().size()); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); }

    2.5.3 封装请求数据Model

    前面我们也说了,项目中所有的网络,请求最外层都有一层包装,真实的数据在data里面,如果是详情,data就是一个对象,如果是列表,data就是一个数组;前面的解析歌单详情的时候,还要给歌单详情外面创建一个包装类,那如果其他对象也按照这种方式实现的话,第一个是要创建很多的包装类,同时外面的包装类是重复的,所以说,可以创建一个通用的包装类,通过泛型的方式指定里面的内容

    1.如何实现封装

    前面看到每个网络请求,最外层都有可能有,message,status两个字段,他们是必要的时候才有,还有一个data字段,只是不同的接口,类型不一样;所以我们可以创建一个BaseResponse。

    创建BaseResponse

    /** * 通用网络请求响应模型 * <p> * 前面看到每个网络请求,最外层都有可能有,message,status两个字段,他们是必要的时候才有, * 还有一个data字段,只是不同的接口,类型不一样;所以我们可以创建一个BaseResponse。 */ public class BaseResponse { /** * 状态码 * <p> * 只有发生了错误才会有 * <p> * 如果用int类型的话,全局变量会默认初始化,这个值就默认为0了 * 而我们不想为0,让发生了错误的时候才会值(不想有值,出错了才有值) * 所以我们这里定义为引用类型Integer,就会默认初始化为null */ private Integer status; /** * 出错的提示信息 * <p> * 发生了错误不一定有 */ private String message; public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }

    创建详情对象 创建一个DetailResponse,它用来解析详情这类网络请求。

    /** * 详情网络请求解析类 * <p> * 继承BaseResponse * 定义了一个泛型T */ public class DetailResponse<T> extends BaseResponse { /** * 真实数据 * 他的类型就是泛型 */ private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } }

    前面创建了BaseResponse,也实现了详情网络请求的包装,那对应列表请求来说,其实也只有data不一样,所以可以创建一个ListResponse。

    /** * 解析列表网络请求 */ public class ListResponse<T> extends BaseResponse { /** * 定义一个列表 * <p> * 里面的对象使用了泛型 */ private List<T> data;//名字要和服务器端的一样 public List<T> getData() { return data; } public void setData(List<T> data) { this.data = data; } }

    2.使用封装后请求

    2.1单个对象详情使用

    统一将里面的model类改成 DetailResponse

    * <p> * 之所以调用接口还能返回数据 * 是因为Retrofit框架内部处理了 * 这里不讲解原理 * 在《详解Retrofit》课程中讲解 */ public interface Service { /** * 歌单详情 * * @param id {id} 这样表示id,表示的@Path("id")里面的id, * path里面的id其实就是接收后面String id 的值 * <p> * 一般情况下,三个名称都写成一样,比如3个都是id * <p> * //Retrofit如何知道我们传入的是id呢,其实通过Retrofit注解@Path("id")知道 * (应该是相等于限定了id,其他的应该会报错) * <p> * Observable<SheetDetailWrapper>:相等于把json数据转换成这个SheetDetailWrapper类型的对象 * Observable:rxjava里面的类 */ @GET("v1/sheets/{id}") // Observable<SheetDetailWrapper> sheetDetail(@Path("id") String id); Observable<DetailResponse<Sheet>> sheetDetail(@Path("id") String id); } /** * 歌单详情 * * @param id 传入的第几个歌曲Id * @return 返回Retrofit接口实例 里面的方法返回的对象 */ public Observable<DetailResponse<Sheet>> sheetDetail(String id) { return service.sheetDetail(id) .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread());//在主线程观察(操作UI在主线程) } //请求DetailResponse歌单详情(封装接口后使用) Api.getInstance().sheetDetail("1") .subscribe(new Observer<DetailResponse<Sheet>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DetailResponse<Sheet> sheetDetailResponse) { LogUtil.d(TAG, "onNext:" + sheetDetailResponse.getData().getTitle()); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    2.2 列表使用

    类似的,使用也把Model类改成 ListResponse 即可。

    最后还要测试能正常的运行。

    2.5.4 封装网络请求回调

    如果大家不知道从和下手,还是按照我们前面的方法,从使用的位置,也就是我们期望封装完成后的效果开始。

    请求网络的时候先通过Api的getInstance方法,获取到Api类,然后调用相应的方法,他返回的是Observable对象,然后重写onSucceeded只关注成功,通过onFailed关注失败。

    1.实现通用Observer回调

    可以看到现在使用Observer的时候,都需要实现全部方法,这样每次使用的时候比较麻烦,所以可以借鉴Java中的设计,就是给接口创建一个默认实现类,这个类只是简单的实现这些方法。

    /** * 通用实现Observer里面的方法 * <p> * 目的是避免要实现所有方法 */ public class ObserverAdapter<T> implements Observer<T> { /** * 开始订阅了执行(可以简单理解为开始执行前) * * @param d Disposable对象 */ @Override public void onSubscribe(Disposable d) { } /** * 下一个Observer(当前Observer执行完成了) * * @param t 具体的对象或者集合 */ @Override public void onNext(T t) { } /** * 发生了错误(执行失败了) * * @param e Throwable对象 */ @Override public void onError(Throwable e) { } /** * 回调了onNext方法后调用 */ @Override public void onComplete() { } } //使用ObserverAdapter Api.getInstance().sheetDetail("1") .subscribe(new ObserverAdapter<DetailResponse<Sheet>>() { @Override public void onNext(DetailResponse<Sheet> sheetDetailResponse) { super.onNext(sheetDetailResponse); LogUtil.d(TAG, "onNext:" + sheetDetailResponse.getData().getTitle()); } });

    可以看到只需要重写需要的方法就行了。

    最后确保能正确的运行

    2.实现HttpObserver回调

    后面我们要实现自动网络错误处理,而这部分逻辑放到ObserverAdapter不太好,所以我们创建HttpObserver。 /** * 网络请求Observer * <p> * 有些错误不方便放在ObserverAdapter处理,所以定义了HttpObserver类这个是继承ObserverAdapter<T>的 * <p> * 本类有成功与失败方法 */ public abstract class HttpObserver<T> extends ObserverAdapter<T> { private static final String TAG = "HttpObserver"; /** * 请求成功 * * @param data 数据(对象或者集合) * Succeeded:success 后面的2个s改成ed * 改成抽象类,让子类实现 */ public abstract void onSucceeded(T data); /** * 请求失败 * * @param data 数据(对象或者集合) 比如传入进来的:DetailResponse<Sheet>模型对象类 * @param e Throwable * @return boolean */ public boolean onFailed(T data, Throwable e) { return false; } @Override public void onNext(T t) { super.onNext(t); LogUtil.d(TAG, "onNext:" + t); //TODO 处理错误 //请求正常 onSucceeded(t); } @Override public void onError(Throwable e) { super.onError(e); LogUtil.d(TAG, "onError:" + e.getLocalizedMessage()); //TODO 处理错误 } }

    使用:

    //使用HttpObserver Api.getInstance().sheetDetail("1") .subscribe(new HttpObserver<DetailResponse<Sheet>>() { @Override public void onSucceeded(DetailResponse<Sheet> data) { LogUtil.d(TAG, "onSucceeded:" + data.getData().getTitle()); } });

    确保能运行成功

    2.2.加强(添加错误处理)

    HttpObserver类

    package com.ixuea.courses.mymusicold.listener; import android.text.TextUtils; import com.ixuea.courses.mymusicold.R; import com.ixuea.courses.mymusicold.domain.response.BaseResponse; import com.ixuea.courses.mymusicold.util.LogUtil; import com.ixuea.courses.mymusicold.util.ToastUtil; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import retrofit2.HttpException; /** * 网络请求Observer * <p> * 有些错误不方便放在ObserverAdapter处理,所以定义了HttpObserver类这个是继承ObserverAdapter<T>的 * <p> * 本类有成功与失败方法 */ public abstract class HttpObserver<T> extends ObserverAdapter<T> { private static final String TAG = "HttpObserver"; /** * 请求成功 * * @param data 数据(对象或者集合) * Succeeded:success 后面的2个s改成ed * 改成抽象类,让子类实现 */ public abstract void onSucceeded(T data); /** * 请求失败 * * @param data 数据(对象或者集合) * @param e Throwable * @return boolean */ public boolean onFailed(T data, Throwable e) { return false; } /** * 如果要404错误,只需要把http://dev-my-cloud-music-api-rails.ixuea.com/v1/sheets中的sheets改下,就变成404 * 如果要500错误,只要用户名不存在就会变成500错误 * 比如: * http://dev-my-cloud-music-api-rails.ixuea.com/v1/users/-1?nickname=11111111这个地址就是500错误 * <p> * 总结:发生错误都会发生在onError中 * * @param t 具体的对象或者集合 */ @Override public void onNext(T t) { super.onNext(t); LogUtil.d(TAG, "onNext:" + t); /** * 已经请求成功,但是登录失败了 * 但是如果用户名 密码错误会返回false * * 可以理解为200~299之间的值就会返回到这里来 * 这里面的错误,可以先看看,到时候遇到再说 */ if (isSucceeded(t)) { //请求正常 onSucceeded(t); } else { //请求出错了(就是登录失败了) requestErrorHandler(t, null); } } @Override public void onError(Throwable e) { super.onError(e); LogUtil.d(TAG, "onError:" + e.getLocalizedMessage()); requestErrorHandler(null, e);//第一个参数为null,出错了,没有数据对象传递到这个方法里面来 } /** * 判断网络请求是否成功 * * @param t T * @return 是否成功 */ private boolean isSucceeded(T t) { //比如返回的code=200,(比如用户名 密码错误) if (t instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse baseResponse = (BaseResponse) t; //没有状态码表示成功 //这是我们和服务端的一个规定(一般情况下status等于0才是成功,我们这里是null才成功) //一般==null,则return true;否则return false return baseResponse.getStatus() == null; } return false; } /** * 处理错误网络请求 * * @param data T 数据模型对象 * @param error Throwable错误对象 */ private void requestErrorHandler(T data, Throwable error) { if (error != null) { //判断错误类型 if (error instanceof UnknownHostException) { ToastUtil.errorShortToast(R.string.error_network_unknown_host); } else if (error instanceof ConnectException) { ToastUtil.errorShortToast(R.string.error_network_connect); } else if (error instanceof SocketTimeoutException) { ToastUtil.errorShortToast(R.string.error_network_timeout); } else if (error instanceof HttpException) { HttpException exception = (HttpException) error; int code = exception.code(); if (code == 401) { ToastUtil.errorShortToast(R.string.error_network_not_auth); } else if (code == 403) { ToastUtil.errorShortToast(R.string.error_network_not_permission); } else if (code == 404) { ToastUtil.errorShortToast(R.string.error_network_not_found); } else if (code >= 500) { ToastUtil.errorShortToast(R.string.error_network_server); } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else {//error为null(这个时候是走onNext-->else-->requestErrorHandler) //(登录失败的这种错误) if (data instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse response = (BaseResponse) data; if (TextUtils.isEmpty(response.getMessage())) { //没有错误提示信息(message可能没有错误提示信息) (未知错误,请稍后再试!) ToastUtil.errorShortToast(R.string.error_network_unknown); } else {//message不为空 ToastUtil.errorShortToast(response.getMessage()); } } } } }
    2.2.1使用(代码实现404和500错误,看看是否会弹出错误提示)
    public interface Service { /** * 歌单详情 * * @param id {id} 这样表示id,表示的@Path("id")里面的id, * path里面的id其实就是接收后面String id 的值 * <p> * 一般情况下,三个名称都写成一样,比如3个都是id * <p> * //Retrofit如何知道我们传入的是id呢,其实通过Retrofit注解@Path("id")知道 * (应该是相等于限定了id,其他的应该会报错) * <p> * Observable<SheetDetailWrapper>:相等于把json数据转换成这个SheetDetailWrapper类型的对象 * Observable:rxjava里面的类 */ // @GET("v1/sheets11111111/{id}")//404 @GET("v1/sheets/{id}") // Observable<SheetDetailWrapper> sheetDetail(@Path("id") String id); Observable<DetailResponse<Sheet>> sheetDetail(@Path("id") String id); //模拟500(也就是) /** * 用户详情 * //后面的查询参数会自动添加到后面的 * http://dev-my-cloud-music-api-rails.ixuea.com/v1/users/-1?nickname=11111111 * 比如这里吗的 问号?和参数nickname=11111111会添加到后面 * 因为这里有个参数 @QueryMap Map<String,String> data */ @GET("v1/users/{id}") Observable<DetailResponse<User>> userDetail(@Path("id") String id, @QueryMap Map<String, String> data); }

    上面Service接口中用到的User模型类,目前还没有内容,因为我们这里是测试错误。‘’

    /** * 用户详情 */ public class User { }

    API类中的

    /** * 歌单详情 * * @param id 传入的第几个歌曲Id * @return 返回Retrofit接口实例 里面的方法返回的对象 */ public Observable<DetailResponse<Sheet>> sheetDetail(String id) { return service.sheetDetail(id) .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread());//在主线程观察(操作UI在主线程) } /** * 歌单详情 * * @param id 传入的第几个歌曲Id * @return 返回Retrofit接口实例 里面的方法返回的对象 */ public Observable<DetailResponse<User>> userDetail(String id, String nickname) { //添加查询参数 HashMap<String, String> data = new HashMap<>(); if (StringUtils.isNotBlank(nickname)) { //如果昵称不为空才添加 // nickname=11111111 键Constant.NICKNAME对应nickname; 值nickname对应11111111 data.put(Constant.NICKNAME, nickname); } return service.userDetail(id, data) .subscribeOn(Schedulers.io())//在子线程执行 .observeOn(AndroidSchedulers.mainThread());//在主线程观察(操作UI在主线程) }

    在登录按钮 点击事件里面 测试

    // //使用HttpObserver 和 404 // Api.getInstance().sheetDetail("1") // .subscribe(new HttpObserver<DetailResponse<Sheet>>() { // @Override // public void onSucceeded(DetailResponse<Sheet> data) { // LogUtil.d(TAG, "onSucceeded:" + data.getData().getTitle()); // } // }); //模拟500错误 用户名错误 Api.getInstance().userDetail("-1", "1111111111") .subscribe(new HttpObserver<DetailResponse<User>>() { @Override public void onSucceeded(DetailResponse<User> data) { LogUtil.d(TAG, "onSucceeded:" + data.getData()); } });

    最后确保能正确的运行。

    2.3.手动处理错误(HttpObserver添加代码)

    有些时候,我们可能希望自定义错误处理,而现在默认是,出错了就在父类处理了。

    如何实现?

    可以使用onFailed方法的返回值来实现,可以这样,如果返回true表示子类处理错误,如果返回false父类处理错误。

    //模拟500错误 用户名错误 Api.getInstance().userDetail("-1", "1111111111") .subscribe(new HttpObserver<DetailResponse<User>>() { @Override public void onSucceeded(DetailResponse<User> data) { LogUtil.d(TAG, "onSucceeded:" + data.getData()); } @Override public boolean onFailed(DetailResponse<User> data, Throwable e) { LogUtil.d(TAG, "onFailed:" + e); // return super.onFailed(data, e);//调用父类,内部处理错误 //return true 表示:手动处理错误 return true;//外部处理,(就是说内部的那个提示没有弹出来) } });

    HttpObserver 类

    /** * 网络请求Observer * <p> * 有些错误不方便放在ObserverAdapter处理,所以定义了HttpObserver类这个是继承ObserverAdapter<T>的 * <p> * 本类有成功与失败方法 */ public abstract class HttpObserver<T> extends ObserverAdapter<T> { private static final String TAG = "HttpObserver"; /** * 请求成功 * * @param data 数据(对象或者集合) * Succeeded:success 后面的2个s改成ed * 改成抽象类,让子类实现 */ public abstract void onSucceeded(T data); /** * 请求失败 * * @param data 数据(对象或者集合) * @param e Throwable * @return boolean false :表示父类(本类)要处理错误(内部处理);true:表示子类要处理错误(外部处理) */ public boolean onFailed(T data, Throwable e) { return false; } /** * 如果要404错误,只需要把http://dev-my-cloud-music-api-rails.ixuea.com/v1/sheets中的sheets改下,就变成404 * 如果要500错误,只要用户名不存在就会变成500错误 * 比如: * http://dev-my-cloud-music-api-rails.ixuea.com/v1/users/-1?nickname=11111111这个地址就是500错误 * <p> * 总结:发生错误都会发生在onError中 * * * 已经请求成功,但是登录失败了 * 但是如果用户名 密码错误会返回false * * 可以理解为200~299之间的值就会返回到这里来 * 这里面的错误,可以先看看,到时候遇到再说 * @param t 具体的对象或者集合 */ @Override public void onNext(T t) { super.onNext(t); LogUtil.d(TAG, "onNext:" + t); if (isSucceeded(t)) { //请求正常 onSucceeded(t); } else { //请求出错了(就是登录失败了) requestErrorHandler(t, null); } } @Override public void onError(Throwable e) { super.onError(e); LogUtil.d(TAG, "onError:" + e.getLocalizedMessage()); requestErrorHandler(null, e);//第一个参数为null,出错了,没有数据对象传递到这个方法里面来 } /** * 判断网络请求是否成功 * * @param t T * @return 是否成功 */ private boolean isSucceeded(T t) { //比如返回的code=200,(比如用户名 密码错误) if (t instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse baseResponse = (BaseResponse) t; //没有状态码表示成功 //这是我们和服务端的一个规定(一般情况下status等于0才是成功,我们这里是null才成功) //一般==null,则return true;否则return false return baseResponse.getStatus() == null; } return false; } /** * 处理错误网络请求 * * @param data T 数据模型对象 * @param error Throwable错误对象 */ private void requestErrorHandler(T data, Throwable error) { if (onFailed(data, error)) {//fasle就会走else,父类(可以说本类)处理错误;true:就是外部处理错误 //回调了请求失败方法 //并且该方法返回了true //返回true就表示外部手动处理错误 //那我们框架内部就不用做任何事情了 } else { if (error != null) { //判断错误类型 if (error instanceof UnknownHostException) { ToastUtil.errorShortToast(R.string.error_network_unknown_host); } else if (error instanceof ConnectException) { ToastUtil.errorShortToast(R.string.error_network_connect); } else if (error instanceof SocketTimeoutException) { ToastUtil.errorShortToast(R.string.error_network_timeout); } else if (error instanceof HttpException) { HttpException exception = (HttpException) error; int code = exception.code(); if (code == 401) { ToastUtil.errorShortToast(R.string.error_network_not_auth); } else if (code == 403) { ToastUtil.errorShortToast(R.string.error_network_not_permission); } else if (code == 404) { ToastUtil.errorShortToast(R.string.error_network_not_found); } else if (code >= 500) { ToastUtil.errorShortToast(R.string.error_network_server); } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else {//error为null(这个时候是走onNext-->else-->requestErrorHandler) //(登录失败的这种错误) if (data instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse response = (BaseResponse) data; if (TextUtils.isEmpty(response.getMessage())) { //没有错误提示信息(message可能没有错误提示信息) (未知错误,请稍后再试!) ToastUtil.errorShortToast(R.string.error_network_unknown); } else {//message不为空 ToastUtil.errorShortToast(response.getMessage()); } } } } } }

    3.重构错误到工具类(HttpUtil)

    HttpUtil 类

    /** * 网络请求相关辅助方法 */ public class HttpUtil { /** * 网络请求错误处理 * * @param data Object * @param error Throwable * 这个static后面的<T>是必须要的,否则参数那里会找不到这个泛型T * 也可以不用泛型T,直接用Object */ // public static <T> void handlerRequest(T data, Throwable error) { public static void handlerRequest(Object data, Throwable error) { if (error != null) { //判断错误类型 if (error instanceof UnknownHostException) { ToastUtil.errorShortToast(R.string.error_network_unknown_host); } else if (error instanceof ConnectException) { ToastUtil.errorShortToast(R.string.error_network_connect); } else if (error instanceof SocketTimeoutException) { ToastUtil.errorShortToast(R.string.error_network_timeout); } else if (error instanceof HttpException) { HttpException exception = (HttpException) error; int code = exception.code(); if (code == 401) { ToastUtil.errorShortToast(R.string.error_network_not_auth); } else if (code == 403) { ToastUtil.errorShortToast(R.string.error_network_not_permission); } else if (code == 404) { ToastUtil.errorShortToast(R.string.error_network_not_found); } else if (code >= 500) { ToastUtil.errorShortToast(R.string.error_network_server); } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else { ToastUtil.errorShortToast(R.string.error_network_unknown); } } else {//error为null(这个时候是走onNext-->else-->requestErrorHandler) //(登录失败的这种错误) if (data instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse response = (BaseResponse) data; if (TextUtils.isEmpty(response.getMessage())) { //没有错误提示信息(message可能没有错误提示信息) (未知错误,请稍后再试!) ToastUtil.errorShortToast(R.string.error_network_unknown); } else {//message不为空 ToastUtil.errorShortToast(response.getMessage()); } } } } }

    在原来的HTTPObserver上 改:

    @Override public void onNext(T t) { super.onNext(t); LogUtil.d(TAG, "onNext:" + t); if (isSucceeded(t)) { //请求正常 onSucceeded(t); } else { //请求出错了(就是登录失败了) handlerRequest(t, null); } } @Override public void onError(Throwable e) { super.onError(e); LogUtil.d(TAG, "onError:" + e.getLocalizedMessage()); handlerRequest(null, e);//第一个参数为null,出错了,没有数据对象传递到这个方法里面来 } /** * 处理错误网络请求 * * @param data T 数据模型对象 * @param error Throwable错误对象 */ private void handlerRequest(T data, Throwable error) { if (onFailed(data, error)) {//fasle就会走else,父类(可以说本类)处理错误;true:就是外部处理错误 //回调了请求失败方法 //并且该方法返回了true //返回true就表示外部手动处理错误 //那我们框架内部就不用做任何事情了 } else { //调用工具处理错误(这个是父类,内部处理错误) HttpUtil.handlerRequest(data, error); } }

    子类这里我们也调用它的工具类HttpUtil来处理错误。 注意:这样的话,处理错误的逻辑是和父类一样的(因为都封装到HTTPUtil这个工具类里面了),但是这里我们还是返回true,调用下HTTPUtil类方法。

    //模拟500错误 用户名错误 Api.getInstance().userDetail("-1", "1111111111") .subscribe(new HttpObserver<DetailResponse<User>>() { @Override public void onSucceeded(DetailResponse<User> data) { LogUtil.d(TAG, "onSucceeded:" + data.getData()); } @Override public boolean onFailed(DetailResponse<User> data, Throwable e) { LogUtil.d(TAG, "onFailed:" + e); // return super.onFailed(data, e);//调用父类,内部处理错误 //return true 表示:手动处理错误 //调用工具类处理错误(这个时候会有提示弹出来) HttpUtil.handlerRequest(data, e); return true;//外部处理,(就是说内部的那个提示没有弹出来) } });

    4.网络请求加载对话框显示和隐藏(网络请求时显示,成功和失败都隐藏)

    在HttpObserver的构造方法中添加一个参数控制是否显示对话框

    HttpObserver类

    package com.ixuea.courses.mymusicold.listener; import com.ixuea.courses.mymusicold.activity.BaseCommonActivity; import com.ixuea.courses.mymusicold.domain.response.BaseResponse; import com.ixuea.courses.mymusicold.util.HttpUtil; import com.ixuea.courses.mymusicold.util.LoadingUtil; import com.ixuea.courses.mymusicold.util.LogUtil; import io.reactivex.disposables.Disposable; /** * 网络请求Observer * <p> * 有些错误不方便放在ObserverAdapter处理,所以定义了HttpObserver类这个是继承ObserverAdapter<T>的 * <p> * 本类有成功与失败方法 */ public abstract class HttpObserver<T> extends ObserverAdapter<T> { private static final String TAG = "HttpObserver"; //final 修饰的成员变量,必须要初始化一次,而这里有个空构造方法,有可能没有初始化成员变量, // 所以final修饰的成员变量在编译的时候可能没有初始化,故报错 // private final BaseCommonActivity activity; // private final boolean isShowLoading; private BaseCommonActivity activity;//公共Activity private boolean isShowLoading;//是否显示加载对话框 /** * 无参构造方法 添加这个的主要目的是:父类中有个有参构造方法(这时父类没有无参构造方法,子类在new无参构造的时候就会报错,所以这里要添加无参构造方法) */ public HttpObserver() { } /** * 有参构造方法 * * @param activity BaseCommonActivity * @param isShowLoading 是否显示加载提示框 */ public HttpObserver(BaseCommonActivity activity, boolean isShowLoading) { this.activity = activity; this.isShowLoading = isShowLoading; } /** * 请求成功 * * @param data 数据(对象或者集合) * Succeeded:success 后面的2个s改成ed * 改成抽象类,让子类实现 */ public abstract void onSucceeded(T data); /** * 请求失败 * * @param data 数据(对象或者集合) * @param e Throwable * @return boolean false :表示父类(本类)要处理错误(内部处理);true:表示子类要处理错误(外部处理) */ public boolean onFailed(T data, Throwable e) { return false; } @Override public void onSubscribe(Disposable d) { super.onSubscribe(d); if (isShowLoading) { //显示加载对话框 LoadingUtil.showLoading(activity); } } /** * 如果要404错误,只需要把http://dev-my-cloud-music-api-rails.ixuea.com/v1/sheets中的sheets改下,就变成404 * 如果要500错误,只要用户名不存在就会变成500错误 * 比如: * http://dev-my-cloud-music-api-rails.ixuea.com/v1/users/-1?nickname=11111111这个地址就是500错误 * <p> * 总结:发生错误都会发生在onError中 * * * 已经请求成功,但是登录失败了 * 但是如果用户名 密码错误会返回false * * 可以理解为200~299之间的值就会返回到这里来 * 这里面的错误,可以先看看,到时候遇到再说 * @param t 具体的对象或者集合 */ @Override public void onNext(T t) { super.onNext(t); LogUtil.d(TAG, "onNext:" + t); //检查是否需要隐藏加载提示框(其他地方如onError中用到,提取到一个方法中) checkHideLoading(); if (isSucceeded(t)) { //请求正常 onSucceeded(t); } else { //请求出错了(就是登录失败了) handlerRequest(t, null); } } @Override public void onError(Throwable e) { super.onError(e); LogUtil.d(TAG, "onError:" + e.getLocalizedMessage()); //检查是否需要隐藏加载提示框 checkHideLoading(); handlerRequest(null, e);//第一个参数为null,出错了,没有数据对象传递到这个方法里面来 } /** * 判断网络请求是否成功 * * @param t T * @return 是否成功 */ private boolean isSucceeded(T t) { //比如返回的code=200,(比如用户名 密码错误) if (t instanceof BaseResponse) { //判断具体的业务请求是否成功 BaseResponse baseResponse = (BaseResponse) t; //没有状态码表示成功 //这是我们和服务端的一个规定(一般情况下status等于0才是成功,我们这里是null才成功) //一般==null,则return true;否则return false return baseResponse.getStatus() == null; } return false; } /** * 处理错误网络请求 * * @param data T 数据模型对象 * @param error Throwable错误对象 */ private void handlerRequest(T data, Throwable error) { if (onFailed(data, error)) {//fasle就会走else,父类(可以说本类)处理错误;true:就是外部处理错误 //回调了请求失败方法 //并且该方法返回了true //返回true就表示外部手动处理错误 //那我们框架内部就不用做任何事情了 } else { //调用工具处理错误(这个是父类,内部处理错误) HttpUtil.handlerRequest(data, error); } } /** * 检查是否需要隐藏加载提示框 */ private void checkHideLoading() { if (isShowLoading) { LoadingUtil.hideLoading(); } } }

    使用

    //测试自动显示加载对话框 Api.getInstance().sheetDetail("1") .subscribe(new HttpObserver<DetailResponse<Sheet>>(getMainActivity(), true) { //false表示不显示 // .subscribe(new HttpObserver<DetailResponse<Sheet>>(getMainActivity(),false) { //无参构造方法(没有参数,也是不显示提示加载对话框) // .subscribe(new HttpObserver<DetailResponse<Sheet>>(getMainActivity(),false) { @Override public void onSucceeded(DetailResponse<Sheet> data) { LogUtil.d(TAG, "onSucceeded:" + data.getData().getTitle()); } });
    Processed: 0.011, SQL: 8