Android Kotlin+Jetpack+MVVM

作者: 一个有故事的程序员 | 来源:发表于2021-05-31 17:46 被阅读0次

    开篇废话

    最近学习了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已经帮我们去处理了,不需要我们过多的去做什么。

    更多内容戳这里(整理好的各种文集)

    相关文章

      网友评论

        本文标题:Android Kotlin+Jetpack+MVVM

        本文链接:https://www.haomeiwen.com/subject/rvsvrltx.html