目录
一、Jetpack学习
1.1lifecycle
1.1.1简介
在使用MVP模式编写代码时,常常需要在View(如Activity或Fragment)的onCreate()函数中进行Presenter的初始化操作,在onDestroy()的函数中进行Presenter的销毁工作,当Presenter很多时,View各个生命周期函数中的代码将变得非常多。
而lifecycle能够监听Activity或者Fragment的生命周期,并在生命周期改变时通知Presenter执行相应的函数,因此使用lifecycle能减少View中的代码量。
1.1.2 使用方法
假设有一个Activity类和一个Presenter类,下面将介绍lifecycle使用方法。
(1)添加lifecycle的依赖
(2)Present继承LifeCycleObserver接口
class Presenter : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onResume() {
Log.d("Presenter", "onCreate")
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onPause() {
Log.d("Presenter", "onDestroy")
}
}
(3)在Activity的onCreate()方法中注册Presenter
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getLifecycle().addObserver(Presenter())
}
override fun onDestroy() {
super.onDestroy()
}
}
这样,当Activity生命周期变化后,Presenter中重写的方法也会被重写。
1.1.3 原理
lifecycle使用了观察者模式,在上边的例子中,继承了LifecycleObserver的Presenter是观察者,Activity是被观察者,当Activity的生命周期变化时,Activity会通知已经注册的观察者自己的生命周期发生改变,从而使Presenter作出相应的操作。
(1)注册观察者的原理
那么Presenter是如何被Activity注册的呢?在1.1.2小节中,Activity通过getLifecycle().addObserver(Presenter())方法注册观察者Presenter,getLifecycle()实际是接口LifecycleOwner的一个方法,Activity继承的ComponentActivity实现了该接口并通过该方法返回了LifecycleRegistry类的一个实例,LifecycleRegistry类继承了抽象类Lifecycle,是实现lifecycle监听生命周期功能的核心类,LifecycleRegistry实例和Activity(LifecycleOwner)互相持有对方的引用。我认为该LifecycleRegistry实例为lifecycle中被观察者监听自身生命周期并通知注册的观察者的核心。
而LifecycleRegistry中存有一个FastSafeIterableMap类的实例mObserverMap,FastSafeIterableMap类的底层实现原理还是HashMap,当Acitivity调用addObserver(Presenter())方法后,实际上是调用了LifecycleRegistry的addObserver(observer:LifecycleObserver)方法,在该方法中,LifecycleRegistry将Presenter和它对应的状态STATE存入mObserverMap中,这样就完成了观察者的注册。
(2)被观察者监听生命周期并通知观察者的原理。
被观察者(Activity)又是如何监听自己的生命周期变化并通知已经注册的观察者(Presenter)们呢?实际上,被观察者可以在自己的每个生命周期函数中都调用函数去通知观察者自己的生命周期改变了,但lifecycle没有这么做,继承LifeCycleOwner类的ComponentActivity在onCreate()函数中注册了一个继承自Fragment类的ReportFragment 。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedStateRegistryController.performRestore(savedInstanceState);
ReportFragment.injectIfNeededIn(this);
if (mContentLayoutId != 0) {
setContentView(mContentLayoutId);
}
}
因为Fragment依赖于创建它的Activity,所以ReportFragment的生命周期和Activity生命周期同步,ReportFragment中在生命周期函数中调用了dispatch()函数,从而通知所有注册的观察者Activity的生命周期发生改变。
@Override
public void onPause() {
super.onPause();
dispatch(Lifecycle.Event.ON_PAUSE);
}
@Override
public void onStop() {
super.onStop();
dispatch(Lifecycle.Event.ON_STOP);
}
在dispatch()函数中,ReportFragment调用了与它绑定的Activity内部的LifecycleRegistry实例的handleLifecycleEvent(event)方法,来处理通知观察者的事项。
private void dispatch(Lifecycle.Event event) {
Activity activity = getActivity();
if (activity instanceof LifecycleRegistryOwner) {
((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
return;
}
if (activity instanceof LifecycleOwner) {
Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
if (lifecycle instanceof LifecycleRegistry) {
((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
}
}
}
在handleLifecycleEvent()方法中,LifecycleRegistry又依次调用moveToState()->sync()方法。
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
State next = getStateAfter(event);
moveToState(next);
}
private void moveToState(State next) {
if (mState == next) {
return;
}
mState = next;
if (mHandlingEvent || mAddingObserverCounter != 0) {
mNewEventOccurred = true;
// we will figure out what to do on upper level.
return;
}
mHandlingEvent = true;
sync();
mHandlingEvent = false;
}
在sync()方法中,LifecycleRegistry通过判断mObserverMap中存储的第一个注册的观察者和最后一个注册的观察者的状态是否相同来判断是否需要进行同步,并根据fordwardPass()和backwardPass()方法进行同步。
private void sync() {
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
+ "garbage collected. It is too late to change lifecycle state.");
}
while (!isSynced()) {
mNewEventOccurred = false;
// no need to check eldest for nullability, because isSynced does it for us.
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
backwardPass(lifecycleOwner);
}
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
mNewEventOccurred = false;
}
而在fordwardPass()中,LifecycleRegistry调用需要同步的观察者(Presenter)的dispatcherEvent()方法进行同步,并最终执根据Activity生命周期的当前状态执行Presenter中我们写的方法。
private void backwardPass(LifecycleOwner lifecycleOwner) {
Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
mObserverMap.descendingIterator();
while (descendingIterator.hasNext() && !mNewEventOccurred) {
Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
ObserverWithState observer = entry.getValue();
while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
Event event = downEvent(observer.mState);
pushParentState(getStateAfter(event));
observer.dispatchEvent(lifecycleOwner, event);
popParentState();
}
}
}
1.2 databinding
1.2.1简介
databinding能实现数据和UI绑定,即当数据改变后自动更新UI,或通过UI直接更新绑定的数据,这将大大减少程序员在更新UI或获取UI数据方便代码的编写量。
1.2.2使用方法
(1)添加依赖
在module的build.gradle文件中添加如下代码,开启databinding功能。
buildFeatures{
dataBinding true
}
(2)新建数据类
新建一个类,将想要和UI进行绑定的数据声明为ObservableField()类型。
class LoginMessageModel{
var username= ObservableField<String>(name)
var password = ObservableField<String>(word)
}
(3)更改布局文件
右键点击布局文件的根(最外层)布局,点击"Convert to data binding layout"选项,即可将布局文件转换成符合databinding要求的布局文件,修改后发现,该布局文件的最外层增加了一层<layout>标签,且在<layout>标签内增加了<data>标签。
<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="model"
type="com.example.testjetpack.login.LoginMessageModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".login.MainActivity">
当我们想要让数据类和布局绑定时,需要如上图所示将数据类写入<data>标签中的<variable>标签中,其中type参数表示数据类的全限定名,name参数表示数据类的在布局文件中的别名。
之后,当想要某个布局文件的内容与数据类中的数据绑定时,可通过@={类名.参数名}的方式进行绑定,如下图所示。
android:padding="10dp"
android:src="@drawable/ic_baseline_account_circle_24"></ImageView>
<EditText
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="@={model.username}"
android:padding="10dp">
</EditText>
</LinearLayout>
注意这里的参数名必须是声明为ObservableField()类型的参数。
(4)生成绑定类
在执行完上述操作后,点击Make Project,Android Studio就会自动生成绑定类,下图中绑定类的名字是ActivityMainBinding.java。
(5)进行绑定
在Activity的onCreate()中,通过DataBindingUtil.setContentView()方法获取绑定类实例,并指定绑定类实例的model(在布局文件的<data>标签中声明过)参数所对应的实例,这样就完成了数据和UI的绑定。
class MainActivity : AppCompatActivity(), View.OnClickListener {
var loginModel: LoginMessageModel?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val dataBinder: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
loginModel= LoginMessageModel("", "", this)
dataBinder.model = loginModel
findViewById<Button>(R.id.login).setOnClickListener(this)
}
二、Jetpack实践
2.1实现登录功能的demo
使用kotlin编写实现登录功能的demo,demo依据MVVM架构进行编写,使用databinding获取用户通过界面输入的账号和密码数据,并根据登录是否成功修改界面的UI。Demo的工作流程如下图所示:
Demo客户端代码上传到gitee仓库中,仓库地址为:logindemo: 自学demohttps://gitee.com/teslgw/logindemo。