// 创建 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
builder.writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//写操作 超时时间
builder.readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS);//读操作超时时间
// 添加公共参数拦截器
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams(“paltform”,“android”)
.addHeaderParams(“userToken”,“1234343434dfdfd3434”)
.addHeaderParams(“userId”,“123445”)
.build();
builder.addInterceptor(commonInterceptor);
// 创建Retrofit
mRetrofit = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiConfig.BASE_URL)
.build();
}
private static class SingletonHolder{
private static final RetrofitServiceManager INSTANCE = new RetrofitServiceManager();
}
/**
获取RetrofitServiceManager @return
*/
public static RetrofitServiceManager getInstance(){
return SingletonHolder.INSTANCE;
}
/**
获取对应的Service @param service Service 的 class @param @return
*/
public T create(Class service){
return mRetrofit.create(service);
}
}
接口实例Service都可以用这个来生成,代码如下:
mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
二,创建接口,通过第一步获取实例
有了可以获取接口实例的方法,然后创建一个接口,代码如下:
public interface MovieService{
//获取豆瓣Top250 榜单
@GET(“top250”)
Observable getTop250(@Query(“start”) int start, @Query(“count”) int count);
@FormUrlEncoded
@POST(“/x3/weather”)
Call getWeather(@Field(“cityId”) String cityId, @Field(“key”) String key);
}
三,创建一个业务Loader ,如XXXLoder,获取Observable并处理相关业务
创建 Loader 的原因:每一个Api 都写一个接口很麻烦,因此就把 请求逻辑 封装在 一个业务Loader 里面,一个 Loader 里面可以处理多个Api 接口。代码如下:
public class MovieLoader extends ObjectLoader {
private MovieService mMovieService;
public MovieLoader(){
mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
}
/**
获取电影列表 @param start @param count @return
*/
public Observable getMovie(int start, int count){
return observe(mMovieService.getTop250(start , count)).map(new Func1
@Override
public List call(MovieSubject movieSubject) {
return movieSubject.subjects;
}
});
}
public Observable getWeatherList(String cityId,String key){
return observe(mMovieService.getWeather(cityId , key)).map(new Func1
@Override
public String call(String s) {
//可以处理对应的逻辑后在返回
return s;
}
});
}
public interface MovieService{
//获取豆瓣Top250 榜单
@GET(“top250”)
Observable getTop250(@Query(“start”) int start, @Query(“count”)int count);
@FormUrlEncoded
@POST(“/x3/weather”)
Call getWeather(@Field(“cityId”) String cityId, @Field(“key”) String key);
}
}
创建一个MovieLoader,构造方法中生成了mHttpService,而 Service 中可以定义和业务相关的多个api,比如:例子中的HttpService中,
可以定义和电影相关的多个api,获取电影列表、获取电影详情、搜索电影等api,就不用定义多个接口了。
MovieLoader 是从 ObjectLoader 中继承下来的,ObjectLoader 提取了一些公共的操作。代码如下:
/**
将一些重复的操作提出来,放到父类以免Loader 里每个接口都有重复代码
*/
public class ObjectLoader {
/**
@param observable @param @return
*/
protected Observable observe(Observable observable){
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
四,Activity/Fragment 中的调用
创建Loader实例:
mMovieLoader = new MovieLoader();
通过Loader 调用方法获取结果,代码如下:
/**
获取电影列表
*/
private void getMovieList(){
mMovieLoader.getMovie(0,10).subscribe(new Action1() {
@Override
public void call(List movies) {
mMovieAdapter.setMovies(movies);
mMovieAdapter.notifyDataSetChanged();
}
}, new Action1() {
@Override
public void call(Throwable throwable) {
Log.e(“TAG”,“error message:”+throwable.getMessage());
}
});
}
五,统一处理结果和错误
1.统一处理请求结果:
现实项目中,所有接口的返回结果都是同一格式,如:
{
“status”: 200,
“message”: “成功”,
“data”: {}
}
在请求api 接口的时候,只关心想要的数据,也就上面的 data{ },其他的东西不太关心,请求失败 的时候可以根据 status 判断进行 错误处理。
包装返回结果:首先需要根据服务端定义的 JSON 结构创建一个 BaseResponse 类,代码如下:
/**
网络请求结果 基类
*/
public class BaseResponse {
public int status;
public String message;
public T data;
public boolean isSuccess(){
return status == 200;
}
}
有了统一的格式数据后,我们需要 剥离出data{ }返回给 上层调用者,创建一个 PayLoad 类,代码如下:
/**
剥离 最终数据
*/
public class PayLoad implements Func1 @Override public T call(BaseResponse tBaseResponse) {//获取数据失败时,包装一个Fault 抛给上层处理错误 if(!tBaseResponse.isSuccess()){ throw new Fault(tBaseResponse.status,tBaseResponse.message); } return tBaseResponse.data; } } PayLoad 继承自 Func1,接收一个BaseResponse , 就是接口返回的 JSON 数据结构,返回的是 T,就是data{ },判断是否请求成功,请求成功 返回Data,请求失败 包装成一个 Fault 返回给上层统一处理错误。 在Loader类里面获取结果后,通过map 操作符剥离数据。代码如下: public Observable return observe(mMovieService.getTop250(start,count)) .map(new PayLoad } 2.统一处理错误: 在PayLoad 类里面,请求失败时,抛出了一个Fault 异常给上层,我在Activity/Fragment 中拿到这个异常,然后判断错误码,进行异常处理。在onError () 中添加。 对应 错误码 处理 相应的错误,代码如下: public void call(Throwable throwable) { Log.e(“TAG”,“error message:”+throwable.getMessage()); if(throwable instanceof Fault){ Fault fault = (Fault) throwable; if(fault.getErrorCode() == 404){ //错误处理 }else if(fault.getErrorCode() == 500){ //错误处理 }else if(fault.getErrorCode() == 501){ //错误处理 } } } 六,添加公共参数 实际项目中,每个接口都有一些基本的相同的参数,我们称之为公共参数,比如:userId、userToken、userName、deviceId等等,我们不必每个接口都去写,可以写一个拦截器,在拦截器里面拦截请求,为每个请求都添加相同的公共参数。 拦截器代码如下: /* 拦截器 向请求头里添加公共参数 */ public class HttpCommonInterceptor implements Interceptor { private Map public HttpCommonInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d(“HttpCommonInterceptor”,“add common params”); Request oldRequest = chain.request(); // 添加新的参数,添加到url 中 /*HttpUrl.Builder authorizedUrlBuilder = oldRequest.url().newBuilder() .scheme(oldRequest.url().scheme()) .host(oldRequest.url().host());*/ // 新的请求 Request.Builder requestBuilder = oldRequest.newBuilder(); requestBuilder.method(oldRequest.method(), oldRequest.body()); //添加公共参数,添加到header中 if(mHeaderParamsMap.size() > 0){ for(Map.Entry requestBuilder.header(params.getKey(),params.getValue()); } } Request newRequest = requestBuilder.build(); return chain.proceed(newRequest); } public static class Builder{ HttpCommonInterceptor mHttpCommonInterceptor; public Builder(){ mHttpCommonInterceptor = new HttpCommonInterceptor(); } public Builder addHeaderParams(String key, String value){ mHttpCommonInterceptor.mHeaderParamsMap.put(key,value); return this; } public Builder addHeaderParams(String key, int value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, float value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, long value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, double value){ return addHeaderParams(key, String.valueOf(value)); } public HttpCommonInterceptor build(){ return mHttpCommonInterceptor; } } } 以上就是添加公共参数的拦截器,在 RetrofitServiceManager 类里面加入OkHttpClient 配置就好了。 代码如下: // 添加公共参数拦截器 HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder() .addHeaderParams(“paltform”,“android”) .addHeaderParams(“userToken”,“1234343434dfdfd3434”) .addHeaderParams(“userId”,“123445”) .build(); builder.addInterceptor(commonInterceptor); 项目使用篇 ----->插入广告!本项目来源于金融研习社App,金融理财类的在线教育 项目是基于RxJava1 1.引入依赖: compile ‘com.google.code.gson:gson:2.6.2’//导入Gson 库 //导入RxJava 和 RxAndroid compile ‘io.reactivex.rxjava2:rxandroid:2.0.2’ compile ‘io.reactivex.rxjava2:rxjava:2.x.y’ compile ‘com.squareup.retrofit2:retrofit:2.3.0’//导入retrofit compile ‘com.squareup.retrofit2:converter-gson:2.3.0’//转换器,请求结果转换成Model compile ‘com.squareup.retrofit2:adapter-rxjava2:2.3.0’//配合Rxjava 使用 compile ‘com.squareup.okhttp3:logging-interceptor:3.8.1’//添加HttpLoggingInterceptor进行调试 2.创建一个HttpService接口: public interface HttpService { /** 获取用户详细资料 */ @POST(“api/XXX/GetUserAllDetails”) Observable getUserAllDetails(@Body GetUserAllDetailsRequestBean bean); /** @param apkUrl 下载地址 */ @GET() @Streaming Call downloadNewApk(@Url String apkUrl); /** 获取推广大使分享图片 */ @GET(“api/XXX/InvitedImage”) Observable getInvitedImage(@QueryMap Map } 3.创建http请求类,并在里面初始化并配置Retrofit和OkHttp: public class HttpMethods { public String TAG = “HttpMethods”; public static final String CACHE_NAME = “xxx”; public static String BASE_URL = URLConstant.BASE_URL; private static final int DEFAULT_CONNECT_TIMEOUT = 30; private static final int DEFAULT_WRITE_TIMEOUT = 30; private static final int DEFAULT_READ_TIMEOUT = 30; private Retrofit retrofit; private HttpService httpService; /** 请求失败重连次数 */ private int RETRY_COUNT = 0; private OkHttpClient.Builder okHttpBuilder; //构造方法私有 private HttpMethods() { //手动创建一个OkHttpClient并设置超时时间 okHttpBuilder = new OkHttpClient.Builder(); /** 设置缓存 */ File cacheFile = new File(ApplicationContext.context.getExternalCacheDir(), CACHE_NAME); Cache cache = new Cache(cacheFile, 1024 * 1024 * 50); Interceptor cacheInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!NetUtil.isNetworkConnected()) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); } Response response = chain.proceed(request); if (!NetUtil.isNetworkConnected()) { int maxAge = 0; // 有网络时 设置缓存超时时间0个小时 response.newBuilder() .header(“Cache-Control”, “public, max-age=” + maxAge) .removeHeader(CACHE_NAME)// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 .build(); } else { // 无网络时,设置超时为4周 int maxStale = 60 * 60 * 24 * 28; response.newBuilder() .header(“Cache-Control”, “public, only-if-cached, max-stale=” + maxStale) .removeHeader(CACHE_NAME) .build(); } return response; } }; okHttpBuilder.cache(cache).addInterceptor(cacheInterceptor); /** 设置头信息 */ Interceptor headerInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request.Builder requestBuilder = originalRequest.newBuilder() .addHeader(“Accept-Encoding”, “gzip”) .addHeader(“Accept”, “application/json”) .addHeader(“Content-Type”, “application/json; charset=utf-8”) .method(originalRequest.method(), originalRequest.body()); requestBuilder.addHeader(“Authorization”, "Bearer " + BaseConstant.TOKEN);//添加请求头信息,服务器进行token有效性验证 Request request = requestBuilder.build(); return chain.proceed(request); } }; okHttpBuilder.addInterceptor(headerInterceptor); // if (BuildConfig.DEBUG) { HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Logger.d(message); } }); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); //设置 Debug Log 模式 okHttpBuilder.addInterceptor(loggingInterceptor); // } /** 设置超时和重新连接 */ okHttpBuilder.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS); okHttpBuilder.readTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS); okHttpBuilder.writeTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS); //错误重连 okHttpBuilder.retryOnConnectionFailure(true); retrofit = new Retrofit.Builder() .client(okHttpBuilder.build()) .addConverterFactory(GsonConverterFactory.create())//json转换成JavaBean .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); httpService = retrofit.create(HttpService.class); } //在访问HttpMethods时创建单例 private static class SingletonHolder { private static final HttpMethods INSTANCE = new HttpMethods(); } //获取单例 public static HttpMethods getInstance() { return SingletonHolder.INSTANCE; } /** 获取retrofit */ public Retrofit getRetrofit() { return retrofit; } public void changeBaseUrl(String baseUrl) { retrofit = new Retrofit.Builder() .client(okHttpBuilder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(baseUrl) .build(); httpService = retrofit.create(HttpService.class); } /** 获取httpService */ public HttpService getHttpService() { return httpService; } /** 设置订阅 和 所在的线程环境 */ public void toSubscribe(Observable o, DisposableObserver s) { o.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(RETRY_COUNT)//请求失败重连次数 .subscribe(s); } } 4.设置回调: 调用者自己对请求数据进行处理 成功时 通过result是否等于1分别回调onSuccees和onFault,默认处理了401错误转登录。 public class OnSuccessAndFaultSub extends DisposableObserver implements ProgressCancelListener { /** 是否需要显示默认Loading */ private boolean showProgress = true; private OnSuccessAndFaultListener mOnSuccessAndFaultListener; private Context context; private WaitProgressDialog progressDialog; /** @param mOnSuccessAndFaultListener 成功回调监听 */ public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener) { this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener; } /** @param mOnSuccessAndFaultListener 成功回调监听 @param context 上下文 */ public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context) { this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener; this.context = context; progressDialog = new WaitProgressDialog(context, this); } /** @param mOnSuccessAndFaultListener 成功回调监听 @param context 上下文 @param showProgress 是否需要显示默认Loading */ public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, boolean showProgress) { this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener; this.context = context; progressDialog = new WaitProgressDialog(context, this); this.showProgress = showProgress; } private void showProgressDialog() { if (showProgress && null != progressDialog) { progressDialog.show(); } } private void dismissProgressDialog() { if (showProgress && null != progressDialog) { progressDialog.dismiss(); } } /** 订阅开始时调用 显示ProgressDialog */ @Override public void onStart() { showProgressDialog(); } /** 完成,隐藏ProgressDialog */ @Override public void onComplete() { dismissProgressDialog(); progressDialog = null; } /** 对错误进行统一处理 隐藏ProgressDialog */ @Override public void onError(Throwable e) { try { if (e instanceof SocketTimeoutException) {//请求超时 } else if (e instanceof ConnectException) {//网络连接超时 mOnSuccessAndFaultListener.onFault(“网络连接超时”); } else if (e instanceof SSLHandshakeException) {//安全证书异常 mOnSuccessAndFaultListener.onFault(“安全证书异常”); } else if (e instanceof HttpException) {//请求的地址不存在 int code = ((HttpException) e).code(); if (code == 504) { mOnSuccessAndFaultListener.onFault(“网络异常,请检查您的网络状态”); } else if (code == 404) { mOnSuccessAndFaultListener.onFault(“请求的地址不存在”); } else { mOnSuccessAndFaultListener.onFault(“请求失败”); } } else if (e instanceof UnknownHostException) {//域名解析失败 mOnSuccessAndFaultListener.onFault(“域名解析失败”); } else { mOnSuccessAndFaultListener.onFault(“error:” + e.getMessage()); } } catch (Exception e2) { e2.printStackTrace(); } finally { Log.e(“OnSuccessAndFaultSub”, “error:” + e.getMessage()); dismissProgressDialog(); progressDialog = null; } } /** 当result等于1回调给调用者,否则自动显示错误信息,若错误信息为401跳转登录页面。 ResponseBody body = response.body();//获取响应体 InputStream inputStream = body.byteStream();//获取输入流 byte[] bytes = body.bytes();//获取字节数组 String str = body.string();//获取字符串数据 */ @Override public void onNext(ResponseBody body) { try { final String result = CompressUtils.decompress(body.byteStream()); Log.e(“body”, result); JSONObject jsonObject = new JSONObject(result); int resultCode = jsonObject.getInt(“ErrorCode”); if (resultCode == 1) { mOnSuccessAndFaultListener.onSuccess(result); } else { String errorMsg = jsonObject.getString(“ErrorMessage”); mOnSuccessAndFaultListener.onFault(errorMsg); Log.e(“OnSuccessAndFaultSub”, "errorMsg: " + errorMsg); } } catch (Exception e) { e.printStackTrace(); } } /** 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求 */ @Override public void onCancelProgress() { if (!this.isDisposed()) { this.dispose(); } } } *请求服务loading关闭监听 */ public interface ProgressCancelListener { void onCancelProgress(); } 5.请求的用法: 建议分类成不同的api,以便快速查找 api里面进行观察者和被观察者的订阅 private void getUserData() { OnSuccessAndFaultListener l = new OnSuccessAndFaultListener() { @Override public void onSuccess(String result) {//成功回调 YXSPreference.setUserData(result); userDataBean = GsonUtils.fromJson(result, UserDetailResponseBean.class); fullData(); } @Override public void onFault(String errorMsg) {//失败的回调 SnackBarManager.showShortMsg(getActivity(), errorMsg); } }; UserApi.getUserAllDetails(new OnSuccessAndFaultSub(l) , YXSPreference.getMemberId() 自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。 深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化! 由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新 如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android) 最后 写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。 一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长! ult); userDataBean = GsonUtils.fromJson(result, UserDetailResponseBean.class); fullData(); } @Override public void onFault(String errorMsg) {//失败的回调 SnackBarManager.showShortMsg(getActivity(), errorMsg); } }; UserApi.getUserAllDetails(new OnSuccessAndFaultSub(l) , YXSPreference.getMemberId() 自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。 深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 [外链图片转存中…(img-u7QK2jSa-1712833845595)] [外链图片转存中…(img-ocHrqxEG-1712833845596)] [外链图片转存中…(img-eJPs2XWm-1712833845596)] [外链图片转存中…(img-5DhhKNkr-1712833845596)] [外链图片转存中…(img-DGldkp8X-1712833845597)] [外链图片转存中…(img-L2LFcx0M-1712833845597)] 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化! 由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新 如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android) [外链图片转存中…(img-1wnhy9LR-1712833845597)] 最后 写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。 [外链图片转存中…(img-Izpnvmti-1712833845598)] 一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长! [外链图片转存中…(img-SIJEkXgf-1712833845598)] 参考阅读 getMovie(int start, int count){
发表评论