1. 上期回顾
上一期我们实现了Android的网络编程,通过Java代码在子线程中请求数据,再通过Java代码解析json数据,因为对于较复杂的json数据,通过java代码需要逐层解析比较困难,所以我们又通过第三方框架GSON,使用它可以直接将字符串解析成对象,并且封装成为一个实体类,配合实体类插件GsonFormatPlus,让我们只需通过简单的几行代码就实现数据解析
网络请求-》java-》OKHTTP
数据解析-》java解析-》GSON配合插件解析
网络编程的数据解析部分已经十分的方便了,但网络请求数据部分,通过java代码创建子线程请求到数据,然后又要通过
runOnUiThread(new Runnable()
方法线程切换到主线程将设数据设置到主线程的控件上,对于开发不便,我们这期学习如何使用OKHTTP框架实现网络请求,解决线程切换的问题
2. 原生HTTP网络访问的缺点
- 每次网络访问需要放在子线程里
- 每次得到结果需要返回到主线程才能更新控件显示
- 需要对各种情况进行处理,没有对状态码判断
3. OKHTTP
概述 - OkHttp (square.github.io)
HTTP是现代应用程序网络的方式。这就是我们交换数据和媒体的方式。有效地执行 HTTP 会使内容加载速度更快并节省带宽。
OkHttp 是一个默认高效的 HTTP 客户端:
- HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
- 连接池可减少请求延迟(如果 HTTP/2 不可用)。
- 透明 GZIP 可缩小下载大小。
- 响应缓存完全避免了重复请求的网络。
OkHttp 适用于 Android 5.0+(API 级别 21+)和 Java 8+。
3.1 框架引入
框架官网地址
https://github.com/square/okhttp
添加网络请求权限
添加依赖
implementation("com.squareup.okhttp3:okhttp:4.9.3")
3.2 框架使用
3.2.1 数据请求
publicclassMainActivityextendsAppCompatActivity {
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//发起请求
//无需手动创建子线程,框架默认帮我们创建
//1.创建一个okhttp对象
OkHttpClientokHttpClient=newOkHttpClient();
//2.创建一个Request对象,里面存放请求到的数据,调用Request类里的builder()方法,相当于调用了Request的静态方法
//.url请求的地址
//.get() 请求的方式 常用的请求方法1.get() 2.post() 这个由后端的接口决定
//.build()
Requestrequest=newRequest.Builder()
.url("http://121.4.44.56/object")
.get()
.build();
//3.将数据放到okHttpClient对象中
Callcall=okHttpClient.newCall(request);
//enqueue一个队列,程序可以发起多个请求,将这些请求存在队列中一个一个的处理
call.enqueue(newCallback() {
@Override
publicvoidonFailure(@NonNullCallcall, @NonNullIOExceptione) {
//请求失败
}
@Override
publicvoidonResponse(@NonNullCallcall, @NonNullResponseresponse) throwsIOException {
//请求成功,通过response对象返回请求到的数据
Stringresult=response.body().string();
Log.i("onResponse", "onResponse: "+result);
}
});
}
}
由此我们可以知道okhttp框架相较于传统的网络请求的优势:
优势一:无需自己创建子线程,okhttp自动帮我们创建
优势二:返回的数据不再是数据流,而是转换好的string字符串
优势三:自动处理异常情况,提供
onFailure()
方法
打印测试结果:
3.2.2 数据解析
这是返回的数据还是没有经过解析的,我们可以使用上节课的GSON搭配实体类插件GsonFormatPlus
来解析数据
第一步:添加gson依赖
//添加GSON依赖
implementation'com.google.code.gson:gson:2.9.0'
第二步:创建实体类
我们可以复制json数据,通过实体类插件GsonFormatPlus
快速创建
第三步:解析数据
//GSON搭配实体类插件`GsonFormatPlus`来解析数据
Gsongson=newGson();
Studentstudent=gson.fromJson(result,Student.class);
第四步:设置到文本控件上显示
我们先得给文本控件取一个id,为了方便查看,我设置了文本的大小和颜色
然后将解析出的数据设置到文本控件上
同样的在okhttp中我们需要切换线程到主线程来设置数据到控件上,说明okhttp框架只帮我们解决了构建子线程的问题,线程切换还是需要自己实现
运行可以看到名字已经解析出来并显示在虚拟机上
小tips:这里我不知为什么没写线程切换直接跑,也运行出来了,还报了一堆奇怪的错,没搞懂,先记录在这里
3.3 MainActivity完整代码
publicclassMainActivityextendsAppCompatActivity {
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextViewtxtView=findViewById(R.id.txtVId1);
//发起请求
//无需手动创建子线程,框架默认帮我们创建
//1.创建一个okhttp对象
OkHttpClientokHttpClient=newOkHttpClient();
//2.创建一个Request对象,里面存放请求到的数据,调用Request类里的builder()方法,相当于调用了Request的静态方法
//.url请求的地址
//.get() 请求的方式 常用的请求方法1.get() 2.post() 这个由后端的接口决定
//.build()
Requestrequest=newRequest.Builder()
.url("http://121.4.44.56/object")
.get()
.build();
//3.将数据放到okHttpClient对象中
Callcall=okHttpClient.newCall(request);
call.enqueue(newCallback() {//enqueue一个队列,程序可以发起多个请求,将这些请求存在队列中一个一个的处理
@Override
publicvoidonFailure(@NonNullCallcall, @NonNullIOExceptione) {
//请求失败,打印解析失败原因
Log.i("onFailure", "onFailure: "+e.getMessage());
}
@Override
publicvoidonResponse(@NonNullCallcall, @NonNullResponseresponse) throwsIOException {
//请求成功,通过response对象返回请求到的数据
//子线程
Stringresult=response.body().string();
Log.i("onResponse", "onResponse: "+result);
//GSON搭配实体类插件`GsonFormatPlus`来解析数据
Gsongson=newGson();
Studentstudent=gson.fromJson(result,Student.class);
//抛出到主线程
runOnUiThread(newRunnable() {
@Override
publicvoidrun() {
txtView.setText(student.name);
}
}
);
}
});
//优势一:无需自己创建子线程
//优势二:返回的数据不再是数据流,而是转换好的string字符串
//优势三:自动处理异常情况,提供`onFailure()`方法
}
}
4. 在线接口
fastmock可以让你在没有后端程序的情况下能真实地在线模拟ajax请求,你可以用fatmock实现项目初期纯前端的效果演示,也可以用fastmock实现开发中的数据模拟从而实现前后端分离。
5. Retrofit
网络请求-》java-》OKHTTP-》Retrofit
数据解析-》java解析-》GSON配合插件解析
Retrofit
是对http网络请求框架的封装,一般由okhttp
来负责网络请求,retrofit
对请求接口进行封装。retrofit
通过接口和注解来描述我们的网络请求,然后通过client
去调用okhttp
框架,通过addConverterFactory
来实现对返回的json数据进行处理,转换成我们需要的数据类型,可以理解为okhttp的加强版,底层封装了Okhttp。
5.1 框架引入
//添加okhttp依赖/
implementation("com.squareup.okhttp3:okhttp:4.9.3")
//添加Retrofit依赖
implementation'com.squareup.retrofit2:retrofit:2.9.0'
implementation'com.squareup.retrofit2:converter-gson:2.9.0'
5.2 框架使用
5.2.1 创建接口
创建一个接口,返回值是Call类型,使用泛型封装Student类,注意引入包不要用okhttp的
importretrofit2.Call;
importretrofit2.http.GET;
publicinterfaceApi {
//http://121.4.44.56/object
//使用get注解,放入请求的地址的最后一部分,前面的部分会在别处拼接
@GET("object")
Call<Student>getStudent();
}
5.2.2 数据请求&数据解析
回到MainActivity方法,将之前写的用okhttp框架的所有代码注释,以及删除对于的所有包
使用Retrofit框架来实现同样的功能
值得注意的是Retrofit框架在okhttp的基础上进一步封装,我们无需手动解析数据,也无需抛出线程
//使用Retrofit框架
//1..创建一个Retrofit对象
Retrofitretrofit=newRetrofit.Builder()
.baseUrl("http://121.4.44.56/")
.addConverterFactory(GsonConverterFactory.create())//表示以GSON框架解析数据
.build();
//2.获取到Api接口
//返回一个接口实例
Apiapi=retrofit.create(Api.class);
//3.通过Api获取到实体类Student
retrofit2.Call<Student>call=api.getStudent();
//4.enqueue一个队列,程序可以发起多个请求,将这些请求存在队列中一个一个的处理
call.enqueue(newCallback<Student>() {
@Override
publicvoidonResponse(Call<Student>call, Response<Student>response) {
//请求成功:返回的结果是一个call对象,而不再是response,
//默认将请求的结果抛回到主线程
Studentstudent=response.body();
txtView.setText(student.name);
}
@Override
publicvoidonFailure(Call<Student>call, Throwablet) {
//请求失败
}
});
5.2.3 日志拦截(Retrofit优化)
Retrofit框架看上去已经很完美了,但是因为它帮我们完成了数据解析,我们无法捕获数据解析是否成功,所以当数据解析失败时通过response.body();
是获取不到数据的
面对这个问题同样也有解决办法
我们引入一下依赖
//拦截日志依赖
implementation'com.squareup.okhttp3:logging-interceptor:4.9.3'
通过如下工具类进行拦截,使用这个工具类我们通过日志过滤的方法查看请求的状态和数据解析是否成功
publicclassRetrofitUtils {
publicstaticRetrofitgetRetrofit(Stringurl) {
//日志显示级别
HttpLoggingInterceptor.Levellevel=HttpLoggingInterceptor.Level.BODY;
//新建log拦截器
HttpLoggingInterceptorloggingInterceptor=newHttpLoggingInterceptor(newHttpLoggingInterceptor.Logger() {
@Override
publicvoidlog(Stringmessage) {
Log.d("RetrofitMessage","OkHttp====Message:"+message);
}
});
loggingInterceptor.setLevel(level);
//定制OkHttp
OkHttpClient.BuilderhttpClientBuilder=newOkHttpClient
.Builder();
//OkHttp进行添加拦截器loggingInterceptor
httpClientBuilder.addInterceptor(loggingInterceptor);
Retrofitretrofit=newRetrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client( httpClientBuilder.build())
.build();
returnretrofit;
}
}
使用这个工具类也能简化请求数据的代码,我们只需这么一行代码就能拿到Api接口实例
Apiapi=RetrofitUtils.getRetrofit("http://121.4.44.56/").create(Api.class);
通过debug级的日志过滤我们可以查看数据请求的状态信息和数据解析结果,也方便我们排错分析