一、android studio 项目中引入retrofit2:
在build.gradle文件下加入下面代码,然后点击同步项目的大象按钮:
implementation "com.squareup.retrofit2:retrofit:2.6.1"
implementation "com.squareup.retrofit2:converter-gson:2.6.1"
implementation 'com.squareup.retrofit2:converter-jackson:2.6.1'
1、请求的API及数据类编写:
通过建立一个接口,用来指定请求的url路径及请求的参数信息以及返回的数据类型。
const val pubHeader = "/app/pub/"
const val Content_type="Content-Type:application/json;charset=UTF-8"
//公开接口请求
public interface PubRequest {
//获取课程分组信息
@Headers(Content_type)
@POST(pubHeader+"course_group")
fun getCourseGroup(@Body u: CourseGroup):Call<RData<List<CourseGroup>>>
}
用@POST指定请求是个表单类型,后面是请求的url,@Headers指定请求的一些头部参数。@Body是个请求内容,对应一个实体数据类。CourseGroup的定义如下:
@JsonIgnoreProperties(ignoreUnknown = true) //反序列化时忽略未知属性
data class CourseGroup(val id:String?="",
val name:String?="",
val index:Int?=0,
val course:List<Course>?=null,
val cclass:String=""
) {
}
CourseGroup 数据类上面有个@JsonIgnoreProperties(ignoreUnknown = true),意思是当反序列json时,如果有无法对应的字段,可以忽略,避免解析错误。另外还有一个比较重要的注解@JsonProperty,可以明确设置返回的字段与本class的字段对应关系。
CourseGroup需要注意的是,属性都有默认值,设置了默认值,默认会构建一个空参数的构造函数,在解析时,会调用空的构造函数构造此对象,不然无法正常解析。
RData是个接收对象,用来规范化接收数据的,代码如下:
data class RData<T>(
val data:T? //返回的数据
,val code:String? //返回的代码
,val msg:String? //返回的消息
) {
}
2、准备开始请求:
请求的基础语法如下:
retfofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
//创建请求
retfofit?.create(PubRequest::class.java)
baseUrl是基础域名,addConverterFactory是指定后端返回的json解析类。域名url我们可以在string.xml中定义。然后通过retfofit?.create(PubRequest::class.java)建立请求。因为我是在Fragment中使用,所以就将请求的准备动作写在了BaseFragment,BaseFragment继承自Fragment:
open class BaseFragment :Fragment(){
var retfofit:Retrofit?=null
//获取域名
fun getDomain(): String{
return activity?.getString(R.string.domain).toString()
}
//建立请求
inline fun <reified T>createRequest():T?{
if (retfofit==null){
getRetfofit()
}
return retfofit?.create(T::class.java);
}
fun getRetfofit(){
retfofit = Retrofit.Builder()
.baseUrl(getDomain())
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
只要继承BaseFragment ,就可以直接使用如下代码进行创建请求:
//内部请求为多线程,需要回调,待完善
private fun getCourse(): String? {
var index_r_data:String?=null
var request = createRequest<PubRequest>()
var courseGroup = CourseGroup("", "", 0, null, getString(R.string.cclass))
request?.getCourseGroup(courseGroup)?.enqueue(object : Callback<RData<List<CourseGroup>>> {
//成功处理返回数据
override fun onResponse(call: Call<RData<List<CourseGroup>>>, response: Response<RData<List<CourseGroup>>>) {
body = response.body() as RData<List<CourseGroup>>
index_r_data = gson.toJson(body)
}
//失败处理
override fun onFailure(call: Call<RData<List<CourseGroup>>>, t: Throwable) {
t.printStackTrace()
Log.d(IndexFragment.javaClass.name, t.localizedMessage)
}
})
return index_r_data
}
var request = createRequest<PubRequest>() 是调用父类createRequest函数,创建PubRequest对象,需要通过指定具体创建的类型为PubRequest。然后调用getCourseGroup方法,传递数据对象,调用enqueue方法来处理返回的数据。
body = response.body() as RData<List<CourseGroup>> 可以直接将返回的数据转成对应对象类型。 index_r_data1 = gson.toJson(body) 是转成json字符串,方便放到Preferences进行持久化,因为Preferences不支持对象存储。使用时,可以通过如下代码在转成对象:
var course=gson.fromJson(index_r_data1,RData::class.java) as RData
这客户端的代码就完成了。
二、springboot 服务端接受与返回:
服务端使用springboot和mybaits来编写。
控制器代码:
@RestController
@RequestMapping("/app/pub")
@Slf4j
public class AppPlusController {
@Autowired
AppCourseMapper appCourseMapper;
//app首页课程分组
@RequestMapping(value = "/course_group",method = RequestMethod.POST)
public R<List<AppCourseGroup>> course_group(@RequestBody HashMap<String, String> map) {
String cclass=map.get("cclass");
log.info("接收到客户端请求app分类:"+cclass);
return R.isOk().msg("ok").code(1).data(appCourseMapper.courseGroup(cclass));
}
}
控制器为@RestController类型,@RequestMapping("/app/pub")指定请求的通用uri资源地址,@Slf4j可以无需声明log对象而直接使用log。
course_group()方法用 @RequestMapping(value = "/course_group",method = RequestMethod.POST)指定接收的具体URI及方法类型为POST。方法里使用@RequestBody来讲表单转为 HashMap类型数据。方便直接获取参数值。参数名称就是客户端传过来的数据对象的字段属性名。
R为响应的数据体:
@Data
public class R<T> implements Serializable {
private T data; //返回的数据
private int code = 0; //状态码,0:成功,1:失败
private String msg = ""; //描述信息
public static R isOk() {
return new R().code(0).msg("成功");
}
public static R isFail() {
return new R().code(1).msg("失败");
}
public static R isFail(Throwable e) {
return isFail().msg(e);
}
public R msg(Throwable e) {
this.setMsg(e.toString());
return this;
}
public R data(T data) {
this.setData(data);
return this;
}
public R msg(String msg){
this.setMsg(msg);
return this;
}
public R code(int status) {
this.setCode(status);
return this;
}
}
AppCourseGroup为返回的数据实体对象,代码如下:
@Data
public class AppCourseGroup implements Serializable {
private Integer id;//id
private String name;
private int index;
private List<CourseLesson> course;
}
AppCourseGroup 使用了@Data注解,是lombok插件的主机,可以省去get、set的编写。
对应的AppCourseMapper代码如下:
@Mapper
public interface AppCourseMapper {
//课程
@Select("SELECT art.id,art.name,art.index FROM course_group as art LEFT JOIN cclass as c on art.cclass=c.id where is_indexpage=1 and c.`name`=#{cclass}")
@Results(
value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "name", column = "name"),
@Result(property = "index", column = "index"),
@Result(property = "course", javaType=List.class, many =@Many(select="courseBycourseGroup"), column = "id") })
List<AppCourseGroup> courseGroup(@Param("cclass") String cclass);
@Select("SELECT * from video_course c where group_id=#{group_id}")
List<Course> courseBycourseGroup(@Param("group_id") String group_id);
}
AppCourseMapper 中的courseGroup方法使用了@Results注解,这个主机仪式可以指定列的与AppCourseGroup属性的对应关系,而是可以下钻查询。就是返回的字段可以是个集合。
@Result(property = "course", javaType=List.class, many =@Many(select="courseBycourseGroup"), column = "id") })
这段代码是指,对应AppCourseGroup的course属性,这个属性是个集合。需要根据返回的Id再去查询数据。many=@Many参数就是指定一个查询方法:courseBycourseGroup,传递一个列作为参数:column="id"。这样就会下钻查询子集合数据了。