02Kotlin项目实操之Retrofit网络模型

Posted 清风百草

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02Kotlin项目实操之Retrofit网络模型相关的知识,希望对你有一定的参考价值。

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

Kotlin项目实战之Retrofit网络模型

Kotlin体系文档

【01】Kotlin语言基础学习

【02】Kotlin项目实操之Retrofit网络模型

【03】Kotlin登录MVP模式搭建

【04】Kotlin登录功能MVP基类封装

1.项目实战的目的是什么?

了解用Kotlin来开发项目的过程

2.Retrofit网络模型

2.1请求服务器的模型封装,登录使用网络模型

2.1.1客户端登录API接口定义

import com.gdc.kotlinproject.entity.LoginResponse
import com.gdc.kotlinproject.entity.LoginResponseWrapper
import io.reactivex.Observable
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.api
 * @file
 * @Description:
 * 
 * 1.客户端API,可以访问服务器端API
 * 
 * 1.1请求接口路径
 * 1.2请求参数
 * 1.3请求数据响应实体
 * 
 * 1.3.1LoginResponseWrapper包装bean
 * 1.3.2LoginResponse:数据bean
 *
 * @date 2021-5-2 16:02
 * @since appVer
 */
interface WanandroidAPI {

    /**
     * 登录API
     * username=Derry-vip&password=123456
     */
    @POST("/user/login")
    @FormUrlEncoded
    fun loginAction(@Field("username") username: String,
                    @Field("password") password: String)
            : Observable<LoginResponseWrapper<LoginResponse>>

    /**
     * 注册API
     */
}

2.1.2登录响应实体封装

(1)分为数据实体与错误码相关信息

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.entity
 * @file
 * @Description:1.登录数据响应实体
 * (1)登录数据实体
 * (2)因为不知道数据实体中data是什么类型,有可能为空或者有数据,所以将其定义为泛型,可以随用户的传递而传递
 * (3)errorCode,errorMsg是具体的
 * @date 2021-5-2 16:37
 * @since appVer
 */
data class LoginResponseWrapper<T>(val data:T,val errorCode:Int,val errorMsg:String) {

}

2.1.2 响应实体中数据实体的封装

/**
 * 1.data 登录成功 需要把这个Bean 给 UI

"data": {
    "admin": false,
    "chapterTops": [],
    "collectIds": [],
    "email": [],
    "icon": "",
    "id": 66720,
    "nickname": "Derry-vip",
    "password": "",
    "publicName": "Derry-vip",
    "token": "",
    "type": 0,
    "username": "Derry-vip"
}

 2.泛型通配符Kotlin中使用的是*,java中使用的是?
 3.为什么email的String类型后还加一个?
 3.1是因为,如果email的值为null的话,不添加?很可能导致序列化进来的数据失败。
 3.1?的作用就是允许该值为null
 */

data class LoginResponse(
    val admin:Boolean,
    val chapterTops : List<*>,
    val collectIds : List<*>,
    val email : String ?,
    val icon : String ?,
    val id :String?,
    val nickname: String?,
    val password: String?,
    val publicName: String?,
    val token: String?,
    val type: Int,
    val username: String?) {
}

3.对应用请求响应API接口的统一管理

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.net
 * @file
 * @Description:
 * 1.对应用请求响应API接口的统一管理
 * 即对每一个请求与响应进行统一管理
 *
 * 1.1单例模式创建该类实例
 * 1.2由Retrofit负责请求方与响应方的联接
 * 1.2.1请求方由OKHttpClient负责完成
 * 1.2.2响应方由RxJava对响应数据进行处理
 * 1.2.2.1响应数据转换为实体数据交由Gson处理
 *
 * @date 2021-5-4 11:28
 * @since appVer
 */ 

class APIClient{

    /**
     * 单例
     */

    private object Holder{
        val INSTANCE  = APIClient()
    }

    /**
     * 派生
     */
    companion object{
        val instance = Holder.INSTANCE;
    }

    /**
     * 1.创建客户端API接口
     * (1)WanAndroidAPI实例化
     * (2)XXXAPI实例化
     * (3)动态的实例化,可以使用到泛型
     * (4)fun <T> instanceRetrofit(apiInterface: Class<T>): T表示此为泛型方法
     * (5)apiInterface: Class<T>:表示此为泛型参数
     * (6): T表示返回类型为泛型
     */
    fun <T> instanceRetrofit(apiInterface: Class<T>) : T{

        //1.1.1OKHttpClient请求服务器
        val okHttpclient = OkHttpClient().newBuilder()
        //1.1.1.1添加读取超时时间
            .readTimeout(10000, TimeUnit.SECONDS)
        //1.1.1.2添加连接超时时间
            .connectTimeout(10000,TimeUnit.SECONDS)
        //1.1.1.3添加写出超时时间
            .writeTimeout(10000,TimeUnit.SECONDS)
            .build()

        val retrofit:Retrofit = Retrofit.Builder()
            //1.1请求方 <-
            .client(okHttpclient)

            //1.2响应方 ->
            //1.2.1RxJava来处理
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            //1.2.2Gson来解析JavaBean
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        //1.3将请求方与响应方建好后,将请求api的参数
        return retrofit.create(apiInterface)
    }

}

4.自定义操作符拦截包装类

import android.content.Context
import com.gdc.kotlinproject.entity.LoginResponseWrapper
import com.gdc.kotlinproject.views.LoadingDialog
import io.reactivex.Observer
import io.reactivex.disposables.Disposable

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.net
 * @file
 * @Description:
 * 1.RxJava自定义操作符
 * 1.1目的是将包装bean拆成两份
 * (1)如果成功,将data给UI
 * (2)如果失败,将错误信息msg给UI
 * 1.2操作符
 * (1)在起点到终点的流向过程当中,该操作符成为了观察者
 * (2)流向的类型是包装bean,因此泛型类型是包装bean
 * (3)不想将包装bean泛型的具体参数类型限定死,即LoginResponse,因此包装beanLoginResponseWrapper<T>
 *     定义为泛型,并且将该类定义为泛型类,是为了让用户可以动态传递.
 * (4)为什么要定义为抽象类?
 *     是为了抽像成功操作与失败操作
 * 1.3用户自定义是否弹出加载框,需要上下文val context : Context
 * @date 2021-5-5 09:46
 * @since appVer
 */

abstract class APIResponse<T>(val context : Context) :Observer<LoginResponseWrapper<T>>{

    /**
     * 1.控制加载框是否弹出
     * (1)默认弹出
     */
    private var isShow : Boolean = true

    /**
     * 1.次构造
     * (1)让对话框的显示与隐藏可控
     */
    constructor(context : Context,isShow :Boolean = false) : this(context){
        this.isShow = isShow;
    }

    /**
     * 1.统一处理数据返回成功的操作
     * 1.1? 允许返回数据为空
     * 1.2 成功将data交给UI展示
     */
    abstract fun success(data : T ?)

    /**
     * 1.统一处理数据响应失败的操作
     * 1.1失败将错误消息丢给UI
     * 1.2?表示错误消息允许为null
     */
    abstract fun failure(errorMsg : String ?)

    /**
     * 1.弹出加载框
     * (1)在起点分发的时候触发
     */
    override fun onSubscribe(d: Disposable) {
        if(isShow){
           LoadingDialog.show(context)
        }
    }

    /**
     * 1.接收从上游流下来的数据
     * (1)成功与失败的判断,根据数据data是否为null进行判断
     * (2)成功调用success将data传给UI显示
     */
    override fun onNext(t: LoginResponseWrapper<T>) {
        //失败
        if(null == t.data){
            failure("登录失败,请检查原因:${t.errorMsg}")
        }else{
            success(t.data)
        }
    }

    /**
     * 1.处理上游流下来的错误
     * (1)取消加载框的显示
     */
    override fun onError(e: Throwable) {
        LoadingDialog.cancel()
        failure(e.message)
    }

    /**
     * 1.停止
     * (1)取消加载框
     */
    override fun onComplete() {
        LoadingDialog.cancel()
    }
}

5.界面调用

/**
 * 1.登录界面
 */
class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        user_login_bt.setOnClickListener(onClickListener)
    }

    /**
     * 1.kotlin与java的无缝交互
     * 1.1view.id不需要写view.getId()或view.setId()是因为Kotlin中自动判断调用哪个方法
     * 1.1.1id的值由内部根据"="知道你是调用get还是set方法
     * 1.1.2WanAndroidAPI::class.java 对应 WanAndroidAPI.class
     * 1.1.3val api = APIClient.instance.instanceRetrofit(WanAndroidAPI::class.java)
     */
    private val onClickListener = View.OnClickListener { view ->
        when (view.id) {
            //1.登录
            R.id.user_login_bt -> {
                val userName = user_phone_et.text.toString()
                val userPwd = user_password_et.text.toString()
                Log.d(Flag.TAG, "userName:$userName,userPwd:$userPwd")

                APIClient.instance.instanceRetrofit(WanAndroidAPI::class.java)

                    //1.1全部都是RxJava知识了
                    //1.1.1创建被观察者,经过此步骤会返回一个Observable对象,它是RxJava的起点
                    //1.1.2将LoginResponseWrapper丢给RxJava慢慢的往传递,直到传给UI界面控件
                    .loginAction(userName, userPwd)

                    //1.1.3因为请求与响应是耗时操作,所以将其放到异步线程中执行。
                    .subscribeOn(Schedulers.io())

                    //1.1.4将下面更新UI的操作,分配到main线程中执行
                    .observeOn(AndroidSchedulers.mainThread())
                    /**
                     * (1)通过RxJava起点把包装bean中的数据,放到流中流向终点,去更新UI
                     * (2)以下代码拿到的是包装bean,需要对其拆卸
                     * (3)自定义操作符把包装bean进行拦截,把包装bean进行拆卸,如果成功,将
                     * 包装bean中的data丢给页面,如果失败将错误码丢给UI
                     */
                    /*.subscribe(object : Consumer<LoginResponseWrapper<LoginResponse>> {
                        override fun accept(t: LoginResponseWrapper<LoginResponse>) {

                        }
                    })*/

                    .subscribe(object : APIResponse<LoginResponse>(this) {
                        override fun success(data: LoginResponse?) {
                            Log.i(Flag.TAG, "success:$data")
                            Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
                        }

                        override fun failure(errorMsg: String?) {
                            Log.i(Flag.TAG, "success:$errorMsg")
                            Toast.makeText(this@LoginActivity, "登录失败", Toast.LENGTH_SHORT).show()
                        }
                    })
            }
        }
    }
}

6.java与Kotlin互调

(1)Kotlin会比Java的编译速度更慢,因为它生成的内容较多。

6.1Java调用Kotlin静态类的静态方法

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.kt
 * @file
 * @Description:
 * 1.工具类
 * (1)演示java与Kotlin互调
 * (2)此类会被生成为一个public final class UtilsKt类
 * (3)在此类中生成的方法都为static final的方法
 *     public static final void show()
 *
 *
 * @date 2021-5-8 09:06
 * @since appVer
 */ 

fun show(info : String){
    println(info)
}

6.2Java调用Kotlin普通类的方法

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.kt
 * @file
 * @Description:
 * 1.演示Java与Kotlin互调
 * (1)有具体的类名,生成的类public final class MyUtils
 * @date 2021-5-8 09:18
 * @since appVer
 */
class MyUtils {
    fun show(info : String){
        println(info)
    }
}
/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.java
 * @file
 * @Description:
 * 1.Java与Kotlin互调
 * (1)Java调用Kotlin静态类的静态方法
 * (2)Java调用Kotlin普通类的方法
 * @date 2021-5-8 09:08
 * @since appVer
 */

public class Client {
    public static void main(String[] args) {
        UtilsKt.show("你好!");
        new MyUtils().show("中华人民共和国");
    }
}

6.3Kotlin调用java

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.kt
 * @file
 * @Description:
 * 1.演示Kotlin调用java
 * 1.1调用java常量
 * (1)in是kotlin的区间,此处与Java的常量冲突,因此使用''解决冲突问题
 * 1.2调用java函数
 * (1)有一个String!代表不确定性,即不知道什么时候有值,什么时候没值
 * (2)最好使用变量接收
 * (3)Student().string.length:写法错误,有可能引发空指针异常
 * 1.3class传递
 *
 * @date 2021-5-8 09:29
 * @since appVer
 */
fun main() {
    //(1)调用java常量
    println(Student.`in`)
    /**
     * (2)调用java函数
     */
    //Student().string.length
    var str : String ? = Student().string
    println(str?.length)

    //(3)传递java类给kotlin方法
    showClass1(Student::class.java)

    //(4)传递kotlin类给kotlin方法
    showClass2(KtStudent::class)
}

/**
 * 1.形参里面全部是java类
 */
fun showClass1(clazz:Class<Student>){

}

/**
 * 1.形参里面全部是Kotlin类
 */
fun showClass2(clazz: KClass<KtStudent>){

}

(1)java类

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.java
 * @file
 * @Description:
 * 1.演示Kotlin调用Java类
 * @date 2021-5-8 09:23
 * @since appVer
 */

public class Student {
    public static String in = "中国人民解放军";

    public String getString(){
        return null;
    }
}

(2)Kotlin类

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.kt
 * @file
 * @Description:
 * @date 2021-5-8 09:46
 * @since appVer
 */ 
class KtStudent{

}

6.4Kotlin使用Java接口回调

(1)java接口

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.java
 * @file
 * @Description:
 * 1.java接口
 * @date 2021-5-8 10:00
 * @since appVer
 */

public interface JavaCallback {
    void show(String info);
}

(2)java接口管理者

public class JavaManager {
    public void setCallBack(JavaCallback javaCallback){
        javaCallback.show("中华人民共和国");
    }
}

(3)Kotlin使用Java接口回调

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.lib.kt
 * @file
 * @Description:
 * 1.Kotlin使用Java接口回调
 * @date 2021-5-8 10:02
 * @since appVer
 */

fun main(){
    //1.第一种写法
    JavaManager().setCallBack({
        println(it)
    })

    //2.第二种写法
    JavaManager().setCallBack(object : JavaCallback{
        override fun show(info: String?) {
            println(info)
        }
    })

    //3.第三种写法
    JavaManager().setCallBack {
            info -> println(info)
    }

    //4.第4种写法
    val callback = JavaCallback {
        println(it)
    }
    JavaManager().setCallBack(callback)

    //5.第5种写法
    val callback2 = object : JavaCallback{
        override fun show(info: String?) {
            println(info)
        }
    }
    JavaManager().setCallBack(callback2)

}

6.5Kotlin中使用kotlin接口回调

(1)kotlin接口

interface KTCallback {
    fun show(name : String)
}

(2)回调管理

class KtManager{
    fun setCallback(callback : KTCallback){
        callback.show("中华人民共和国")
    }
}
fun main(){

    //1.第一种写法
    KtManager().setCallback(object :KTCallback{
        override fun show(name: String) {

        }

    })

    //2.第二种写法
    val c = object : KTCallback{
        override fun show(name: String) {

        }

    }
    KtManager().setCallback(c)

    /**
     * 1.Kotlin不能向Java一样如此使用回调
     */
    /*KtManager().setCallback(KTCallback {

    })*/
}
fun main(){

    /**
     * 1.kotlin使用java的回调
     */
    Handler(Looper.getMainLooper(),object : Handler.Callback{
        override fun handleMessage(msg: Message): Boolean {
            return true
        }

    })

    /**
     * 2.Kotlin的lamboda表达式
     */
    Handler(Looper.getMainLooper(),Handler.Callback {
        true
    })

    /**
     * 3.使用线程
     */
    Thread{
        Log.e("","main:")
    }.start()
}

7.打赏鼓励

感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!

7.1微信打赏

在这里插入图片描述

7.2支付宝打赏

在这里插入图片描述

以上是关于02Kotlin项目实操之Retrofit网络模型的主要内容,如果未能解决你的问题,请参考以下文章

07Kotlin项目实操之Navigation使用

10Kotlin项目实操之首页模块

10Kotlin项目实操之首页模块

10Kotlin项目实操之首页模块

08Kotlin项目实操之收藏业务

07Kotlin项目实操之Navigation使用