一、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"。这样就会下钻查询子集合数据了。