架构演进|研究mvp到mvvm(传统架构mvvm和Jetpack下的区别)

  • 需求,手写一个微信"我的"页面的单页面APP。
  • 通过MVP、MVVM传统架构和Jetpack下新架构来实现。
  • "我的"页面显示数据,则来自读取本地的json文件。
  • 根据需求已实现效果,如图所示
    在这里插入图片描述

温馨提示:关键内容描述会体现在代码块中
项目源码点击下载 gitee源码查阅

MVP架构实现

MVP架构让View层专注UI逻辑和用户交互的处理。把View层中的业务逻辑全部分离出来,所有跟Android API无关的业务逻辑由 Presenter 层来完成。但是缺点就是增加了代码量。

MVP架构实现逻辑思想,在Presenter层负责处理业务逻辑以及执行数据请求,并通过Presenter层中View层的引用,调用回调方法将数据回传到View层,之后装显数据内容。而Presenter层持有View引用这一操作很关键!如何实现?下面揭晓!

  • 出示一下MVP架构下的代码层次
    在这里插入图片描述

定义数据结构类型

  • 定义用于封装数据的数据结构类型。
/**WechatInfo.kt ——(我的页面数据结构类)*/
data class WechatInfo(
    val wechatName: String,
    val wechatID: String,
    val wechatAvatar: String,
    val list: List<WechatItem>
)
data class WechatItem(val itemName: String, val itemIcon: String)
  • 定义并创建MVP架构中View层接口BaseView.java
  • 定义并创建MVP架构中Presener层基类BasePresenter.java
    BasePresenter使用泛型参数,目的是通过泛型参数可方便获得实现接口BaseView的实体类MainMvpActivity对象。
    其中BasePresenter作为Presenter层基类,为Presenter层持有View层引用创造了条件。
/**MVP架构View层基类 BaseView.java*/
public interface BaseView {
    
    
    boolean isActive();
    Context context();
}

/**MVP架构Presenter层基类 BasePresenter.java*/
public class BasePresenter<T extends BaseView> {
    
    
    public T view;
	// 将MainMvpActivity对象引用关联到Preseneter层
    public void attach(T view) {
    
    
        this.view = view;
    }
	// 解除关联
    public void detach() {
    
    
        this.view = null;
    }

}
  • 定义接口MainMvpContract.java,将View层Presenter层建立关联。

MainMvpContract类中,定义Presenter层抽象基类MainMvpPresenter,并传入接口MainMvpView作为泛型。成功将View层绑定到Presenter层。

/**MainMvpContract.java*/
public interface MainMvpContract {
    
    
	// View层基类的接口,定义回调方法将数据从Presenter层传递出
    interface MainMvpView extends BaseView {
    
    
        void userInfoCallback(WechatInfo wechatInfo);
    }
	// Presenter层基类抽象类,定义Presenter层获取服务数据的方法
	// 【关键】是 传入的BasePreseneter的泛型
    abstract class MainMvpPresenter extends BasePresenter<MainMvpView> {
    
    
        public abstract void getUserInfoPromote();
    }
}
  • 创建MVP架构Presenter层的实体类MainMvpPresenter.java
/**MainMvpPresenter.java*/
public class MainMvpPresenter extends MainMvpContract.MainMvpPresenter {
    
    
    @Override
    public void getUserInfoPromote() {
    
    // 获取数据的方法
        WechatInfo wechatInfo = getWechatInfo(view.context());// 获取数据
        view.userInfoCallback(wechatInfo); // 回调到MainMvpActivity实现的方法userInfoCallback

    }
	// 获取数据(读取本地的json)
    private WechatInfo getWechatInfo(Context context) {
    
    
        AssetManager assetManager = context.getAssets();
        try {
    
    
            InputStreamReader reader = new InputStreamReader(assetManager.open("mvvm.json"));
            BufferedReader bufferedReader = new BufferedReader(reader);
            String line;
            StringBuffer stringBuffer = new StringBuffer();
            while ((line = bufferedReader.readLine()) != null) {
    
    
                stringBuffer.append(line);
            }
            bufferedReader.close();
            reader.close();

            Gson gson = new Gson();
            WechatInfo wechatInfo = gson.fromJson(stringBuffer.toString(), WechatInfo.class);
            return wechatInfo;

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

        return null;

    }
}
  • 创建View层实体类MainMvpActivity.kt,实现在中间类MainMvpContract中定义的接口MainMvpView。由此,Presenter层中的View层引用则成功绑定到MainMvpActivity。
  • 省略RecyclerView列表代码逻辑

@Route(path = "/mvp/activity/MainMvpActivity")
class MainMvpActivity : AppCompatActivity(), MainMvpContract.MainMvpView {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_mvp)

        val mainMvpPresenter = MainMvpPresenter()
        mainMvpPresenter.attach(this) // 绑定当前View层对象引用到Presenter层

        mainMvpPresenter.getUserInfoPromote()
    }

    override fun isActive(): Boolean {
    
    
        return isFinishing || isDestroyed
    }

    override fun context(): Context {
    
    
        return this
    }

    override fun userInfoCallback(wechatInfo: WechatInfo?) {
    
    
        wechatInfo?.let {
    
     info ->
            updateUI(info)
        }
    }
    private fun updateUI (info: WechatInfo) {
    
    
        Glide.with(this).load(info.wechatAvatar).into(user_avatar)
        user_name.text = info.wechatName
        user_id.text = info.wechatID

        recycle_list.layoutManager = LinearLayoutManager(this)
        val adapter = MainMvpAdapter()
        recycle_list.adapter = adapter
        adapter.addData(info.list)
    }
}

即使在经历了MVP架构改进后,许多的接口、抽象类也不免被创建。并且基于这种架构模式,若要在项目中大量沿用。那么每个UI页面,都要重复创建类似的接口、抽象类进行规范。由此工程中就会积累大量的近乎重复的文件。个人觉得这种架构模式即使相对于古老的MVC架构更还有优势,但是劣势也是非常显著。MVP架构若相比于MVVM的架构而言,MVVM架构更适合应用到项目中。

MVP架构改进——反射获取泛型类型实例

我们看到在View层MainMvpActivity中部分代码可以继续精简。比如BaseView中的回调方法、Presenter层对象的创建过程等。

  • 精简BaseView中的回调方法,使用基类BaseActivity将回调方法内聚。
  • 精简Presenter层对象的创建方式,将其对象创建内聚到BaseActivity中,使用泛型参数反射创建获得Preseneter层对象。
// MainMvpBaseActivity.java
/**创建View层基类,将部分方法、Presenter对象创建内聚*/
public class MainMvpBaseActivity<P extends BasePresenter>
        extends AppCompatActivity implements BaseView {
    
    
    protected P mPresenter = null;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
// 这里为什么不适用arguments[0].getClass()得到Class呢?	像下面代码逻辑获取Presenter层对象实例?
//        Type genericSuperclass = this.getClass().getGenericSuperclass();
//        // 判断类是否是泛型参数类型
//        if (genericSuperclass instanceof ParameterizedType) {
    
    
//            Type[] arguments = (ParameterizedType)genericSuperclass.getActualTypeArguments();
//            if (arguments != null && arguments[0] instanceof BasePresenter) {
    
    
//                try {
    
    
//                    mPresenter = (P)arguments[0].getClass().newInstance();
//                    mPresenter.attach(this); // 绑定view层对象到presenter层
//                } catch (Exception ex){
    
    
//                    ex.printStackTrace();
//                }
//            }
//        }

        Type genericSuperclass = this.getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
    
    
            // 具备反省参数类型
            Type[] arguments = ((ParameterizedType)genericSuperclass).getActualTypeArguments();
            try {
    
    
                try {
    
    
                    Class cls = (Class) arguments[0];
                    if (cls != null && cls.newInstance() instanceof BasePresenter) {
    
    
                        mPresenter = (P) cls.newInstance();
                        mPresenter.attach(this);
                    }
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }	
    }

    @Override
    public boolean isActive() {
    
    
        return isFinishing() || isDestroyed();
    }

    @Override
    public Context context() {
    
    
        return this;
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        mPresenter.detach();
    }
}



// MainMvpActivity.kt 改进之后的文件代码逻辑。确实精简了许多。
class MainMvpActivity : MainMvpBaseActivity<MainMvpPresenter>(),
    MainMvpView {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_mvp)

        mPresenter!!.getUserInfoPromote() // 获取在基类中反射获取到的对象。别的逻辑内聚到基类。
    }

    override fun userInfoCallback(wechatInfo: WechatInfo?) {
    
    
        wechatInfo?.let {
    
     info ->
            updateUI(info)
        }
    }
    private fun updateUI (info: WechatInfo) {
    
    
        Glide.with(this).load(info.wechatAvatar).into(user_avatar)
        user_name.text = info.wechatName
        user_id.text = info.wechatID

        recycle_list.layoutManager = LinearLayoutManager(this)
        val adapter = MainMvpAdapter()
        recycle_list.adapter = adapter
        adapter.addData(info.list)
    }
}
  • MainMvpBaseActivity.java分析代码第9行 到 23行,回答第9行问题~
  • 在进行MVP架构改进的过程,执行Class cls = arguments[0].getClass();得到Class对象是java.lang.Class,并不是泛型类型(如这里的MainMvpPresenter)的Class对象。
  • 因为arguments[0]返回是接口Type,Type.getClass()得到的会是java.lang.Class。然而跟进会发现argments[0]值内容是泛型类型(如这里的MainMvpPresenter)的Class,且Class是Type接口的实现类。那么进行Class类型对Type接口强转就能获得泛型类型的Class,之后.newInstance()可获得对象实例

MVVM架构基础

配置DataBinding

注意:若BataBinding应用在Project下多模块、不同层级的模块中时,每个module都要配置。
该操作可实现传统MVVM架构

// *.gradle文件的android{}根节点下
// 开启dataBinding,不需要引入额外的依赖库
    dataBinding{
    
    
        enabled = true
    }

添加Jetpack组件

另外,若在使用的module下*.gradle添加下面依赖库支持,
将ViewModel+LiveData组件结合,可实现Jetpack下的MVVM架构。

apply plugin: 'kotlin-kapt' 

// dependencies...
api 'androidx.appcompat:appcompat:1.1.0'
api "androidx.lifecycle:lifecycle-extensions:2.0.0"
kapt "androidx.lifecycle:lifecycle-compiler:2.0.0"

MVVM架构下的项目层次目录

在这里插入图片描述

传统MVVM架构实现

创建类ViewModel层(即下面的DataBindingOldViewModelMvvm.java),类名自定义,需要在该ViewModule中定义观察者ObservableField,并将请求到的数据存入ObservableField对象中,以起到观察作用。之后结合Activity中由DatabindingUtil.setContentView生成的ViewDataBinding对象完成数据绑定。

如何使“我的页面”布局文件具被主动赋值能力?答:完成数据绑定后即可。

  • 配置布局:在布局文件<variable>节点中<type>导入已定义的ViewModel类<name>是为该ViewModule类设置的对象别名。
  • DataBindingUtil.setContentView,一共做了两件事。
    • 1是activity.setContentView(layoutId);,等同于继续Activity的setContentView工作。
    • 2是完成对布局View的绑定并生成以驼峰布局名称+BindingImpl为类名的对象
  • ViewModel与DataBinding实现数据绑定:Activity中使用DataBindingUtil.setContentView返回ViewDataBinding对象,并调用set方法将自定义ViewModel层对象赋值给布局中的ViewModel,即完成了数据绑定。

ViewDataBinding对象,类名是Activity布局文件名称(_下划线拼接转驼峰)+Binding

定义数据结构类型

  • 定义用于封装数据的Bean数据结构类型。
/**WechatInfo.kt —— 自定义Bean类(我的页面数据结构类)*/
data class WechatInfo(
    val wechatName: String,
    val wechatID: String,
    val wechatAvatar: String,
    val list: List<WechatItem>
)
data class WechatItem(val itemName: String, val itemIcon: String)

创建传统架构下的ViewModel

  • 传统架构下自定义类ViewModel,用于处理与数据相关的业务逻辑。然后通过观察者ObservableField将数据传递出去。
/**DataBindingOldViewModelMvvm.java —— 自定义ViewModel*/
// 定义VM  —— ViewModule
public class DataBindingOldViewModelMvvm {
    
    

    public ObservableField<WechatInfo> obField;

    protected void getUserInfoPromote(Context context, ViewModelCallback callback) {
    
    
        obField = new ObservableField<WechatInfo>();
        WechatInfo info = getWechatInfo(context);
        obField.set(info);
        callback.onViewModelCallback(info); // 通过接口回调的方式,将由后台获得的数据传递出去

    }
	// 读取json文件
    private WechatInfo getWechatInfo(Context context) {
    
    
        ...}

    interface ViewModelCallback {
    
    
        void onViewModelCallback(WechatInfo wechatInfo);
    }
}

创建Activity及基于DataBinding布局文件

  • Activity中控制并获取数据,并基于DataBinding将ViewModel和布局(View层)实现了绑定。
/**DataBindingOldMvvmActivity.kt*/
@Route(path = "/old/activity/OldMvvmActivity")
class DataBindingOldMvvmActivity : AppCompatActivity() {
    
    
    private lateinit var binding: ActivityOldDatabindingmvvmBinding
    private lateinit var viewModelMvvm: DataBindingOldViewModelMvvm

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        // 绑定布局中的View,并返回绑定后的ViewDataBinding对象
        binding = DataBindingUtil.setContentView(this, R.layout.activity_old_databindingmvvm)
        viewModelMvvm = DataBindingOldViewModelMvvm()
        binding!!.oldMvvm = viewModelMvvm // 传统架构模式下,实现数据赋值绑定
        
        viewModelMvvm!!.getUserInfoPromote(this, DataBindingOldViewModelMvvm.ViewModelCallback {
    
    
            onUpdateUI(it)
        })
    }

	/**该方法是为RecyclerView列表配置、赋初始值*/
    private fun onUpdateUI(wechatInfo: WechatInfo) {
    
    
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = RecyclerView.VERTICAL
        binding!!.recycleList.layoutManager = layoutManager
        val adapter = DataBindingOldAdapter()
        binding!!.recycleList.adapter = adapter
        adapter.addData(wechatInfo.list)
    }
}

  • 基于DataBinding在下面xml布局文件中进行数据绑定,即可以直接在xml布局文件中完成数据,事件绑定等工作。并由此可实现由数据 到 UI的双向绑定。

双向绑定:即~数据变更,UI将自动刷新;UI改变,则数据将自动同步。

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable
            name="oldMvvm" // 自定义别名
            type="org.bagou.xiangs.framework_mvvm.older.DataBindingOldViewModelMvvm" /> // 自定义的ViewModel类
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#aaf4f4f4"
        tools:context=".newer.NewMvvmActivity">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/layout_user_info"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:paddingHorizontal="20dp"
                android:paddingVertical="40dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent">

                <ImageView
                    android:id="@+id/user_avatar"
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    app:layout_constraintLeft_toLeftOf="@id/layout_user_info"
                    app:layout_constraintTop_toTopOf="@id/layout_user_info"
                    app:circleUrl="@{oldMvvm.obField.wechatAvatar}"
                    android:src="@drawable/bb2" />
				/** 这里TextView 单向绑定:双向绑定使用 '@={}' 单向绑定使用 '@{}' */
                <TextView
                    android:id="@+id/user_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginHorizontal="15dp"
                    android:layout_marginTop="4dp"
                    android:textColor="#000"
                    android:textFontWeight="800"
                    android:textSize="18sp"
                    android:text="@{oldMvvm.obField.wechatName}" 
                    app:layout_constraintLeft_toRightOf="@+id/user_avatar"
                    app:layout_constraintTop_toTopOf="@id/layout_user_info"
                    tools:text="用户名称" />

                <TextView
                    android:id="@+id/user_id"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginHorizontal="15dp"
                    android:layout_marginBottom="6dp"
                    android:textColor="#666"
                    android:textFontWeight="400"
                    android:textSize="14sp"
                    android:text="@{oldMvvm.obField.wechatID}"
                    app:layout_constraintBottom_toBottomOf="@id/layout_user_info"
                    app:layout_constraintLeft_toRightOf="@+id/user_avatar"
                    tools:text="微信号:cangpingqu" />
            </androidx.constraintlayout.widget.ConstraintLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycle_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </LinearLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

实现RecyclerView列表UI数据绑定

Activity中使用DataBindingUtil.setContent,而在RecyclerListView中使用DataBindingUtil.inflate。或者使用Activity中生成的ViewDataBinding对象.inflate来完成对布局文件的View绑定。


/** DataBindingOldAdapter.kt */
class DataBindingOldAdapter : RecyclerView.Adapter<DataBindingOldAdapter.MvvmViewHolder>() {
    
    

    private var dataList: ArrayList<WechatItem> = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MvvmViewHolder {
    
    
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding: ItemUserOldDatabindingBinding =
            DataBindingUtil.inflate<ItemUserOldDatabindingBinding>(
                layoutInflater,
                R.layout.item_user_old_databinding,
                parent,
                false
            )
        binding.root.setBackgroundColor(Color.WHITE)
        return MvvmViewHolder(binding)
    }

    override fun getItemCount(): Int {
    
    
        return dataList.size
    }

    override fun onBindViewHolder(holder: MvvmViewHolder, position: Int) {
    
    
        onViewLayoutSet(holder, position)
        // 完成布局中数据对象赋值的绑定
        holder.binding.setVariable(BR.infoItem, dataList[position])

    }

	// 初始化数据
    fun addData(data: List<WechatItem>) {
    
    
        dataList.addAll(data)
        notifyDataSetChanged()
    }

    inner class MvvmViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root)

    // 设置控件样式
    private fun onViewLayoutSet(holder: MvvmViewHolder, position: Int) {
    
    
        val layoutParams: ViewGroup.LayoutParams = holder.binding.root.layoutParams
        when (position) {
    
    
            0 -> {
    
    
                (layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin = 30
                (layoutParams as? ViewGroup.MarginLayoutParams)?.bottomMargin = 30
            }
            itemCount - 1 -> (layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin = 30
            else -> (layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin = 1
        }
        holder.binding.root.layoutParams = layoutParams
    }
}

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="infoItem"
            type="org.bagou.xiangs.framework_mvvm.older.WechatItem" />
    </data>

    <LinearLayout
        android:id="@+id/layout_list_item"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:paddingHorizontal="20dp"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:background="@android:color/white">

        <ImageView
            android:id="@+id/item_icon"
            android:layout_width="25dp"
            android:layout_height="25dp"
            app:circleUrl="@{infoItem.itemIcon}"
            />

        <TextView
            android:id="@+id/item_text"
            android:textColor="#333"
            android:textSize="16sp"
            android:text="@{infoItem.itemName}"
            android:layout_marginHorizontal="15dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Space
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1" />

        <ImageView
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:padding="4dp"
            android:src="@drawable/ic_arrow" />
    </LinearLayout>
</layout>

Jetpack下MVVM架构实现

创建类ViewModel(即下文的DatabindingNewViewModelMvvm.java),类名自定义,但须继承Jetpack组件类ViewModel。结合liveData组件将请求得到的数据对象传递出去,并在Activity中注册Observer观察者并接收数据。之后结合Activity中由DatabindingUtil.setContentView生成的ViewDataBinding对象完成数据绑定。

Jetpack下如何使“我的页面”布局文件具被主动赋值能力?答:完成数据绑定后即可。

  • 配置布局:在布局文件<variable>节点中<type>导入已定义的WechatInfo.kt类(封装数据的数据结构类型)<name>是为该数据结构类设置的对象别名。
  • DataBindingUtil.setContentView,一共做了两件事。
    • 1是activity.setContentView(layoutId);,等同于继续Activity的setContentView工作。
    • 2是完成对布局View的绑定并生成以驼峰布局名称+BindingImpl为类名的对象
  • Model与dataBinding实现数据绑定:Activity中使用DataBindingUtil.setContentView替换原setContentView()并返回一个ViewDataBinding对象,使用该对象的set方法将从观察者得到的数据赋值给数据结构类对象。从而建立关系即完成了数据绑定。

ViewDataBinding对象,类名是Activity布局文件名称(_下划线拼接转驼峰)+Binding

定义数据结构类型

数据结构对象定义不变,同上 WechatInfo.kt

定义Jetpack组件下的ViewModel

  • Jetpack新架构下定义的ViewModel需要继承Jetpack组件ViewModel。为关联宿主生命周期,并能避免数据无故丢失问题。
/**DatabindingNewViewModelMvvm.kt —— 继承了Jetpack组件ViewModel*/
public class DatabindingNewViewModelMvvm extends ViewModel {
    
    

    protected LiveData<WechatInfo> getUserInfoPromote(Context context) {
    
    
		// 定义LiveData组件对象,向观察者发送数据
        MutableLiveData<WechatInfo> liveData = new MutableLiveData<>();
        WechatInfo info = getWechatInfo(context);
        liveData.postValue(info); // 通过liveData组件将数据传递出去
        return liveData;
    }

    private WechatInfo getWechatInfo(Context context) {
    
    
        ...}

创建Activity及基于DataBinding布局文件

  • 在Jetpack新架构模式下,由ViewModelProvider轻松获得我们定义的Jetpack组件对象viewModel。然后添加观察者并获取ViewModel层传递出来的数据。
/**DatabindingNewMvvmActivity.kt*/

@Route(path = "/new/activity/NewDatabindingMvvmActivity")
class DatabindingNewMvvmActivity : AppCompatActivity() {
    
    

    private var binding: ActivityNewDatabindingmvvmBinding? = null
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        // 绑定布局中View,并返回ViewDataBinding对象
        binding = DataBindingUtil.setContentView<ActivityNewDatabindingmvvmBinding>(this, R.layout.activity_new_databindingmvvm)
        getUserInfoPromote()
    }


    private fun onUpdateUI(wechatInfo: WechatInfo?) {
    
    
        binding!!.dataModel = wechatInfo // 数据绑定
		/**该方法是为RecyclerView列表配置、赋初始值*/
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = RecyclerView.VERTICAL
        recycle_list.layoutManager = layoutManager
        val adapter = DataBindingNewMvvmAdapter()
        recycle_list.adapter = adapter
        adapter.addData(wechatInfo!!.list)
    }

    private fun getUserInfoPromote() {
    
    
    	// 获取ViewMedelProvider对象,并通过ViewMedelProvider获取ViewModel组件对象
        val viewModelProvider = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
        val viewModel = viewModelProvider.get(DatabindingNewViewModelMvvm::class.java)
        viewModel.getUserInfoPromote(this@DatabindingNewMvvmActivity)
        	// 添加观察者
            .observe(this@DatabindingNewMvvmActivity,
                Observer {
    
     wechatInfo ->
                    runOnUiThread {
    
    
                        onUpdateUI(wechatInfo)
                    }
                })
    }
}

布局文件中省略部分则和传统MVVM架构布局文件相同。
部分相同内容省略则是为凸显两种架构模式下的区别。

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable
            name="dataModel"
            type="org.bagou.xiangs.framework_mvvm.newer_databinding.WechatInfo" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        ...>
        <LinearLayout
            ...>
            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/layout_user_info"
                ....>

                <ImageView
                    android:id="@+id/user_avatar"
                    app:circleUrl="@{dataModel.wechatAvatar}"
                    .../>

                <TextView
                    android:id="@+id/user_name"
                    android:text="@{dataModel.wechatName}"
                    tools:text="用户名称" 
                    .../>

                <TextView
                    android:id="@+id/user_id"
                    android:text="@{dataModel.wechatID}"
                    tools:text="微信号:" 
                    .../>
            </androidx.constraintlayout.widget.ConstraintLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycle_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </LinearLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Jetpack下实现RecyclerView列表UI数据绑定

Jetpack新架构模式下,RecyclerView列表UI数据绑定代码逻辑 与 传统架构模式下一模一样。

传统架构和Jetpack下新架构区别

  • Jetpack下的新架构,使用组件LiveData、ViewModel可关联宿主生命周期(有效避免空指针)。使用ViewModel组件,又能有效防止页面数据由于内存不足等非正常原因导致的数据丢失。若同时基于Databinding,此时Activity和Fragment只关注UI逻辑和用户的交互即可,数据绑定则交给Databinding!

  • 分析两种架构实现方案看,传统模式的架构在灵活度上逊色于Jetpack下新架构。虽然两者都在定义了自己的ViewModel。然而在Jetpack架构模式下,定义ViewModel可应用的地方更加灵活和广泛。因为我们可以将该架构下的ViewModel单独拎出来使用,具备非常方便的解耦能力。

  • 传统架构下,需要在定义的ViewModel中定义ObservableField,以实现对绑定数据的监听。Jetpack新架构下,则需要继承Jetpack组件ViewModel,并结合组件LIveData将数据发送出去。

  • 两种架构下,在Activity(V层)中两者同样使用DataBindingUtil.setContentView得到的ViewDataBinding对象。传统架构得到的ViewDataBinding对象用来绑定赋值定义的ViewModel对象(VM层);Jetpack架构下得到的ViewDataBinding对象则用来绑定赋值Bean对象(M层)。

  • 两种架构模式下,布局文件的配置也有区别。

    • 传统架构,<variable>标签配置自定义的ViewModel类,并通过该ViewModel别名对象.ObservableField对象.Bean对象.变量从而对控件进行赋值绑定操作。
    • Jetpack新架构,<variable>标签配置自定义的Bean类,并通过该Bean别名对象.变量从而对控件进行赋值绑定操作。
  <data> // 传统架构模式下
     <variable
            name="oldMvvm" // 自定义ViewModel对象别名
            type="org.***.DataBindingOldViewModelMvvm" />  // 定义的ViewModel类,包名+类名
  </data>
  <data> // Jetpack新架构模式下
     <variable
            name="dataModel" // Bean对象别名
            type="org.***.WechatInfo" />  // 定义的Bean类,包名+类名
  </data>

猜你喜欢

转载自blog.csdn.net/u012827205/article/details/125322817