background

In the previous article, we introduced the basic use of Mvvm. This article adds the ViewModel layer to the original ViewBinding, making the code very concise. This article is only used as a record of knowledge, if there is any wrong place please advise!

MVVM infrastructure encapsulation

1. The base class BaseActivity

package com.example.studymvvmproject01.base

import android.content.pm.ActivityInfo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.example.studymvvmproject01.R
import com.gyf.immersionbar.ImmersionBar

abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
  protected open var mBinding: VB? = null

  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      mBinding = getViewBinding()
      setContentView(mBinding?.root)
      requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT//竖屏
      initialize()


  }

  open fun initialize() {

  }

  abstract fun getViewBinding(): VB?
  override fun onDestroy() {
      super.onDestroy()
      mBinding = null
  }

}
Copy the code

2. BaseFragment encapsulation

abstract class BaseFragment<VB:ViewBinding>:Fragment() { protected open var binding:VB? =null protected open val mBinding get()= binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle?) : View? { binding = getViewBinding() return mBinding? .root } abstract fun getViewBinding(): VB? override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initialize() } open fun initialize() { } override fun onDestroy() { super.onDestroy() binding=null } }Copy the code

3. BaseViewModel layer encapsulation

typealias Block<T> = suspend (CoroutineScope) -> T typealias Error = suspend (Exception) -> Unit typealias Cancel = suspend (Exception) -> Unit open class BaseViewModel : ViewModel() { var TAG="BaseViewModel" val needLogin = MutableLiveData<Boolean>().apply { value = false } protected fun launch( block: Block<Unit>, error: Error? = null, cancel: Cancel? = null, showErrorToast: Boolean = true, ): Job { return viewModelScope.launch { try { block.invoke(this) } catch (e: Exception) { when (e) { is CancellationException -> { cancel? .invoke(e) } else -> { onError(e, showErrorToast) error? .invoke(e)}}}}} /** * Unified processing error * @param e exception * @param showErrorToast Whether to display wrong toast */ @SuppressLint("WrongConstant") private fun onError(e: Exception, showErrorToast: Boolean) { when (e) { is ApiException -> { when (e.code) { -1001 -> { if (showErrorToast) { Toast. MakeText (AppHelper mContext, e.m essage, 1000). The show ()} needLogin. Value = true} / / other errors else - > {the if (showErrorToast) Toast. MakeText (AppHelper mContext, e.m essage, 1000). The show ()}} the e (TAG, e. oString ())} / / network request is failure ConnectException, is SocketTimeoutException, is UnknownHostException, Is HttpException -> {if (showErrorToast) toast.maketext (apphelper.mContext," network request failed ",1000).show() Log.e(TAG," network request failed "+ e.tostring ())} is JsonParseException -> {log.e (TAG," network request failed "+ e.tostring ())} else -> {log.e (TAG," other errors "+ e.tostring ())}}}}Copy the code

Trip: introductions of CoroutineScope zhuanlan.zhihu.com/p/297543508 can reference

4. BaseVmActivity encapsulation

abstract class BaseVmActivity<VB : ViewBinding, VM : BaseViewModel> : BaseActivity<VB>() { protected open lateinit var mViewModel: VM // number of protected open val mTotalCount = 20 protected open var mCurrentSize = 0 Override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initViewModel() observer() initView() initData() setListener() } open fun SetListener () {} Open fun initData() {} Open fun initView() {} private fun Observer () { MViewModel. NeedLogin. Observe (this, {/ / if you do not login, If (it) {sputil. setBoolean(myconfig.is_login, Private fun initViewModel() {mViewModel = ViewModelProvider(this).get(viewModelClass())} private fun initViewModel() {mViewModel = ViewModelProvider(this). abstract fun viewModelClass(): Class<VM> override fun onDestroy() { super.onDestroy() mCurrentSize = 0 mCurrentPage = 0 } }Copy the code

5. BaseVMFragment encapsulation

abstract class BaseVMFragment<VB : ViewBinding, VM : BaseViewModel> : BaseFragment<VB>() { protected lateinit var mViewModel: VM private var lazyLoaded = false VM private var lazyLoaded = false VM private var lazyLoaded = false Override fun onViewCreated(view: view, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViewModel() observe() initView() initData() setListener() } open fun setListener() { } open fun initData() { } open fun initView() { } open fun observe() { mViewModel.needLogin.observe(viewLifecycleOwner, { if (it) { SpUtil.setBoolean(MyConfig.IS_LOGIN, false) } }) } private fun initViewModel() { mViewModel = ViewModelProvider(this).get(viewModelClass()) } abstract fun viewModelClass(): Class<VM> override fun onResume() { super.onResume() if(! lazyLoaded){ lazyLoadData() lazyLoaded=true } } open fun lazyLoadData() { } }Copy the code

6. BaseRepository encapsulation

open class BaseRepository{

    protected fun apiService(): Api {
        return RetrofitClient.create(Api::class.java)
    }

}
Copy the code

7. Network architecture encapsulation

(1) RetrofitClient class

object RetrofitClient{ private const val CALL_TIMEOUT = 10L private const val CONNECT_TIMEOUT = 20L private const val IO_TIMEOUT = 20L private val mRetrofit:Retrofit init { val loggingInterceptor = HttpLoggingInterceptor { Log.d("httpLog", it) } loggingInterceptor.level=HttpLoggingInterceptor.Level.BODY /** * OkHttpClient */ val okHttpClient=OkHttpClient.Builder() .callTimeout(CALL_TIMEOUT, TimeUnit.SECONDS) .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(IO_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(IO_TIMEOUT, Timeunit.seconds) // Add header information. AddInterceptor (AddCookiesInterceptor()) // Intercepts interface header information AddInterceptor (ReceivedCookiesInterceptor ()) / / log intercept. AddInterceptor (loggingInterceptor). RetryOnConnectionFailure (true)  .build() mRetrofit= Retrofit.Builder().client(okHttpClient) .baseUrl(Api.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } fun <T> create(tClass: Class<T>?) : T { return mRetrofit.create(tClass) } }Copy the code

(2) Add header information interception

class AddCookiesInterceptor:Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val builder:Request.Builder=chain.request().newBuilder();
        val stringSet = SpUtil.getString(MyConfig.COOKIE)
            builder.addHeader("Authorization","Bearer "+stringSet)
        return chain.proceed(builder.build())

    }

}
Copy the code

A message is displayed indicating that Authorization is background JWT authentication service interface authentication

The above part is the MVVM basic framework package, the following part mainly explains how to use.

The instance

Interface Api

interface Api { companion object{ // const val BASE_URL="https://www.wanandroid.com/" const val BASE_URL="http://172.16.7.3:8066/"} @get ("tree/json") suspend fun getTree(): MutableList<Unit> /** * Obtain the login token * static Header * @param requestBody Body * @return */ @headers ("Authorization:Basic dmlkZW9hbmFseXNpczp2aWRlb2FuYWx5c2lz") @POST("/safeMobileServer/smc/auth/oauth/token") suspend fun toke(@Body requestBody: RequestBody?):LoginBean /** * get data by unit * @param headers * @param siteId siteId * @return */ @GET("/safeMobileServer/smc/psmgpersonloccur/personCntByType") suspend fun workIndex( @Query("siteId") siteId: Int, ):HomeProadBean }Copy the code

2. The LoginBean entity class

class LoginBean {

    /**
     * access_token : fd770c91-83ab-414e-b204-1112c26aed55
     * token_type : bearer
     * refresh_token : f5705a7a-914b-4371-ac56-f05f5c22406b
     * expires_in : 40755
     * scope : server
     * tenant_id : 3
     * license : made by kyny
     * user_id : 300
     * site_id : 1
     * active : true
     * dept_id : null
     * username : 66ADMIN
     */
    private var access_token: String? = null
    private var token_type: String? = null
    private var refresh_token: String? = null
    private var expires_in = 0
    private var scope: String? = null
    private var tenant_id = 0
    private var license: String? = null
    private var user_id = 0
    private var site_id = 0
    private var active = false
    private var dept_id: Any? = null
    private var username: String? = null

    fun getAccess_token(): String? {
        return access_token
    }

    fun setAccess_token(access_token: String?) {
        this.access_token = access_token
    }

    fun getToken_type(): String? {
        return token_type
    }

    fun setToken_type(token_type: String?) {
        this.token_type = token_type
    }

    fun getRefresh_token(): String? {
        return refresh_token
    }

    fun setRefresh_token(refresh_token: String?) {
        this.refresh_token = refresh_token
    }

    fun getExpires_in(): Int {
        return expires_in
    }

    fun setExpires_in(expires_in: Int) {
        this.expires_in = expires_in
    }

    fun getScope(): String? {
        return scope
    }

    fun setScope(scope: String?) {
        this.scope = scope
    }

    fun getTenant_id(): Int {
        return tenant_id
    }

    fun setTenant_id(tenant_id: Int) {
        this.tenant_id = tenant_id
    }

    fun getLicense(): String? {
        return license
    }

    fun setLicense(license: String?) {
        this.license = license
    }

    fun getUser_id(): Int {
        return user_id
    }

    fun setUser_id(user_id: Int) {
        this.user_id = user_id
    }

    fun getSite_id(): Int {
        return site_id
    }

    fun setSite_id(site_id: Int) {
        this.site_id = site_id
    }

    fun isActive(): Boolean {
        return active
    }

    fun setActive(active: Boolean) {
        this.active = active
    }

    fun getDept_id(): Any? {
        return dept_id
    }

    fun setDept_id(dept_id: Any?) {
        this.dept_id = dept_id
    }

    fun getUsername(): String? {
        return username
    }

    fun setUsername(username: String?) {
        this.username = username
    }
}
Copy the code

3. HomeProadBean entity class

Class HomeProadBean {/** * code: 0 * MSG: [ * {"personCnt":11,"typeCode":"OVERALL"} * ,{"personCnt":1,"typeCode":"OVERALL"} * ,{"personCnt":0,"typeCode":"STAFF"},  * {"personCnt":0,"typeCode":"OUTSRC"}, * {"personCnt":6,"typeCode":"OTHERS"}] */ private var code = 0 private var msg: String? = null private var data: List<DataBean? >? = null fun getCode(): Int { return code } fun setCode(code: Int) { this.code = code } fun getMsg(): String? { return msg } fun setMsg(msg: String?) { this.msg = msg } fun getData(): List<DataBean? >? { return data } fun setData(data: List<DataBean? >? { this.data = data } class DataBean { /** * personCnt : 11 * typeCode : OVERALL */ var personCnt = 0 var typeCode: String? = null var name: String? = null } }Copy the code

3.LoginRepository

class LoginRepository:BaseRepository() { suspend fun login()=apiService().getTree() suspend fun token(requestBody: RequestBody? ) :LoginBean{ return apiService().toke(requestBody) } suspend fun workIndex(siteId:Int):HomeProadBean{ return apiService().workIndex(siteId) } }Copy the code

4.LoginViewModel

class LoginViewModel : BaseViewModel() {
    val repository by lazy {
        LoginRepository()
    }

    val loginInfo=MutableLiveData<LoginBean>()
    val homeProadBean=MutableLiveData<HomeProadBean>()
    fun login(requestBody: RequestBody?) {
        launch(
            block = {
                 val token = repository.token(requestBody)
                loginInfo.value=token
            }
        )
    }
    fun workIndex(siteId:Int) {
        launch(
            block = {
                homeProadBean.value= repository.workIndex(siteId)
            }
        )
    }


}
Copy the code

5. LoginActivity implementation

class LoginActivity : BaseVmActivity<ActivityMain1Binding, LoginViewModel>() { override fun viewModelClass(): Class<LoginViewModel> { return LoginViewModel::class.java } override fun getViewBinding(): ActivityMain1Binding { return ActivityMain1Binding.inflate(layoutInflater) } override fun initData() { super.initData() Val hashMap = hashMap <String, String>() hashMap["scope"] = "server" hashMap["username"] = "66admin" hashMap["password"] = "Kyny@2021" hashMap["grant_type"] = "password" val requestBody = RequestUtil.getRequestBody(hashMap); Mviewmodel.workindex (1)} Override fun initView() {super.initView() mViewModel.loginInfo.observe(this, { mBinding? .tvContent? .text = it.getAccess_token() + "," Sputil.setstring (myconfig.cookie, it.getAccess_token())}) Parameters and needs to take the lead department certification mViewModel. HomeProadBean. Observe (this, {it. GetMsg ()? .let { it1 -> Log.e("TAG", it1) } for(data in it.getData()!!) { Log.e("data", data? .personCnt.toString()) } }) } }Copy the code

6.RequestUtil

This method converts the Map collection into a RequestBody entity class

object RequestUtil { fun getRequestBody(hashMap: HashMap<String, String>): RequestBody? { val data = StringBuffer() if (hashMap ! = null && hashMap.size > 0) { val iter: Iterator<*> = hashMap.entries.iterator() while (iter.hasNext()) { val entry = iter.next() as Map.Entry<*, *> val key = entry.key!! val `val` = entry.value!! data.append(key).append("=").append(`val`).append("&") } } val jso = data.substring(0, data.length - 1) return RequestBody.create("application/x-www-form-urlencoded; charset=utf-8".toMediaTypeOrNull(), jso) } }Copy the code

End: The above is the basic encapsulation of MVVM architecture. Please advise if there is any mistake.