開篇廢話
最近學(xué)習(xí)了Kotlin僵腺,學(xué)習(xí)了Jetpack,發(fā)現(xiàn)是真香壶栋,所以就手寫了一個MVVM的框架辰如,可以方便開發(fā)。Kotlin+Jetpack+MVVM之GitHub地址贵试,幫我點個Star琉兜,贈人玫瑰凯正,手留余香,謝謝呕童。
先講一下思路
ViewModel
ViewModel可以放一些數(shù)據(jù)和網(wǎng)絡(luò)請求漆际,通過LiveData回調(diào)給V層數(shù)據(jù),因為LiveData會傳入Lifecycle夺饲,可以防止內(nèi)存泄漏奸汇。
Activity & Fragment
V層可以通過ViewModelStore拿到相應(yīng)作用域的ViewModel畔师,通過ViewModel去獲取數(shù)據(jù)她倘。
其它
同時簡單封裝了幾個網(wǎng)絡(luò)請求之后V層比較關(guān)心的請求結(jié)果的成功失敗以及列表是否還有更多數(shù)據(jù)。
還封裝了一個LiveData的擴展函數(shù)省有,減少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?) {}
/**
* 加載數(shù)據(jù)開始 用調(diào)用V層的loadStart
*/
protected fun loadStart() {
loadStateLiveData.value = LoadState.LoadStart
}
/**
* 加載數(shù)據(jù)結(jié)束 用調(diào)用V層的loadFinish
*/
protected fun loadFinish(success: Boolean) {
if (success) {
loadStateLiveData.setValue(LoadState.LoadSuccess)
} else {
loadStateLiveData.setValue(LoadState.LoadFail)
}
}
/**
* 是否有更多數(shù)據(jù) 用調(diào)用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()
}
/**
* 預(yù)配置
*/
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(getViewModelStoreOwner(),
ViewModelProvider.AndroidViewModelFactory.getInstance(application))
.get(tClass as Class<M>)
}
return null
}
/**
* LiveEventBus的Listener
*/
protected open fun onRegisterLiveListener() {}
/**
* LiveData的Observer
*/
protected abstract fun liveDataObserver()
/**
* 回調(diào)刷新控件狀態(tài)
*/
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() {}
//加載結(jié)束
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()
}
/**
* 預(yù)配置
*/
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(getViewModelStoreOwner(),
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()
/**
* 回調(diào)刷新控件狀態(tài)
*/
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() {}
//加載結(jié)束
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擴展函數(shù)封裝
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及數(shù)據(jù)等。
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!!!"
}
}
結(jié)束小語
總來來說普筹,封裝的還是比較簡單的败明,因為很多工作Jetpack已經(jīng)幫我們?nèi)ヌ幚砹耍恍枰覀冞^多的去做什么太防。