在介绍Android中相关网络框架的时候,我们介绍过了Square公司制作的OkHttp和OKIo。事实上,这两个框架配合使用的时候,基本上已经可以完成大部分的网络业务需求了。不过,学习是永远没有尽头的。这篇博客中,我们将介绍同样由Square公司制作,目前同样流行的另一个网络框架——Retrofit
Retrofit是一个现在比较火的网络请求框架,它的底层是依靠OkHttp实现的。确切的讲,Retrofit是对OkHttp的进一步封装,它的功能更加强大,支持同步和异步、支持多种数据的解析(默认使用Gson),也支持RxJava。
Retrofit的一个优势,就是简洁易用,它通过注解配置网络请求的参数,采用大量的设计模式来简化我们的使用。而且它的拓展性也做的相当的好,Retrofit的功能模块高度封装,高内聚低耦合,我们可以自定义自己想要的组件,比如说我们可以自己选择解析工具而不用默认的Gson,实现了高度的定制性。
除此之外,Retrofit还有诸如性能好,处理速度快,代码简化等优势,给足了理由让我们去尝试使用这款网络请求框架。
在使用Retrofit之前,我们应该先了解它的特性。使用Retrofit的过程中,由于其大量注解开发的形式,我觉得十分类似于Java中的Spring MVC框架,通过标识专属的注解,就可以达到快速的配置和开发,一些Retrofit常见的方法注解如图所示:
除此之外,一些常见的字段注解如图所示:
将这些注解进行整理,可以得到一张大致的结构图:
其中:
看到这么多的注解,是否有一种头晕目眩的感觉?没关系,在本篇博客中,只会演示比较常用的注解:@GET,@POST,@Path,@Query
,其他的代码可以根据需求进行相应地调用,调用过程是类似的,具体的api调用还是那句话:查看Retrofit官网获取更多的相关信息。
在使用任何框架之前,集成都是第一步。由于Retrofit已经用到了OkHttp,所以这里只需要导入Retrofit一个依赖即可,修改module下的build.gradle,代码如下:
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
修改完成后Sync一下,确保Retrofit集成到了你的项目中。
这里的配置主要是针对在使用Retrofit遇到的一个坑,简单提一下避免读者在使用的时候踩坑。
问题:导入Retrofit依赖后,在build工程后出现如图所示的错误
原因:从出错原因可以很简单地看出,使用Retrofit需要你的Android版本最低达到Android O(即api 26),所以只需要修改有关此处的配置信息即可。
解决方法:在项目下的build.gradle文件添加一个compileOptions
闭包,声明JDK版本,代码如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.androidframelearn.http_retrofit"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
接下来,我们直接开始布局文件activity_main.xml的编写。该布局很简单,仅有四个按钮,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送get请求——通过http拼接网络参数"/>
<Button
android:id="@+id/btn_get_restful"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送get请求——通过restful拼接网络参数"/>
<Button
android:id="@+id/btn_post"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送post请求"/>
LinearLayout>
之后,我们简单用字符串封装一下要请求的URL。这里封装两个URL,是为了演示分别通过传统方式和RestFul风格提交GET请求的不同效果(现在很少有RestFul风格的免费开放api,所以这里就只能假设一个本地的Tomcat服务器,然后放置一个JSON文件来进行校验),代码如下(服务器URL不固定,根据自己的服务器路径名进行相应修改):
private static final String URL = "https://tcc.taobao.com/cc/json/";
private static final String LOCALURL = "http://10.0.2.2:8080/";
这里的URL
是一个目前还开放的可以查看电话归属地信息的api,而LOCALURL
则是本地服务器的api。
另外:保证你的URL路径输入到网页的链接栏中是可以访问的!这点很重要,如果你输入的网页是不存在的(即body为null),那么在调用response.body().string()
时就会报出空指针的异常,如图所示:
编写一个名为TestService的接口,其中有一个名为getInfoByHttp的方法,代码如下:
public interface TestService {
/**
/**
* 通过拼接网络参数的方式提交GET请求(下面的参数为https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15878896543)
* 注意:这种方式为http的参数传递方式,若使用这种方式请求,需要在注解上使用@Query而非@Path
* @param tel
* @return
*/
@GET("mobile_tel_segment.htm")
Call<ResponseBody> getInfoByHttp(@Query("tel") String tel);
}
我们定义了一个getInfoByHttp方法,其返回类型为retrofit的Call类型,尖括号里是okhttp的ResponseBody类型,@GET
注解的作用是声明采用GET的方法进行网络请求,而@Query
注解的作用是声明其之后的String tel
参数,将以路径的形式被拼接到@GET
注解括号里的{mobile_tel_segment.htm}
处。比如说,之后我们在调用getInfoByHttp方法时传入参数15878896543
,@GET
注解里将会被转化为 mobile_tel_segment.htm?tel=15878896543
,这个15878896543
又会被接到我们之前设置的baseUrl
之后,变成 https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15878896543
,这样就变成了一个完整的网络请求地址。
其实使用Retrofit进行GET请求有点类似于OkHttp,步骤可以大抵分为:
create()
方法来获取服务接口的实例enqueue()
异步提交请求response.body().string()
获取请求信息代码如下:
private void getByRetrogit() {
btn_get.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1.使用建造者模式创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.build();
// 2.使用Retrofit对象调用create()创建服务实例(BookService)
TestService testService = retrofit.create(TestService.class);
// 3.通过服务实例创建Call对象
Call<ResponseBody> call = testService.getInfoByHttp("15878896543");
// 4.通过Call对象构建网络请求(异步)
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String output = response.body().string();
Log.i(TAG,"GET请求发送成功!获取到的信息为:" + output);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG,"GET请求发送失败");
}
});
}
});
}
编写一个名为TestService的接口,其中有一个名为getInfoByRestFul的方法,代码如下:
public interface TestService {
/**
* 通过restful的方式提交GET请求(下面的参数为http://10.0.2.2:8080/update74.json)
* @param filename
* @return
*/
@GET("{filename}")
Call<ResponseBody> getInfoByRestFul(@Path("filename") String filename);
}
我们定义了一个getInfoByRestFul方法,其返回类型为retrofit的Call类型,尖括号里是okhttp的ResponseBody类型,@GET
注解的作用是声明采用GET的方法进行网络请求,而@Path
注解的作用是声明其之后的String filename
参数,将以路径的形式被替换到@GET
注解括号里的{filename}
处。比如说,之后我们在调用getInfoByRestFul方法时传入参数update74.json
,@GET
注解里将会被转化为 update74.json
,这个update74.json
又会被接到我们之前设置的baseUrl
之后,变成 http://10.0.2.2:8080/update74.json
,这样就变成了一个完整的网络请求地址。
其实使用Retrofit进行GET请求有点类似于OkHttp,步骤可以大抵分为:
create()
方法来获取服务接口的实例enqueue()
异步提交请求response.body().string()
获取请求信息代码如下:
private void getByRetrogitForRestful() {
btn_get_restful.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1.使用建造者模式创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(LOCALURL)
.build();
// 2.使用Retrofit对象调用create()创建服务实例(BookService)
TestService testService = retrofit.create(TestService.class);
// 3.通过服务实例创建Call对象
Call<ResponseBody> call = testService.getInfoByRestFul("update74.json");
// 4.通过Call对象构建网络请求(异步)
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String output = response.body().string();
Log.i(TAG,"GET请求发送成功!获取到的信息为:" + output);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG,"GET请求发送失败");
}
});
}
});
}
编写一个名为TestService的接口,其中有一个名为postInfoByRestFul的方法,代码如下:
public interface TestService {
/**
* 通过restful的方式提交POST请求(下面的参数为http://10.0.2.2:8080/update74.json)
* @param filename
* @return
*/
@POST("{filename}")
Call<ResponseBody> postInfoByRestFul(@Path("filename") String filename);
}
事实上,发送POST请求与GET请求的区别就是注解,需要使用@POST
。其他的调用方式跟上面是一样的,创建POST请求提交的步骤也是跟GET请求也一样,所以这里就不再赘述了,直接贴上代码:
private void postByRetrogit() {
btn_post.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1.使用建造者模式创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(LOCALURL)
.build();
// 2.使用Retrofit对象调用create()创建服务实例(BookService)
TestService testService = retrofit.create(TestService.class);
// 3.通过服务实例创建Call对象
Call<ResponseBody> call = testService.postInfoByRestFul("update74.json");
// 4.通过Call对象构建网络请求(异步)
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String output = response.body().string();
Log.i(TAG,"POST请求发送成功!获取到的信息为:" + output);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG,"POST请求发送失败");
}
});
}
});
}
AFL——Android框架学习