本
文
摘
要
本篇文章主要遵循Android mvvm进行代码架构构建,使代码耦合度高,扩展性强。本篇文章建议初级开发者阅读,大佬请忽略~~~~。
MVVM基础架构封装
基础类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 }}
BaseFragment 封装
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 } }BaseViewModel 层封装
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) } } } } } /** * 统一处理错误 * @param e 异常 * @param showErrorToast 是否显示错误吐司 */ @SuppressLint("WrongConstant") private fun onError(e: Exception, showErrorToast: Boolean) { when (e) { is ApiException -> { when (e.code) { -1001 -> { if (showErrorToast) { Toast.makeText(AppHelper.mContext,e.message,1000).show() } needLogin.value = true } // 其他错误 else -> { if (showErrorToast) Toast.makeText(AppHelper.mContext,e.message,1000).show() } } Log.e(TAG,e.toString()) } // 网络请求失败 is ConnectException, is SocketTimeoutException, is UnknownHostException, is HttpException -> { if (showErrorToast) Toast.makeText(AppHelper.mContext,"网络请求失败",1000).show() Log.e(TAG,"网络请求失败"+e.toString()) } // 数据解析错误 is JsonParseException -> { Log.e(TAG,"数据解析错误"+e.toString()) } // 其他错误 else -> { Log.e(TAG,"其他错误"+e.toString()) } } } }trip:关于CoroutineScope的介绍可以参考
zhuanlan.zhihu.com/p/297543508BaseVmActivity封装
abstract class BaseVmActivity<VB : ViewBinding, VM : BaseViewModel> : BaseActivity<VB>() { protected open lateinit var mViewModel: VM //加载数量 protected open val mTotalCount = 20 protected open var mCurrentSize = 0//当前加载数量 protected open var mCurrentPage = 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 (it) { SpUtil.setBoolean(MyConfig.IS_LOGIN, false) //跳转登录页面 } }) } private fun initViewModel() { mViewModel = ViewModelProvider(this).get(viewModelClass()) } abstract fun viewModelClass(): Class<VM> override fun onDestroy() { super.onDestroy() mCurrentSize = 0 mCurrentPage = 0 } }BaseVMFragment封装
abstract class BaseVMFragment<VB : ViewBinding, VM : BaseViewModel> : BaseFragment<VB>() { protected lateinit var mViewModel: VM private var lazyLoaded = false //分页参数 protected open val mTotalCount = 20//每次加载数量 protected open var mCurrentSize = 0//当前加载数量 protected open var mCurrentPage = 0//当前加载页数 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() { } }BaseRepository封装
open class BaseRepository{ protected fun apiService(): Api { return RetrofitClient.create(Api::class.java) } }网络架构封装
RetrofitClient类
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) //添加头部信息 .addInterceptor(AddCookiesInterceptor()) //拦截接口头部信息 // .addInterceptor(ReceivedCookiesInterceptor()) //日志拦截 .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) } }添加头部信息拦截
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()) } }提示 此处Authorization为后台jwt认证服务接口认证
以上部分为mvvm基础框架封装,下面部分主要讲解如何使用。
实例
接口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> /** * 获取登录token * 静态Header * @param requestBody body * @return */ @Headers("Authorization:Basic dmlkZW9hbmFseXNpczp2aWRlb2FuYWx5c2lz") @POST("/safeMobileServer/ *** c/auth/oauth/token") suspend fun toke(@Body requestBody: RequestBody?):LoginBean /** * 那些单位获取电厂作业数据 * @param headers 动态header * @param siteId 站点Id * @return */ @GET("/safeMobileServer/ *** c/p *** gpersonloccur/personCntByType") suspend fun workIndex( @Query("siteId") siteId: Int, ):HomeProadBean }LoginBean 实体类
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 } }HomeProadBean实体类
class HomeProadBean { /** * code : 0 * msg : 成功获取电厂现场人数分类统计 * data : [ * {"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 } }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) } }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) } ) } }LoginActivity 实现
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.login(requestBody) //登录返回结果数据订阅 mViewModel.workIndex(1) } override fun initView() { super.initView() mViewModel.loginInfo.observe(this, { mBinding?.tvContent?.text = it.getAccess_token() + "," + it.getUsername() //此处为登录接口,获取用户token SpUtil.setString(MyConfig.COOKIE, it.getAccess_token()) }) //此接口为获取正常数据接口,并需要带头部参数认证 mViewModel.homeProadBean.observe(this,{ it.getMsg()?.let { it1 -> Log.e("TAG", it1) } for(data in it.getData()!!){ Log.e("data", data?.personCnt.toString()) } }) } }RequestUtil
此方法是将map *** 转换为RequestBody实体类
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) } }end:以上为mvvm架构基础封装,如有不对之处请指教。
源码地址:
https://gitee.com/gxx123/android-mvvm-basic-framework更多android mvvm的优缺点(androidmvvm模式)相关信息请关注本站,本文仅仅做为展示!