开篇废话
最近学习了Kotlin,学习了Jetpack,发现是真香,所以就手写了一个MVVM的框架,可以方便开发。Kotlin+Jetpack+MVVM之GitHub地址,帮我点个Star,赠人玫瑰,手留余香,谢谢。
先讲一下思路
ViewModel
ViewModel可以放一些数据和网络请求,通过LiveData回调给V层数据,因为LiveData会传入Lifecycle,可以防止内存泄漏。
Activity & Fragment
V层可以通过ViewModelStore拿到相应作用域的ViewModel,通过ViewModel去获取数据。
其它
同时简单封装了几个网络请求之后V层比较关心的请求结果的成功失败以及列表是否还有更多数据。
还封装了一个LiveData的扩展函数,减少V层逻辑,使代码更清晰。
上核心代码
对ViewModel进行封装
package com.cc.mvvm.mvvm
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
/**
* Created by guoshichao on 2021/2/20
* MVVM BaseModel
*/
abstract class BaseViewModel : ViewModel() {
val loadStateLiveData = MutableLiveData<LoadState>()
val hasMoreStateLiveData = MutableLiveData<HasMoreState>()
open fun init(arguments: Bundle?) {}
/**
* 加载数据开始 用调用V层的loadStart
*/
protected fun loadStart() {
loadStateLiveData.value = LoadState.LoadStart
}
/**
* 加载数据结束 用调用V层的loadFinish
*/
protected fun loadFinish(success: Boolean) {
if (success) {
loadStateLiveData.setValue(LoadState.LoadSuccess)
} else {
loadStateLiveData.setValue(LoadState.LoadFail)
}
}
/**
* 是否有更多数据 用调用V层的HasMore和noMore
*/
protected fun hasMore(hasMore : Boolean){
if (hasMore) {
hasMoreStateLiveData.value = HasMoreState.HasMore
} else {
hasMoreStateLiveData.value = HasMoreState.NoMore
}
}
override fun onCleared() {
Log.v("BaseViewModel", this.javaClass.name + this + " onCleared()")
super.onCleared()
}
}
对Activity进行封装
package com.cc.mvvm.mvvm
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.viewbinding.ViewBinding
import com.cc.mvvm.base.BaseActivity
import java.lang.reflect.ParameterizedType
/**
* Created by guoshichao on 2021/2/20
* MVVM BaseActivity
*/
abstract class MVVMBaseActivity<V : ViewBinding, M : BaseViewModel> : BaseActivity() {
protected lateinit var mViewBinding: V
protected lateinit var mViewModel: M
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
onPrepare()
mViewBinding = getViewBinding()
setContentView(mViewBinding.root)
mViewModel = getViewModel()!!
mViewModel.init(if (intent != null) intent.extras else null)
loadState()
onRegisterLiveListener()
liveDataObserver()
init()
}
/**
* 预配置
*/
protected open fun onPrepare() {}
/**
* 获取ViewBinding
*/
abstract fun getViewBinding(): V
/**
* 返回ViewModelStoreOwner
*/
protected open fun getViewModelStoreOwner() : ViewModelStoreOwner {
return this
}
/**
* 获取ViewModel
*/
protected open fun getViewModel(): M? {
//这里获得到的是类的泛型的类型
val type = javaClass.genericSuperclass
if (type != null && type is ParameterizedType) {
val actualTypeArguments = type.actualTypeArguments
val tClass = actualTypeArguments[1]
return ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application))
.get(tClass as Class<M>)
}
return null
}
/**
* LiveEventBus的Listener
*/
protected open fun onRegisterLiveListener() {}
/**
* LiveData的Observer
*/
protected abstract fun liveDataObserver()
/**
* 回调刷新控件状态
*/
private fun loadState() {
mViewModel.loadStateLiveData.observe(this, Observer {
when (it) {
LoadState.LoadStart -> loadStart()
LoadState.LoadSuccess -> loadFinish(true)
LoadState.LoadFail -> loadFinish(false)
}
})
mViewModel.hasMoreStateLiveData.observe(this, Observer {
when (it) {
HasMoreState.HasMore -> hasMore()
HasMoreState.NoMore -> noMore()
}
})
}
/**
* 初始化
*/
protected abstract fun init()
//加载开始
protected open fun loadStart() {}
//加载结束
protected open fun loadFinish(success: Boolean) {}
//有下一页
protected open fun hasMore() {}
//无下一页
protected open fun noMore() {}
}
对Fragment进行封装
package com.cc.mvvm.mvvm
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.viewbinding.ViewBinding
import com.cc.mvvm.base.BaseFragment
import java.lang.reflect.ParameterizedType
/**
* Created by guoshichao on 2021/2/20
* MVVM BaseFragment
*/
abstract class MVVMBaseFragment<V : ViewBinding, M : BaseViewModel> : BaseFragment() {
protected lateinit var mViewBinding: V
protected lateinit var mViewModel: M
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
onPrepare()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
mViewBinding = getViewBinding()
return mViewBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
load()
}
private fun load() {
mViewModel = getViewModel()!!
mViewModel.init(arguments)
loadState()
onRegisterLiveListener()
liveDataObserver()
init()
}
/**
* 预配置
*/
protected open fun onPrepare() {}
/**
* 获取ViewBinding
*/
abstract fun getViewBinding(): V
/**
* 返回ViewModelStoreOwner
*/
protected open fun getViewModelStoreOwner() : ViewModelStoreOwner {
return this
}
/**
* 获取ViewModel
*/
protected open fun getViewModel(): M? {
//这里获得到的是类的泛型的类型
val type = javaClass.genericSuperclass
if (type != null && type is ParameterizedType) {
val actualTypeArguments = type.actualTypeArguments
val tClass = actualTypeArguments[1]
return ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(activity!!.application))
.get(tClass as Class<M>)
}
return null
}
/**
* LiveEventBus的Listener
*/
protected open fun onRegisterLiveListener() {}
/**
* LiveData的Observer
*/
protected abstract fun liveDataObserver()
/**
* 初始化
*/
protected abstract fun init()
/**
* 回调刷新控件状态
*/
private fun loadState() {
mViewModel.loadStateLiveData.observe(viewLifecycleOwner, Observer {
when (it) {
LoadState.LoadStart -> loadStart()
LoadState.LoadSuccess -> loadFinish(true)
LoadState.LoadFail -> loadFinish(false)
}
})
mViewModel.hasMoreStateLiveData.observe(viewLifecycleOwner, Observer {
when (it) {
HasMoreState.HasMore -> hasMore()
HasMoreState.NoMore -> noMore()
}
})
}
//加载开始
protected open fun loadStart() {}
//加载结束
protected open fun loadFinish(success: Boolean) {}
//有下一页
protected open fun hasMore() {}
//无下一页
protected open fun noMore() {}
}
其它封装
LoadState的封装
package com.cc.mvvm.mvvm
enum class LoadState {
LoadStart, LoadSuccess, LoadFail
}
HasMoreState的封装
package com.cc.mvvm.mvvm
enum class HasMoreState {
HasMore, NoMore
}
LiveData扩展函数封装
package com.cc.mvvm.mvvm
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, observer: (t: T) -> Unit) {
liveData.observe(this, Observer { it?.let { t -> observer(t) } })
}
BaseActivity & BaseFragment
大家可以直接继承自己项目中的BaseActivity和BaseFragment,也可以使用我的,主要是加了一些Log,方便观察bug及数据等。
BaseActivity
package com.cc.mvvm.base;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by guoshichao on 2021/4/28
*/
public class BaseActivity extends AppCompatActivity {
protected void onCreate(Bundle args) {
if (Build.VERSION.SDK_INT == 26 && this.isTranslucentOrFloating()) {
boolean result = this.fixOrientation();
Log.i("BaseActivity", "onCreate fixOrientation when Oreo, result = " + result);
}
super.onCreate(args);
Log.v("BaseActivity", this.getClass().getName() + " onCreate()");
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.v("BaseActivity", this.getClass().getName() + " onSaveInstanceState()");
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.v("BaseActivity", this.getClass().getName() + " onRestoreInstanceState()");
}
protected void onStart() {
super.onStart();
Log.v("BaseActivity", this.getClass().getName() + " onStart()");
}
protected void onResume() {
super.onResume();
Log.v("BaseActivity", this.getClass().getName() + " onResume()");
}
protected void onPause() {
super.onPause();
Log.v("BaseActivity", this.getClass().getName() + " onPause()");
}
protected void onStop() {
super.onStop();
Log.v("BaseActivity", this.getClass().getName() + " onStop()");
}
protected void onDestroy() {
super.onDestroy();
Log.v("BaseActivity", this.getClass().getName() + " onCreate()");
}
private boolean isTranslucentOrFloating() {
boolean isTranslucentOrFloating = false;
try {
int[] styleableRes = (int[])((int[])Class.forName("com.android.internal.R$styleable").getField("Window").get((Object)null));
TypedArray ta = this.obtainStyledAttributes(styleableRes);
Method m = ActivityInfo.class.getMethod("isTranslucentOrFloating", TypedArray.class);
m.setAccessible(true);
isTranslucentOrFloating = (Boolean)m.invoke((Object)null, ta);
m.setAccessible(false);
} catch (Exception var5) {
var5.printStackTrace();
}
return isTranslucentOrFloating;
}
private boolean fixOrientation() {
try {
Field field = Activity.class.getDeclaredField("mActivityInfo");
field.setAccessible(true);
ActivityInfo o = (ActivityInfo)field.get(this);
o.screenOrientation = -1;
field.setAccessible(false);
return true;
} catch (Exception var3) {
var3.printStackTrace();
return false;
}
}
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.v("BaseActivity", this.getClass().getName() + " onNewIntent()");
}
}
BaseFragment
package com.cc.mvvm.base;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* Created by guoshichao on 2021/4/28
*/
public class BaseFragment extends Fragment {
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.v("BaseFragment", this.getClass().getName() + this + " onAttach()");
}
@TargetApi(22)
public void onAttach(Context context) {
super.onAttach(context);
Log.v("BaseFragment", this.getClass().getName() + this + " onAttach()");
}
public void onDetach() {
Log.v("BaseFragment", this.getClass().getName() + this + " onDetach()");
super.onDetach();
}
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("BaseFragment", this.getClass().getName() + this + " onCreate()");
}
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.v("BaseFragment", this.getClass().getName() + this + " onCreateView()");
View contentView = super.onCreateView(inflater, container, savedInstanceState);
return contentView;
}
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.v("BaseFragment", this.getClass().getName() + this + " setUserVisibleHint(), isVisibleToUser:" + isVisibleToUser);
}
public void onStart() {
super.onStart();
Log.v("BaseFragment", this.getClass().getName() + this + " onStart()");
}
public void onResume() {
super.onResume();
Log.v("BaseFragment", this.getClass().getName() + this + " onResume()");
}
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.v("BaseFragment", this.getClass().getName() + this + " onSaveInstanceState()");
}
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
Log.v("BaseFragment", this.getClass().getName() + this + " onViewStateRestored()");
}
public void onPause() {
super.onPause();
Log.v("BaseFragment", this.getClass().getName() + this + " onPause()");
}
public void onStop() {
super.onStop();
Log.v("BaseFragment", this.getClass().getName() + this + " onStop()");
}
public void onDestroyView() {
super.onDestroyView();
Log.v("BaseFragment", this.getClass().getName() + this + " onDestroyView()");
}
public void onDestroy() {
super.onDestroy();
Log.v("BaseFragment", this.getClass().getName() + this + " onDestroy()");
}
}
使用Demo
V层
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.cc.kotlin_jetpack_mvvm
import android.widget.Toast
import com.cc.kotlin_jetpack_mvvm.databinding.ActivityMainBinding
import com.cc.mvvm.mvvm.MVVMBaseActivity
import com.cc.mvvm.mvvm.observe
class MainActivity : MVVMBaseActivity<ActivityMainBinding, MainViewModel>() {
override fun getViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
}
override fun liveDataObserver() {
observe(mViewModel.modelLiveData, ::showToast)
}
private fun showToast(message : String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
override fun init() {
mViewBinding.tvHello.setOnClickListener{
mViewModel.getModel()
}
}
}
MainViewModel.kt
package com.cc.kotlin_jetpack_mvvm
import androidx.lifecycle.MutableLiveData
import com.cc.mvvm.mvvm.BaseViewModel
/**
* Created by guoshichao on 2021/4/28
*/
class MainViewModel : BaseViewModel() {
val modelLiveData = MutableLiveData<String>()
fun getModel() {
modelLiveData.value = "Hello!!!"
}
}
结束小语
总来来说,封装的还是比较简单的,因为很多工作Jetpack已经帮我们去处理了,不需要我们过多的去做什么。
网友评论