mvp、mvvm架构演进|研究
- 需求,手写一个微信"我的"页面的单页面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为类名的对象
。
- 1是
- 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为类名的对象
。
- 1是
- 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>