04Kotlin登录功能MVP基类封装

Posted 清风百草

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了04Kotlin登录功能MVP基类封装相关的知识,希望对你有一定的参考价值。

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

Kotlin登录功能MVP基类封装

Kotlin体系文档

【01】Kotlin语言基础学习

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

【03】Kotlin登录MVP模式搭建

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

文章目录

1.架构图示

红框部分为基类的基类

2.BaseActivity基类

2.1知识点

2.1.1泛型的使用

2.1.2继承与接口实现

package com.gdc.kotlinproject.base

import android.os.Bundle
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.base
 * @file
 * @Description:
 * 1.是所有Activity的父类
 *
 * 1.1继承AppCompatActivity类
 *
 * 1.2为了确保该类被其他类继承,成员变量或函数被继承,因此将其声明为abstract类
 *
 * 1.3具体使用哪个Presenter类不需要关心,所以使用泛型来标示
 *
 * 1.4限定泛型必须是LoginPresenter子类才可以
 *
 * (1)P : LoginPresenter类似于java中P extends LoginPresenter
 * (2)P extends LoginPresenter & Serializable 类似于where P : IBasePresenter,P:Serializable,需要单独做限定
 *
 * 1.5不实例化presenter,面向于抽象,不面向细节编程,因此定义抽象方法,createP(),让其在子类中实现,返回类型为泛型
 *
 * 1.6强制子类在onDestroy执行时调用recycle()函数
 *
 * 1.7工具类函数(公用的函数)
 *
 */
abstract class BaseActivity<P> : AppCompatActivity() where P : IBasePresenter

    lateinit var presenter: P

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        presenter = createP()

        /**
         * 1.还可以自己扩展很多公用功能
         */
        //setContentView(getLayoutID())
        //initView()
        //initData()
    

    abstract fun createP() : P

    abstract fun recycle()

    override fun onDestroy() 
        super.onDestroy()
        recycle()
    

    /**
     * 1.类似于工具类函数
     * 1.1暴露给子类直接调用,完成隐藏ActionBar的功能
     * 1.2任何Java代码的东西,必须用?允许其为空
     */
    fun hideActionBar()
        val actionBar : ActionBar? = supportActionBar
        actionBar?.hide()
    

3.IBasePresenter接口

/**
 * @author XiongJie
 * @version appVer
 * @Package com.gdc.kotlinproject.base
 * @file
 * @Description:
 * 1.所有P层的最上层父类
 * 2.即在接口之上再实现接口
 * 3.也就是所有接口必须要实现的方法写在此接口中
 * @date 2021-5-13 10:41
 * @since appVer
 */
interface IBasePresenter 
    //fun attachView()

    //1.试图离开了(Activity/Fragmet离开了)消毁释放资源
    fun unAttachView()

3.View层

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import com.gdc.kotlinproject.R
import com.gdc.kotlinproject.base.BaseActivity
import com.gdc.kotlinproject.config.Flag
import com.gdc.kotlinproject.entity.LoginRegisterResponse
import com.gdc.kotlinproject.modules.login.interfaces.LoginPresenter
import com.gdc.kotlinproject.modules.login.interfaces.LoginView
import com.gdc.kotlinproject.modules.register.RegisterActivity
import kotlinx.android.synthetic.main.activity_login.*
/**
 * 1.登录界面
 * (1)View层的实现,即LoginView接口的实现,只需要关注P层,实现Presenter层
 * (2)Presenter层的创建在BaseActivity中定义,在具体Activity子类中实现
 * 2.MVP优势
 * (1)MVP模式中,Activity与Fragment只做View显示角色,不做Controller与Model相关工作
 * (2)单独可以拆卸便于测试
 * 3.设计模式的选择
 * (1)只有合适与不合适,没有好与不好。
 * (2)MVC可以减少创建多个类,但是一个类业务复杂情况下会有上万行代码情况出现,不利于项目维护。
 * (3)MVP可以需要创建多个类完成业务,但是职责清晰,便于测试,便于团队开发大型项目。
 */
class LoginActivity : BaseActivity<LoginPresenter>() ,LoginView

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        user_login_bt.setOnClickListener(onClickListener)
        user_register_tv.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")

                //1.1调用Presenter层实现业务
                presenter.loginAction(this@LoginActivity,userName,userPwd)

            
            R.id.user_register_tv ->
                val intent = Intent(this@LoginActivity,RegisterActivity::class.java);
                startActivity(intent)
            
        
    

    /**
     * 响应
     */
    override fun loginSucess(loginRegisterBean: LoginRegisterResponse?) 
        Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
    

    override fun loginFailure(errorMsg: String?) 
        Toast.makeText(this@LoginActivity, "登录失败", Toast.LENGTH_SHORT).show()
    

    /**
     * 子类中实现创建Presenter对象
     */
    override fun createP(): LoginPresenter = LoginPresenterImpl(this)

    override fun recycle() 
        presenter.unAttachView()
    

4.Presenter层

interface LoginPresenter :IBasePresenter

    /**
     * 1.登录
     * (1)context是为了给网络模型用的
     */
    fun loginAction(context : Context,username:String,password : String)

    /**
     * 1.监听Module层的接口回调
     */
    interface OnLoginListener
        fun loginSucess(loginRegisterBean: LoginRegisterResponse?)

        fun loginFailure(errorMsg : String ?)
    

5.Module层

interface LoginModule 

    /**
     * 1.取消请求动作
     */
    fun cancelRequest()

    /**
     * 1.登录
     * (1)接口回调:把data结果,给Presenter层
     */
    fun login(
        context : Context,
        username : String,
        password:String,
        onLoginListener:LoginPresenter.OnLoginListener
    )

class LoginModelImpl :LoginModule

    override fun cancelRequest() 

    

    override fun login(
        context: Context,
        username: String,
        password: String,
        onLoginListener: LoginPresenter.OnLoginListener) 
        APIClient.instance.instanceRetrofit(WanAndroidAPI::class.java)

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

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

            //1.1.4将下面更新UI的操作,分配到main线程中执行
            .observeOn(AndroidSchedulers.mainThread())

            .subscribe(object : APIResponse<LoginRegisterResponse>(context) 
                override fun success(data: LoginRegisterResponse?) 
                    Log.i(Flag.TAG, "success:$data")
                    //1.成功
                    onLoginListener.loginSucess(data)
                

                override fun failure(errorMsg: String?) 
                    Log.i(Flag.TAG, "success:$errorMsg")
                    //2.失败
                    onLoginListener.loginFailure(errorMsg)
                
            )
    

6.网络层

6.1网络请求客户端

(1)Retrofit负责请求方与响应方的联接

(2)请求方由OKHttpClient负责完成

(3)RxJava负责响应数据处理

(3)Gson负责将响应数据转换为实体数据

/**
 * @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请求方 <-
            .baseUrl(Flag.BASE_URL)
            .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)
    


6.2客户端请求接口API

/**
 * @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<LoginRegisterResponseWrapper<LoginRegisterResponse>>

    /**
     * 注册API
     */
    @POST("/user/register")
    @FormUrlEncoded
    fun registerAction(@Field("username") username: String,
                    @Field("password") password: String,
                    @Field("password") repassword: String)
            : Observable<LoginRegisterResponseWrapper<LoginRegisterResponse>>

6.3自定义操作符处理业务响应数据

/**
 * @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<LoginRegisterResponseWrapper<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: LoginRegisterResponseWrapper<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()
    

7.打赏鼓励

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

7.1微信打赏

7.2支付宝打赏

以上是关于04Kotlin登录功能MVP基类封装的主要内容,如果未能解决你的问题,请参考以下文章

Android MVP-编程思想3(MVP-内存泄露问题处理,基类封装,有没有必要再使用软引用?)

基于Kotlin的MVP基础封装

Android当中的MVP模式View 层 Activity 的基类--- BaseMvpActivity 的封装

Android当中的MVP模式View 层 Activity 的基类--- BaseMvpActivity 的封装

03Kotlin登录MVP模式搭建

Android MVP-编程思想7(为什么使用代理类抽取通用方法而不是工具类?,基类BaseMvpFragment)