先来看一下封装过后的Retrofit如何使用。
RetrofitHelper.getApiService()
.getMezi()
.compose(this.<List>bindToLifecycle())
.compose(ProgressUtils.<List>applyProgressBar(this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultObserver<List>() {
@Override
public void onSuccess(List response) {
showToast(“请求成功,妹子个数为” + response.size());
}
});
没错,就是这么简洁的一个链式调用,可以显示加载动画,还加入了Retrofit生命周期的管理。
开始之前需要先在module项目里的Gradle文件中添加用到的依赖库
compile “io.reactivex.rxjava2:rxjava:
r
o
o
t
P
r
o
j
e
c
t
.
e
x
t
.
r
x
j
a
v
a
2
V
e
r
s
i
o
n
"
c
o
m
p
i
l
e
"
c
o
m
.
s
q
u
a
r
e
u
p
.
r
e
t
r
o
f
i
t
2
:
r
e
t
r
o
f
i
t
:
rootProject.ext.rxjava2Version" compile "com.squareup.retrofit2:retrofit:
rootProject.ext.rxjava2Version"compile"com.squareup.retrofit2:retrofit:rootProject.ext.retrofit2Version”
compile “com.squareup.retrofit2:converter-scalars:
r
o
o
t
P
r
o
j
e
c
t
.
e
x
t
.
r
e
t
r
o
f
i
t
2
V
e
r
s
i
o
n
"
c
o
m
p
i
l
e
"
c
o
m
.
s
q
u
a
r
e
u
p
.
r
e
t
r
o
f
i
t
2
:
c
o
n
v
e
r
t
e
r
−
g
s
o
n
:
rootProject.ext.retrofit2Version" compile "com.squareup.retrofit2:converter-gson:
rootProject.ext.retrofit2Version"compile"com.squareup.retrofit2:converter−gson:rootProject.ext.retrofit2Version”
compile “com.squareup.retrofit2:adapter-rxjava2:
r
o
o
t
P
r
o
j
e
c
t
.
e
x
t
.
r
e
t
r
o
f
i
t
2
V
e
r
s
i
o
n
"
c
o
m
p
i
l
e
′
c
o
m
.
j
a
k
e
w
h
a
r
t
o
n
.
r
e
t
r
o
f
i
t
:
r
e
t
r
o
f
i
t
2
−
r
x
j
a
v
a
2
−
a
d
a
p
t
e
r
:
1.0.
0
′
c
o
m
p
i
l
e
′
i
o
.
r
e
a
c
t
i
v
e
x
.
r
x
j
a
v
a
2
:
r
x
a
n
d
r
o
i
d
:
2.0.
1
′
c
o
m
p
i
l
e
′
c
o
m
.
s
q
u
a
r
e
u
p
.
o
k
h
t
t
p
3
:
l
o
g
g
i
n
g
−
i
n
t
e
r
c
e
p
t
o
r
:
3.4.
1
′
c
o
m
p
i
l
e
"
c
o
m
.
t
r
e
l
l
o
.
r
x
l
i
f
e
c
y
c
l
e
2
:
r
x
l
i
f
e
c
y
c
l
e
:
rootProject.ext.retrofit2Version" compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' compile "com.trello.rxlifecycle2:rxlifecycle:
rootProject.ext.retrofit2Version"compile′com.jakewharton.retrofit:retrofit2−rxjava2−adapter:1.0.0′compile′io.reactivex.rxjava2:rxandroid:2.0.1′compile′com.squareup.okhttp3:logging−interceptor:3.4.1′compile"com.trello.rxlifecycle2:rxlifecycle:rootProject.ext.rxlifecycle”
//compile “com.trello.rxlifecycle2:rxlifecycle-android:
r
o
o
t
P
r
o
j
e
c
t
.
e
x
t
.
r
x
l
i
f
e
c
y
c
l
e
"
c
o
m
p
i
l
e
"
c
o
m
.
t
r
e
l
l
o
.
r
x
l
i
f
e
c
y
c
l
e
2
:
r
x
l
i
f
e
c
y
c
l
e
−
c
o
m
p
o
n
e
n
t
s
:
rootProject.ext.rxlifecycle" compile "com.trello.rxlifecycle2:rxlifecycle-components:
rootProject.ext.rxlifecycle"compile"com.trello.rxlifecycle2:rxlifecycle−components:rootProject.ext.rxlifecycle”
为了方便依赖库版本的修改我们采用”io.reactivex.rxjava2:rxjava:$rootProject.ext.rxjava2Version”这中方式添加依赖,因此需要在project的build.gradle文件的加上以下内容:
ext {
supportLibVersion = ‘25.1.0’
butterknifeVersion = ‘8.5.1’
rxjava2Version = ‘2.0.8’
retrofit2Version = ‘2.2.0’
rxlifecycle=‘2.1.0’
gsonVersion = ‘2.8.0’
}
下面将通过几个小节对本次封装作详细的解析:
- 服务器响应数据的基类BasicResponse
- 构建初始化Retrofit的工具类IdeaApi
- 通过GsonConverterFactory获取真实响应数据
- 封装DefaultObserver处理服务器响应
- 处理加载Loading
- 管理Retrofit生命周期
- 如何使用封装
- 小结
一.服务器响应数据的基类BasicResponse。
假定服务器返回的Json数据格式如下:
{
“code”: 200,
“message”: “成功”,
“content”: {
…
}
}
根据Json数据格式构建我们的BasicResponse(BasicResponse中的字段内容需要根据自己服务器返回的数据确定)。代码如下:
public class BasicResponse {
private int code;
private String message;
private T content;
…此处省去get、set方法。
二.构建初始化Retrofit的工具类IdeaApi。
该类通过RetrofitUtils来获取ApiService的实例。代码如下:
public class IdeaApi {
public static T getApiService(Class cls,String baseUrl) {
Retrofit retrofit = RetrofitUtils .getRetrofitBuilder(baseUrl).build();
return retrofit.create(cls);
}
}
RetrofitUtils用来构建Retrofit.Builder,并对OkHttp做以下几个方面的配置:
-
设置日志拦截器,拦截服务器返回的json数据。Retrofit将请求到json数据直接转换成了实体类,但有时候我们需要查看json数据,Retrofit并没有提供直接获取json数据的功能。因此我们需要自定义一个日志拦截器拦截json数据,并输入到控制台。
-
设置Http请求头。给OkHttp 添加请求头拦截器,配置请求头信息。还可以为接口统一添加请求头数据。例如,把用户名、密码(或者token)统一添加到请求头。后续每个接口的请求头中都会携带用户名、密码(或者token)数据,避免了为每个接口单独添加。
-
为OkHttp配置缓存。同样可以同过拦截器实现缓存处理。包括控制缓存的最大生命值,控制缓存的过期时间。
-
如果采用https,我们还可以在此处理证书校验以及服务器校验。
-
为Retrofit添加GsonConverterFactory。此处是一个比较重要的环节,将在后边详细讲解。
RetrofitUtils 代码如下:
public class RetrofitUtils {
public static OkHttpClient.Builder getOkHttpClientBuilder() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
try {
LogUtils.e(“OKHttp-----”, URLDecoder.decode(message, “utf-8”));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
LogUtils.e(“OKHttp-----”, message);
}
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
File cacheFile = new File(Utils.getContext().getCacheDir(), “cache”);
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
return new OkHttpClient.Builder()
.readTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(new HttpHeaderInterceptor())
.addNetworkInterceptor(new HttpCacheInterceptor())
// .sslSocketFactory(SslContextFactory.getSSLSocketFactoryForTwoWay()) // https认证 如果要使用https且为自定义证书 可以去掉这两行注释,并自行配制证书。
// .hostnameVerifier(new SafeHostnameVerifier())
.cache(cache);
}
public static Retrofit.Builder getRetrofitBuilder(String baseUrl) {
Gson gson = new GsonBuilder().setDateFormat(“yyyy-MM-dd HH:mm:ss”).serializeNulls().create();
OkHttpClient okHttpClient = RetrofitUtils.getOkHttpClientBuilder().build();
return new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseUrl);
}
}
三.通过GsonConverterFactory获取真实响应数据
在第一节中我们构建了服务器响应数据BasicResponse,BasicResponse由code、message、和content三个字段。其中code为服务器返回的错误码。我们会事先和服务器约定成功时的code值,比如200表示请求成功。但通常在请求服务器数据过程中免不了会出现各种错误。例如用户登录时密码错误、请求参数错误的情况。此时服务器会根据错误情况返回对应的错误码。一般来说,我们只关心成功时即code为200时的content数据。而对于code不为200时我们只需要给出对应的Toast提示即可。事实上我们对我们有用的仅仅时code为200时的content数据。因此我们可以考虑过滤掉code和message,在请求成功的回调中只返回content的内容。
在此种情况下就需要我们通过自定义GsonConverterFactory来实现了。我们可以直接从Retrofit的源码中copy出GsonConverterFactory的三个相关类来做修改。
其中最终要的一部分是修改GsonResponseBodyConverter中的convert方法。在该方法中拿到服务器响应数据并判断code是否为200。如果是,则获取到content并返回,如果不是,则在此处可以抛出对应的自定义的异常。然后再Observer中统一处理异常情况。GsonResponseBodyConverter代码如下:
final class GsonResponseBodyConverter implements Converter<ResponseBody, Object> {
private final TypeAdapter adapter;
GsonResponseBodyConverter(TypeAdapter adapter) {
this.adapter = adapter;
}
@Override
public Object convert(ResponseBody value) throws IOException {
try {
BasicResponse response = (BasicResponse) adapter.fromJson(value.charStream());
if (response.getCode()==200) {
return response.getResults();
} else {
// 特定 API 的错误,在相应的 DefaultObserver 的 onError 的方法中进行处理
throw new ServerResponseException(response.getCode(), response.getMessage());
}
} finally {
value.close();
}
return null;
}
}
四.构建DefaultObserver处理服务器响应。
上一节中我们讲到了在请求服务器时可能出现的一些例如密码错误、参数错误的情况,服务器给我们返回了对应的错误码,我们根据错误码抛出了对应自定义异常。除此之外在我们发起网络请求时还可能发生一些异常情况。例如没有网络、请求超时或者服务器返回了数据但在解析时出现了数据解析异常等。对于这样的情况我们也要进行统一处理的。那么我们就需要自定义一个DefaultObserver类继承Observer,并重写相应的方法。
该类中最重要的两个方法时onNext和onError。
1.在服务器返回数据成功的情况下会回调到onNext方法。因此我们可以在DefaultObserver中定义一个抽象方法onSuccess(T response),在调用网络时重写onSuccess方法即可。
2.如果在请求服务器过程中出现任何异常,都会回调到onError方法中。包括上节中我们自己抛出的异常都会回调到onError。因此我们的重头戏就是处理onError。在onError中我们根据异常信息给出对应的Toast提示即可。
DefaultObserver类的代码如下:
public abstract class DefaultObserver implements Observer {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(T response) {
onSuccess(response);
onFinish();
}
@Override
public void onError(Throwable e) {
LogUtils.e(“Retrofit”, e.getMessage());
if (e instanceof HttpException) { // HTTP错误
onException(ExceptionReason.BAD_NETWORK);
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) { // 连接错误
onException(ExceptionReason.CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) { // 连接超时
onException(ExceptionReason.CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) { // 解析错误
onException(ExceptionReason.PARSE_ERROR);
}else if(e instanceof ServerResponseException){
onFail(e.getMessage());
} else {
onException(ExceptionReason.UNKNOWN_ERROR);
}
onFinish();
}
@Override
public void onComplete() {
}
/**
- 请求成功
- @param response 服务器返回的数据
*/
abstract public void onSuccess(T response);
/**
- 服务器返回数据,但响应码不为200
*/
public void onFail(String message) {
ToastUtils.show(message);
}
public void onFinish(){}
/**
- 请求异常
- @param reason
*/
public void onException(ExceptionReason reason) {
switch (reason) {
case CONNECT_ERROR:
ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT);
break;
case CONNECT_TIMEOUT:
ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT);
break;
case BAD_NETWORK:
ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT);
break;
case PARSE_ERROR:
ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT);
break;
case UNKNOWN_ERROR:
default:
ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT);
break;
}
}
/**
- 请求网络失败原因
/
public enum ExceptionReason {
/* - 解析数据失败
/
PARSE_ERROR,
/* - 网络问题
/
BAD_NETWORK,
/* - 连接错误
/
CONNECT_ERROR,
/* - 连接超时
/
CONNECT_TIMEOUT,
/* - 未知错误
*/
UNKNOWN_ERROR,
}
}
五.处理加载Loading
关于Loading我们可以通过RxJava的compose操作符来做一个非常优雅的处理。首先定义一个ProgressUtils工具类,然后通过RxJava的ObservableTransformer做一个变换来处理Loading。想要显示Loading,只需要加上.compose(ProgressUtils.< T >applyProgressBar(this))
即可。
ProgressUtils代码如下:
public class ProgressUtils {
public static ObservableTransformer<T, T> applyProgressBar(
@NonNull final Activity activity, String msg) {
final WeakReference activityWeakReference = new WeakReference<>(activity);
final DialogUtils dialogUtils = new DialogUtils();
dialogUtils.showProgress(activityWeakReference.get());
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource apply(Observable upstream) {
return upstream.doOnSubscribe(new Consumer() {
@Override
public void accept(Disposable disposable) throws Exception {
}
}).doOnTerminate(new Action() {
@Override
public void run() throws Exception {
Activity context;
if ((context = activityWeakReference.get()) != null
&& !context.isFinishing()) {
dialogUtils.dismissProgress();
}
}
}).doOnSubscribe(new Consumer() {
@Override
public void accept(Disposable disposable) throws Exception {
/Activity context;
if ((context = activityWeakReference.get()) != null
&& !context.isFinishing()) {
dialogUtils.dismissProgress();
}/
}
});
}
};
}
public static ObservableTransformer<T, T> applyProgressBar(
@NonNull final Activity activity) {
return applyProgressBar(activity, “”);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/7cca0da7b15b2bb3946bbb2d56383671.jpeg)
总结
首先是感觉自己的基础还是不够吧,大厂好像都喜欢问这些底层原理。
另外一部分原因在于资料也还没有看完,一面时凭借那份资料考前突击恶补个几天居然也能轻松应对(在这里还是要感谢那份资料,真的牛),于是自我感觉良好,资料就没有怎么深究下去了。
之前的准备只涉及了Java、Android、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。
下一步还是要查漏补缺,进行针对性复习。
最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!希望大家不要犯和我一样的错误呀!!!一定要看完!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
方面,面对面试官对其他基础课程的考察显得捉襟见肘。
下一步还是要查漏补缺,进行针对性复习。
最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!希望大家不要犯和我一样的错误呀!!!一定要看完!
[外链图片转存中…(img-1xUPkJF2-1712391992543)]
[外链图片转存中…(img-2N05l6Fj-1712391992543)]
[外链图片转存中…(img-lq1qSOfs-1712391992544)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!