Skip to content

V2 普通版本使用说明

chenli edited this page Apr 10, 2024 · 2 revisions

1.如何集成

  • 1.1 在root's build.gradle中加入Jitpack仓库
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • 1.2 在app's build.gradle中添加依赖
dependencies {
  ...
    //框架地址
    implementation 'com.github.cl-6666:mvvm-framework:v2.1.2'
}
  • 1.3 在app's build.gradle中,android 模块下按需开启DataBinding与ViewBinding
AndroidStudio 4.0 以下版本------>
android {
    ...
    dataBinding {
        enabled = true 
    }
    viewBinding {
        enabled = true
    }
}

AndroidStudio 4.0及以上版本 ------>
android {
    ...
   buildFeatures {
        dataBinding = true
        viewBinding = true
    }
}
 
  • 1.4Application初始化
class AppApplication : BaseApplication() { ... }
如果由于某种原因,导致你不能继承[BaseApplication];你也可以在你自定义的Application的onCreate函
数中通过调用[BaseApplication.initAppConfig]来进行初始化

2.继承基类

一般我们项目中都会有一套自己定义的符合业务需求的基类 BaseActivity/BaseFragment,所以我们的基类需要继承本框架的Base类

  • 不想用Databinding与ViewBinding-------可以继承 BaseVmActivity/BaseVmFragment
  • 用Databinding-----------可以继承BaseVmDbActivity/BaseVmDbFragment**
  • 用Viewbinding-----------可以继承BaseVmVbActivity/BaseVmVbFragment**

Activity:

abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbActivity<VM, DB>() {
     /**
     * 当前Activity绑定的视图布局Id abstract修饰供子类实现
     */
    abstract override fun layoutId(): Int
    /**
     * 当前Activityc创建后调用的方法 abstract修饰供子类实现
     */
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 创建liveData数据观察
     */
    override override fun createObserver()

    /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

Fragment:

abstract class BaseFragment<VM : BaseViewModel,DB:ViewDataBinding> : BaseVmDbFragment<VM,DB>() {
   
    abstract override fun initView(savedInstanceState: Bundle?)

    /**
     * 懒加载 只有当前fragment视图显示时才会触发该方法 abstract修饰供子类实现
     */
    abstract override fun lazyLoadData()

    /**
     * 创建liveData数据观察 懒加载之后才会触发
     */
    override override fun createObserver()
  
    /**
     * Fragment执行onViewCreated后触发的方法 
     */
    override fun initData() {

    }
    
   /**
     * 打开等待框 在这里实现你的等待框展示
     */
    override fun showLoading(message: String) {
       ...
    }

    /**
     * 关闭等待框 在这里实现你的等待框关闭
     */
    override fun dismissLoading() {
       ...
    }
}

3.编写一个网络请求功能

  • 3.1 创建MainViewModel类继承BaseViewModel
class MainViewModel : BaseViewModel() {}
  • 3.2 创建HomeFragment 继承基类传入相关泛型,第一个泛型为你创建的MainViewModel,第二个泛型为ViewDataBind,保存fragment_home.xml后databinding会生成一个FragmentHomeBinding类。(如果没有生成,试着点击Build->Clean Project)
class HomeFragment : BaseFragment<MainViewModel, FragmentHomeBinding>() {
    
    /**
     *  初始化操作
     */
    override fun initView(savedInstanceState: Bundle?) {
        ...
    }
    
    /**
     *  fragment 懒加载
     */
    override fun lazyLoadData() { 
        ...
    }
}

4.网络请求(Retrofit+协程)

  • 4.1 新建请求配置类继承 BaseNetworkApi 示例:
val apiService: ApiService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
    //type 为false 表示不使用htpps忽略证书验证
    NetworkApi.INSTANCE.getApi(ApiService::class.java, BASE_URL, false)
}
class NetworkApi : BaseNetworkApi() {
    companion object {
        val INSTANCE: NetworkApi by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            NetworkApi()
        }
    }
    /**
     * 实现重写父类的setHttpClientBuilder方法,
     * 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作
     */
    override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
        //普通网络日志显示写法
        val httpLoggingInterceptor = HttpLoggingInterceptor { message ->
            Log.e(
                "网络日志", message
            )
        }
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        //框架内部封装日志显示写法 hideVerticalLine = true代表隐藏横线
        val loggingInterceptor = AndroidLoggingInterceptor.build(hideVerticalLine = true)
        builder.apply {
            /** 设置缓存配置 缓存最大10M */
            cache(Cache(File(app.cacheDir, "cxk_cache"), 10 * 1024 * 1024))
            /** 添加Cookies自动持久化 */
            cookieJar(cookieJar)
            /** 演示添加缓存拦截器 可传入缓存天数,不传默认7天 */
            addInterceptor(CacheInterceptor())
            /** 演示添加公共heads 注意要设置在日志拦截器之前,不然Log中会不显示head信息 */
            addInterceptor(MyHeadInterceptor())
            /** 演示token过期拦截器演示 */
            addInterceptor(TokenOutInterceptor())
            /** 演示日志拦截器 */
            addInterceptor(loggingInterceptor)
            /** 超时时间 连接、读、写 */
            connectTimeout(10, TimeUnit.SECONDS)
            readTimeout(5, TimeUnit.SECONDS)
            writeTimeout(5, TimeUnit.SECONDS)
        }
        return builder
    }
    /**
     * 实现重写父类的setRetrofitBuilder方法,
     * 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,protobuf等
     */
    override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
        return builder.apply {
            addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
        }
    }
    val cookieJar: PersistentCookieJar by lazy {
        PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(app))
    }
}
  • 4.2如果你请求服务器返回的数据有基类(没有可忽略这一步)例如:
{
    "data": ...,
    "errorCode": 0,
    "errorMsg": ""
}

该示例格式是 玩Android Api返回的数据格式,如果errorCode等于0 请求成功,否则请求失败 作为开发者的角度来说,我们主要是想得到脱壳数据-data,且不想每次都判断errorCode==0请求是否成功或失败 这时我们可以在服务器返回数据基类中继承BaseResponse,实现相关方法:

data class ApiResponse<T>(var errorCode: Int, var errorMsg: String, var data: T) : BaseResponse<T>() {

    // 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来编写
    override fun isSucces() = errorCode == 0

    override fun getResponseCode() = errorCode

    override fun getResponseData() = data

    override fun getResponseMsg() = errorMsg

}
  • 4.3 在ViewModel中发起请求,所有请求都是在viewModelScope中启动,请求会发生在IO线程,最终回调在主线程上,当页面销毁的时候,请求会统一取消,不用担心内存泄露的风险,框架做了2种请求使用方式

1、将请求数据包装给ResultState,在Activity/Fragment中去监听ResultState拿到数据做处理

class MainViewModel : BaseViewModel() {

    /**
     *自动脱壳过滤处理请求结果,自动判断结果是否成功
     */
    var articleListResult = MutableLiveData<ResultState<Data>>()

    /**
     * 不用框架帮脱壳
     */
    var articleListResult2 = MutableLiveData<ResultState<ApiResponse<Data>>>()

    /**
     * 网络请求
     */
    fun apiArticleListData() {
        //1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
        request(
            { apiService.getEntryAndExitData() }, //请求体
            articleListResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
            true,//是否显示等待框,,默认false不显示 可以默认不传
            "数据请求中..."//等待框内容,可以默认不填请求网络中...
        )
        //2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
        requestNoCheck(
            { apiService.getEntryAndExitData() },
            articleListResult2,
            true,
            "数据请求中..."
        )
    }
}

class HomeFragment : BaseFragment<MainViewModel,FragmentHomeBinding>(){

    private val mArticleListAdapter: ArticleListAdapter by lazy { ArticleListAdapter(arrayListOf()) }

    override fun initView(savedInstanceState: Bundle?) {
        mViewModel.apiArticleListData()
        mDatabind.rvArticleList.init(LinearLayoutManager(activity), mArticleListAdapter, false)
    }

    override fun createObserver() {
        super.createObserver()
        //脱壳
        mViewModel.articleListResult.observe(viewLifecycleOwner,
            Observer { resultState ->
                parseState(resultState, {
                    //请求成功 打印消息
                    mArticleListAdapter.submitList(it.datas)
                }, {
                    //请求失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
                    toast("请求失败")
                })
            })


        //不脱壳
        mViewModel.articleListResult2.observe(viewLifecycleOwner) { resultState ->
            parseState(resultState, {
                if (it.errorCode == 0) {
                    //请求成功 打印消息
                    toast(it.data.toString())
                } else {
                    //请求失败
                    toast(it.errorMsg)
                }
            }, {
                //请求发生了异常
                toast(it.errorMsg)
            })
        }
    }
}

2、 直接在当前ViewModel中拿到请求结果

class MyFragmentViewModel : BaseViewModel() {
    
    fun testData() {
        //1.拿到已脱壳的数据(项目有基类的可以用)
        request({ apiService.getEntryAndExitData() }, {
            //请求成功 已自动处理了 请求结果是否正常
        }, {
            //请求失败 网络异常,或者请求结果码错误都会回调在这里
        }, true, "正在登录中...")

        //2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类或者不想框架帮忙脱壳的可以用)
        requestNoCheck({ apiService.getEntryAndExitData() }, {
            //请求成功 自己拿到数据做业务需求操作
            if (it.errorCode == 0) {
                //结果正确
            } else {
                //结果错误
            }
        }, {
            //请求失败 网络异常回调在这里
        }, true, "正在登录中...")
    }
}
}
 

3、请求结果截图 演示

5.写了一些常用的拓展函数

 算了不写了,这个不重要,想具体看的话可以在
 me.hgj.jetpackmvvm.ext.util
 me.hgj.jetpackmvvm.ext.view
 的包中看,反正你也可以自己写,按照自己的喜好与需求来

6.项目使用的三方库及其简单示例和资料

  • Kotlin
  • Jetpack
    • Lifecycle: 观察 Android 生命周期并根据生命周期变化处理 UI 状态
    • ViewModel: 管理与 UI 相关的数据持有者和生命周期感知。 允许数据在配置更改(例如屏幕旋转)中保存下来。
    • ViewBinding: 使用声明性格式而不是以编程方式将布局中的 UI 组件绑定到应用程序中的数据源。
    • Room: 通过在 SQLite 上提供抽象层来构建数据库,以允许流畅的数据库访问。
  • LiveData
  • ksp: Kotlin 符号处理 API。
  • coil, Coil地址: Coil 是一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片。
  • Timber: 日志框架
  • OkHttp:网络请求
  • PersistentCookieJar:持久CookieJar实现
  • logging-interceptor:网络日志
  • Retrofit:网络请求