【小木箱生长营】Android事务架构系列文章:

Android事务架构 进步篇 MVC、MVP、MVVM和MVI四剑客

Android事务架构 实践篇 MVI+Jetpack+Kotlin手把手搭建直播运用App

Tips: 关注小木箱生长营大众号, 回复”事务架构”可免费获取Android事务架构思想导图。

一、序言

Hello,我是小木箱,欢迎来到小木箱生长营事务架构系列教程,今日同享的内容是事务架构 根底篇 Jetpack四件套。

2017年,Google发布了Android Architecture Components,包括Room、LiveData、ViewModel和Paging等组件,旨在协助开发者更轻松地完结MVVM架构。

2018年,Google在I/O大会上推出的一套Android开发组件库,旨在协助开发者更轻松、更高效地构建Android运用。

跟着时间的推移,Android Jetpack不断地更新和添加新的组件,使得Android运用的开发愈加高效、安稳和可保护。

今日的主题主要分为三个维度。第一个维度是4W2H剖析Jetpack,第二个维度是Jetpack四件套。第三个维度是总结与展望。

其间,4W2H剖析Jetpack主要针对Jetpack提出了6个高价值问题。

其间,Jetpack四件套列举了LifeCycle、LiveData、ViewModel和DataBing四种常见的Jetpack东西包。

Android业务架构  基础篇  Jetpack四件套

假如学完事务架构系列教程,那么任何人都能完整构建一套合适企业事务背景的架构规划。

二、4W2H剖析Jetpack

2.1 What: Jetpack是什么?

Android业务架构  基础篇  Jetpack四件套

Android Jetpack是一组Android软件组件、东西和指南,它们可以协助开发者构建高质量、安稳的Android运用程序。Jetpack中包括多个库,它们旨在处理Android运用程序开发中的常见问题,并供给共同的API和开发体会。

Jetpack中包括的库包括:

  1. ViewModel:协助办理UI组件的生命周期并存储和办理UI相关的数据。
  2. LiveData:供给了呼应式编程的功用,可以让数据在数据源发生改动时主动更新UI。
  3. Room:供给了一个笼统层,可以让开发者方便地拜访和办理SQLite数据库。
  4. Navigation:供给了一种简略、共同的办法来处理运用程序的导航。
  5. WorkManager:供给了一种简略、牢靠的办法来办理后台使命。 除此之外,Jetpack还包括了比如Paging、Data Binding、Preferences、Security等库,这些库都旨在简化开发过程并进步运用程序的功用和牢靠性。

2.2 Where: 什么场景下运用Jetpack?

Jetpack适用于开发各种类型的Android运用程序,包括单页面运用程序、多页面运用程序、后台使命运用程序等。下面是一些合适运用Jetpack的场景:

  1. 构建大型运用程序:Jetpack供给了一些库,如ViewModel、LiveData和Navigation,可以协助开发者更好地办理运用程序的生命周期、状况和导航,使得构建大型运用程序愈加简略。

  2. 处理后台使命:Jetpack中的WorkManager库供给了一种简略、牢靠的办法来办理后台使命,如数据同步、推送告诉、文件上传等。

  3. 数据库拜访:Jetpack中的Room库供给了一个笼统层,可以让开发者方便地拜访和办理SQLite数据库,使得数据存储和拜访愈加简略。

  4. 呼应式编程:Jetpack中的LiveData和Data Binding库供给了呼应式编程的功用,可以让数据在数据源发生改动时主动更新UI,进步运用程序的功用和牢靠性。

  5. 代码重用:Jetpack中的各种库都旨在处理Android运用程序开发中的常见问题,并供给共同的API和开发体会,使得代码重用愈加简略。

2.3 Why: 为什么运用Jetpack?

以下是运用Jetpack的一些好处:

  1. 更好的代码安排和可保护性:Jetpack供给了一组库,这些库旨在处理Android运用程序开发中的常见问题,如生命周期办理、数据存储、后台使命处理等。运用Jetpack可以使代码愈加模块化,易于保护。
  2. 更好的代码可读性:Jetpack供给了共同的API和开发体会,可以使代码愈加易于了解和阅览。
  3. 更好的开发功率:Jetpack供给了一些库和东西,如Navigation和Data Binding,可以协助开发者更快地开发Android运用程序,进步开发功率。
  4. 更好的功用和牢靠性:Jetpack中的一些库,如LiveData和WorkManager,供给了呼应式编程和后台使命处理的功用,可以进步运用程序的功用和牢靠性。
  5. 更好的向后兼容性:Jetpack中的一些库,如ViewModel和LiveData,供给了对Android不同版别的向后兼容性支撑,可以使开发者更简略地编写适用于不同版别的Android体系的运用程序。 综上所述,Jetpack可以协助开发者更好地安排代码、进步开发功率、进步运用程序的功用和牢靠性,并供给了对不同版别的Android体系的向后兼容性支撑。

2.4 Who: 什么样的团队该运用Jetpack?

Jetpack适用于各种规划的Android开发团队,特别是那些期望进步运用程序质量、开发功率和可保护性的团队。以下是一些团队合适运用Jetpack的场景:

  1. 大型团队:Jetpack供给了共同的API和开发体会,可以使大型团队更简略协作开发,进步团队的开发功率和代码质量。
  2. 跨功用团队:Jetpack供给了一些库和东西,如Navigation和Data Binding,可以协助开发者更快地开发Android运用程序,使得跨功用团队之间更简略协作。
  3. 需求进步运用程序功用和牢靠性的团队:Jetpack中的一些库,如LiveData和WorkManager,供给了呼应式编程和后台使命处理的功用,可以进步运用程序的功用和牢靠性。
  4. 需求进步代码可保护性的团队:Jetpack供给了一些库,如ViewModel和Room,可以协助开发者更好地办理运用程序的状况和数据,使得代码更易于保护。
  5. 需求坚持向后兼容性的团队:Jetpack中的一些库,如ViewModel和LiveData,供给了对Android不同版别的向后兼容性支撑,可以使开发者更简略地编写适用于不同版别的Android体系的运用程序。 综上所述,Jetpack合适各种规划和类型的Android开发团队,特别是那些期望进步运用程序质量、开发功率和可保护性的团队。

2.5 How: 怎样运用Jetpack?

以下是运用Jetpack的一般过程:

  1. 添加Jetpack库:Jetpack库可以经过在build.gradle文件中添加依靠项的办法进行添加。例如,添加Lifecycle库的依靠项:

  dependencies {
      def lifecycle_version = "2.3.1"// ViewModel
      implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"// LiveData
      implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"// Lifecycle
      implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
  }

运用Jetpack库:在添加Jetpack库后,就可以在运用程序中运用Jetpack库供给的功用了。例如,运用ViewModel库创立一个ViewModel类:


   import androidx.lifecycle.ViewModel
   class MyViewModel : ViewModel() {
       // Add ViewModel logic here
   }

结合Jetpack组件运用:Jetpack库供给的组件可以结合运用,以进步运用程序的开发功率和可保护性。例如,运用ViewModel库和LiveData库完结一个呼应式的用户界面:


    class MyActivity : AppCompatActivity() {
        private lateinit var viewModel: MyViewModel
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_my)
            // Get a reference to the ViewModel
            viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
            // Observe a LiveData object in the ViewModel
            viewModel.someData.observe(this, Observer {
                // Update the UI with the new data
            })
        }
    }

以上是运用Jetpack的一般过程。需求根据详细的Jetpack库和运用程序需求进行相应的装备和代码完结。

2.6 How Much: 运用Jetpack事务价值

运用Jetpack可以带来以下事务价值:

  1. 进步开发功率:Jetpack供给了一些开发东西和库,例如ViewModel、LiveData和Room,可以削减重复的编写代码,简化开发流程,并进步开发功率。
  2. 进步运用程序质量:Jetpack供给了共同的API和开发体会,可以削减因为人为因素引起的代码过错,进步运用程序的质量。
  3. 进步运用程序功用:Jetpack中的一些库,例如Lifecycle和WorkManager,供给了呼应式编程和后台使命处理的功用,可以进步运用程序的功用和呼应速度。
  4. 简化运用程序架构:Jetpack供给了一些组件和库,例如ViewModel和Data Binding,可以协助开发者更好地办理运用程序的状况和数据,并简化运用程序的架构。
  5. 支撑向后兼容性:Jetpack中的一些库,例如ViewModel和LiveData,供给了对Android不同版别的向后兼容性支撑,可以使开发者更简略地编写适用于不同版别的Android体系的运用程序。

综上所述,运用Jetpack可以带来多种事务价值,可以进步运用程序的质量、功用和开发功率,一同简化运用程序架构和支撑向后兼容性,可以使运用程序更易于保护和晋级。

三、Jetpack四件套

3.1 LifeCycle

Android业务架构  基础篇  Jetpack四件套

3.1.1 LifeCycle根底界说

Android Jetpack Lifecycle是Android Jetpack组件库中的一部分,Lifecycle是根据Android Framework中的Lifecycle概念而构建的。

Lifecycle供给了一种轻松办理组件(如Activity和Fragment)生命周期的办法,一同也支撑自界说组件的生命周期。

Jetpack Lifecycle供给了一组类和接口,使得开发者可以在组件的生命周期各个阶段履行相应的操作。

这些类和接口包括:

  • LifecycleOwner: 具有生命周期的目标,通常是Activity和Fragment。

  • LifecycleObserver: 监听组件的生命周期事情的调查者目标。

  • Lifecycle: 组件的生命周期,包括CREATED、STARTED、RESUMED、PAUSED、STOPPED、DESTROYED等状况。

  • LiveData: 一个可调查的数据容器,可以在组件生命周期的不同阶段更新数据。

运用Jetpack Lifecycle,可以更简略地防止内存走漏和其他生命周期相关的问题。

例如,可以在组件被毁掉时主动开释资源、撤销网络恳求等操作。

此外,Jetpack Lifecycle还供给了一种办法来创立自界说的生命周期状况,以更好地满意App的需求。

总之,Jetpack Lifecycle是Android Jetpack组件库中的一个重要组件,可以协助开发者更轻松地办理组件的生命周期,然后进步App的质量和功用。

3.1.2 LifeCycle根底运用

在App的主Activity中完结一个简略的计时器,当Activity处于前台时,计时器会不断递加,当Activity被毁掉时,计时器将中止。

详细完结过程如下:

  1. 在gradle文件中添加Jetpack组件库的依靠。

dependencies {
    implementation "androidx.lifecycle:lifecycle-extensions:2.4.0"
}
  1. 创立一个名为Timer的Java类,并完结LifeCycleObserver接口。

public class Timer implements LifecycleObserver {
    private Handler handler;
    private int seconds = 0;
    @OnLifecycleEvent(Lifecycle.Event.ON_START)public void startTimer() {
        handler = new Handler();
        handler.post(new Runnable() {
            @Overridepublic void run() {
                Log.d("Timer", "Seconds: " + seconds);
                seconds++;
                handler.postDelayed(this, 1000);
            }
        });
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)public void stopTimer() {
        handler.removeCallbacksAndMessages(null);
        handler = null;
    }
}
  1. 在MainActivity中添加LifecycleOwner,并在onCreate办法中添加Observer。

public class MainActivity extends AppCompatActivity {
    @Overrideprotected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取LifecycleOwner目标LifecycleOwner lifecycleOwner = this;
        // 将Timer实例添加为Observer
        getLifecycle().addObserver(new Timer());
        // ...
    }
    // ...
}

这样,当Activity处于前台时,Timer实例中的startTimer办法会被调用,计时器会开端递加;

当Activity被毁掉时,Timer实例中的stopTimer办法会被调用,计时器会中止。

这个比如展现了怎么运用Jetpack LifeCycle组件来办理App组件的生命周期。

当App中存在需求在组件生命周期不同阶段履行的操作时,运用LifeCycle可以更方便地完结这些操作,一同防止了一些常见的生命周期问题。

3.1.3 LifeCycle优势下风

优势

  1. 办理生命周期方便

运用LifeCycle组件可以更方便地办理App组件的生命周期,防止了一些常见的生命周期问题,如内存走漏和空指针反常等。

  1. 模块化编程

运用LifeCycle组件可以将App的事务逻辑分解为模块化的组件,每个组件担任办理自己的生命周期,便于代码复用和保护。

  1. 规范化编程

运用LifeCycle组件可以规范化App的开发,使代码更易于阅览、了解和保护。

  1. 支撑多个组件

LifeCycle组件支撑多个组件进行生命周期办理,可以轻松地在多个组件之间同享状况和数据。

下风

  1. 需求承继LifecycleObserver:在完结LifeCycle功用的时分,需求承继LifecycleObserver接口,这会导致代码的承继联系稍微有点复杂。
  2. 需求添加注释:在运用LifeCycle组件的时分,需求添加一些注释来指示办法是在什么时分被调用,否则或许会呈现一些难以确诊的问题。

3.1.4 LifeCycle运用场景

Jetpack LifeCycle组件的实际开发运用场景包括:

  1. Activity和Fragment生命周期办理:运用LifeCycle组件可以更方便地办理Activity和Fragment的生命周期,防止了一些常见的生命周期问题,如内存走漏和空指针反常等。
  2. 后台服务办理:运用LifeCycle组件可以更方便地办理后台服务的生命周期,可以在App退出后主动中止后台服务,防止了一些不必要的资源糟蹋。
  3. 数据库衔接办理:运用LifeCycle组件可以更方便地办理数据库衔接的生命周期,可以在App退出时主动关闭数据库衔接,防止了一些不必要的资源糟蹋。
  4. 网络恳求办理:运用LifeCycle组件可以更方便地办理网络恳求的生命周期,可以在Activity或Fragment毁掉时主动撤销网络恳求,防止了一些不必要的网络恳求。
  5. 视图控制器办理:运用LifeCycle组件可以更方便地办理视图控制器的生命周期,可以在Activity或Fragment毁掉时主动铲除视图控制器的状况,防止了一些不必要的状况保存和康复操作。

3.1.5 LifeCycle原理剖析

类图

LifecycleOwner表明具有生命周期的组件,比如Activity和Fragment。

Lifecycle表明组件的生命周期,LifecycleObserver表明一个组件的生命周期调查者。

LifecycleRegistry是Lifecycle接口的一个完结类,它保护了一个生命周期状况机,用于记录组件的生命周期状况和生命周期事情。

LifecycleRegistry供给了一系列办法,用于办理组件的生命周期状况和生命周期事情。当组件的生命周期事情发生改动时,LifecycleRegistry会主动更新状况机,并告诉一切的LifecycleObserver调查者目标,以便它们可以相应地更新自己的状况。

LifecycleOwner可以经过getLifecycle()办法获取到一个Lifecycle目标,然后将自己的生命周期调查者目标添加到Lifecycle目标中,然后完结对组件生命周期的监听。

当组件的生命周期事情发生改动时,Lifecycle会主动告诉一切的生命周期调查者目标,以便它们可以相应地更新自己的状况。

Android业务架构  基础篇  Jetpack四件套

源码

Lifecycle库的中心是Lifecycle接口和LifecycleObserver接口。

Lifecycle接口界说了一组办法,用于将LifecycleOwner与LifecycleObserver进行相关。

public abstract class Lifecycle {
    //添加调查者
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);
    //移除调查者
    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);
    //获取当时状况
    public abstract State getCurrentState();
//生命周期事情,对应Activity生命周期办法
    public enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY  //可以呼应任意一个事情
    }
    //生命周期状况. (Event是进入这种状况的事情)
    public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;
        //判别至少是某一状况
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }

LifecycleObserver接口界说了一组回调办法,用于接收LifecycleOwner的生命周期事情。

在Lifecycle库的完结中,Lifecycle接口有两个重要的完结类,分别是LifecycleRegistry和LifecycleOwner。

LifecycleRegistry完结了Lifecycle接口,并供给了一组办法,用于办理LifecycleOwner的生命周期状况。

LifecycleOwner是一个接口,用于标识具有生命周期状况的目标,通常是Activity或Fragment。

//androidx.activity.ComponentActivity,这儿忽略了一些其他代码,咱们只看Lifecycle相关
public class ComponentActivity extends androidx.core.app.ComponentActivity implements LifecycleOwner{
    ...
    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    ...
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        ReportFragment.injectIfNeededIn(this); //运用ReportFragment分发生命周期事情
        if (mContentLayoutId != 0) {
            setContentView(mContentLayoutId);
        }
    }
    @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        Lifecycle lifecycle = getLifecycle();
        if (lifecycle instanceof LifecycleRegistry) {
            ((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
        }
        super.onSaveInstanceState(outState);
        mSavedStateRegistryController.performSave(outState);
    }
    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

在LifecycleRegistry中,有一个名为mObserverMap的成员变量,用于存储LifecycleObserver目标和其相关的EventObserver目标。

当LifecycleOwner的生命周期状况更改时,LifecycleRegistry会主动调用mObserverMap中与之相相关的EventObserver目标的相应办法,以便它们可以履行恰当的操作。

//LifecycleRegistry.java
   //体系自界说的保存Observer的map,可在遍历中增删
    private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap = new FastSafeIterableMap<>();
    public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);//获取event发生之后的将要处于的状况
        moveToState(next);//移动到这个状况
    }
    private void moveToState(State next) {
        if (mState == next) {
            return;//假如和当时状况共同,不处理
        }
        mState = next; //赋值新状况
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            return;
        }
        mHandlingEvent = true;
        sync(); //把生命周期状况同步给一切调查者
        mHandlingEvent = false;
    }
        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()) {  //isSynced()意思是 一切调查者都同步完了
            mNewEventOccurred = false;
            //mObserverMap便是 在activity中添加observer后 用于寄存observer的map
            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;
    }
    ...
     static State getStateAfter(Event event) {
        switch (event) {
            case ON_CREATE:
            case ON_STOP:
                return CREATED;
            case ON_START:
            case ON_PAUSE:
                return STARTED;
            case ON_RESUME:
                return RESUMED;
            case ON_DESTROY:
                return DESTROYED;
            case ON_ANY:
                break;
        }
        throw new IllegalArgumentException("Unexpected event value " + event);
    }

LifecycleRegistry还供给了一组办法,如handleLifecycleEvent()、getCurrentState()、addObserver()、removeObserver()等,用于办理组件的生命周期状况和LifecycleObserver目标。

在Lifecycle库的完结中,还有一些其他的类和接口,如GenericLifecycleObserver、FullLifecycleObserver、LifecycleEvent、EventObserver等,它们都是用于办理和处理组件生命周期事情的。

3.1.6 LifeCycle留意事项

3.1.6.1 不要在 onCreate() 办法中运用 Lifecycle 组件

Lifecycle 组件在 onCreate() 办法中没有初始化完结,因而在该办法中运用它们或许会导致崩溃或不可猜测的行为。建议在 onStart() 办法中运用 Lifecycle 组件。

3.1.6.2 不要手动调用 onDestroy() 办法

手动调用 onDestroy() 办法会损坏 Lifecycle 组件的生命周期,然后导致运用程序行为反常。Lifecycle 组件应该由体系主动办理,应该防止手动干涉。

3.1.6.3 防止在 Fragment 中运用多个 LifecycleOwner

Fragment 自身便是一个 LifecycleOwner,因而不该该在 Fragment 中创立其他的 LifecycleOwner。这样会导致多个 LifecycleOwner 之间的状况不同步,然后导致运用程序呈现问题。

3.2 LiveData

Android业务架构  基础篇  Jetpack四件套

3.2.1 LiveData根底界说

Android Jetpack LiveData是一种用于办理运用程序界面和数据交互的组件。

LiveData是一种可调查的数据持有者,用于在运用程序组件(如Activity、Fragment和Service)之间同享数据,并在数据发生更改时告诉调查者。

LiveData可以确保UI与数据的同步更新,防止了一些常见的过错,如内存走漏和UI组件无法正确更新的问题。

LiveData具有生命周期感知功用,可以主动感知运用程序组件的生命周期,并在组件处于活动状况时更新UI,而在组件处于非活动状况时中止更新,然后有用地削减了资源耗费。

LiveData还供给了线程安全的拜访数据的机制,防止了多线程并发拜访的问题。

3.2.2 LiveData根底运用

怎么 TextView 控件的显现内容呢?

首要,在 XML 布局文件中添加一个 TextView 控件:

<TextView
    android:id="@+id/text_view" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

然后,在 Activity 或 Fragment 中创立一个 LiveData 目标,用于更新 TextView 的显现内容:


public class MyActivity extends AppCompatActivity {
    private LiveData<String> mLiveData;
    private TextView mTextView;
    @Overrideprotected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.text_view);
        mLiveData = new MutableLiveData<>();
        mLiveData.observe(this, new Observer<String>() {
            @Overridepublic void onChanged(String s) {
                mTextView.setText(s);
            }
        });
    }
}

在上述代码中,咱们创立了一个 LiveData 目标,并将其与一个 TextView 控件相关。当 LiveData 目标中的值发生改动时,咱们运用 Observer 来监听这个改动,然后更新 TextView 的显现内容。

最终,咱们可以在代码中经过 setValue() 或 postValue() 办法来更新 LiveData 目标的值,然后更新 TextView 的显现内容。例如:


((MutableLiveData<String>) mLiveData)
.setValue("Hello, world!");

以上便是一个简略的 LiveData 事例,用于更新 TextView 控件的显现内容。当 LiveData 中的值发生改动时,TextView 控件会主动更新显现内容。

3.2.3 LiveData优势下风

优势

  1. 生命周期感知:LiveData 可以感知组件(如 Activity 或 Fragment)的生命周期,然后防止了因为 UI 组件的生命周期改动而引发的空指针反常和内存走漏等问题。

  2. 数据更新告诉:LiveData 可以在数据发生改动时主动告诉一切调查者更新数据,然后完结数据的实时更新和呼应。

  3. 数据共同性:LiveData 可以确保在装备更改时坚持数据的共同性,防止了数据丢掉和重复加载等问题。

  4. 线程安全:LiveData 可以确保在主线程中更新 UI 界面,并支撑在工作线程中进行异步操作,然后防止了多线程数据竞争问题。

  5. 与 ViewModel 结合运用:LiveData 可以与 ViewModel 结合运用,完结数据与 UI 界面的别离,然后进步了代码的可保护性和可测验性。

下风

  1. 适用场景有限:LiveData 适用于数据改动频频、需求实时更新的场景,关于数据改动较少的场景,运用 LiveData 或许会添加代码复杂性。
  2. API 限制:LiveData 是 Android Jetpack 组件,只能在支撑 Jetpack 的 Android 版别上运用,关于一些较老的 Android 版别或许需求运用其他技能方案。

3.2.4 LiveData运用场景

LiveData的运用场景包括但不限于以下几个方面:

  1. 数据库操作:LiveData可以与Room耐久化库结合运用,当数据库中的数据发生改动时,LiveData会主动告诉UI组件进行更新。
  2. 网络恳求:LiveData可以与Retrofit网络恳求库结合运用,当网络恳求的成果回来时,LiveData会主动告诉UI组件进行更新。
  3. 数据同享:LiveData可以在不同的组件之间同享数据,例如,当一个Activity和一个Fragment需求同享数据时,可以将LiveData目标设置为一个公共的数据持有者。
  4. 资源开释:LiveData可以在UI组件不再处于活动状况时主动开释资源,防止呈现内存走漏等问题。
  5. 代码简练:LiveData可以削减代码复杂度,经过数据调查者的办法,防止手动编写繁琐的数据更新代码。

3.2.5 LiveData原理剖析

LiveData是一种可以感知生命周期的数据持有者,它可以让数据更新时告诉UI界面进行更新,一同也可以防止因为生命周期问题带来的内存走漏。

类图

LiveData的简化类图:

Android业务架构  基础篇  Jetpack四件套

在上面的类图中,Observer是LiveData的调查者接口,LiveData是可调查数据的持有者类。LiveData具有生命周期感知能力,可以根据其生命周期状况主动办理数据的订阅和撤销订阅。MutableLiveData是LiveData的可变子类,允许更新LiveData持有的数据。LiveData的observe()办法用于注册调查者,setValue()和postValue()办法用于更新LiveData数据。

源码

LiveData的中心代码在androidx.lifecycle.LiveData类中,下面临LiveData的源码进行简要剖析:

  1. LiveData的基本结构

LiveData类是一个笼统类,它有一个泛型类型T,表明LiveData中存储的数据类型。LiveData类内部保护了一个数据源(mData)和一个调查者列表(mObservers),当LiveData中的数据发生改动时,会告诉一切注册的调查者进行UI更新。


public abstract class LiveData<T> {
    private static final Object NOT_SET = new Object();
    private Object mData;
    private boolean mDispatchingValue;
    private int mActiveCount;
    private volatile Object mPendingData = NOT_SET;
    private volatile Object mVersion = new Object();
    private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
    private final SafeIterableMap<Observer<T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
    // ...
}
  1. 调查者注册

LiveData中的调查者是经过observe()办法进行注册的,这个办法承受一个LifecycleOwner目标和一个Observer目标。LifecycleOwner是一个具有生命周期的目标,当LifecycleOwner的生命周期结束时,LiveData会主动解注册一切与该LifecycleOwner相关的调查者,防止内存走漏。


public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignorereturn;
    }
    // ...LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // ...
}
  1. 调查者解注册

解注册则是经过removeObserver()办法进行的,该办法承受一个Observer目标,用于从调查者列表中删去相应的调查者。


public void removeObserver(@NonNull Observer<? super T> observer) {
    assertMainThread("removeObserver");
    // ...mObservers.remove(observerWrapper);
    // ...
}
  1. 数据更新和告诉调查者

LiveData中的数据更新是经过setValue()和postValue()办法进行的。setValue()办法是在主线程中进行调用的,它会直接更新LiveData中的数据并告诉一切的调查者进行UI更新;而postValue()办法是在异步线程中进行调用的,它会将要更新的数据封装成PendingPost目标,并提交给主线程的Handler进行处理。


protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}
protected void postValue(T value) {
    if (Looper.myLooper() != Looper.getMainLooper()) {
        // ...
        return;
    }
    mPendingData = value;
    if (mPendingData == NOT_SET) {
        // ignore
        return;
    }
    // ...
    mMainThreadExecutor.execute(mPostValueRunnable);
}

当LiveData中的数据发生更新时,LiveData会告诉一切的调查者进行UI更新。LiveData运用了模板办法

规划形式中的调查者形式,它将数据源和调查者进行了解耦。LiveData类中的dispatchingValue()办法便是告诉调查者进行UI更新的中心办法。


private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions();
            while (iterator.hasNext()) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

dispatchingValue()办法中运用了一个迭代器遍历一切的调查者,然后调用considerNotify()办法进行UI更新。


private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

considerNotify()办法首要判别调查者是否处于活跃状况,假如不是则直接回来;接着判别调查者是否应该处于活跃状况,假如不是则调用activeStateChanged()办法将调查者状况更新为非活跃状况;最终判别数据版别号是否发生改动,假如发生改动则调用调查者的onChanged()办法进行UI更新。

  1. 其他办法

除了以上中心办法之外,LiveData还供给了其他办法,例如getValue()办法用于获取LiveData中存储的数据;hasActiveObservers()办法用于判别是否存在处于活跃状况的调查者等等。


public T getValue() {
    Object data = mData;
    if (data != NOT_SET) {
        return (T) data;
    }
    return null;
}
public boolean hasActiveObservers() {
    return mActiveCount > 0;
}

以上是对LiveData源码的简要剖析,LiveData运用了调查者形式,可以感知生命周期并进行UI更新,防止了因为生命周期问题带来的内存走漏。

3.2.6 LiveData留意事项

数据倒灌

要处理LiveData数据倒灌问题,可以运用以下办法:

运用MediatorLiveData代替LiveData:MediatorLiveData可以作为一个中间层,将多个LiveData目标的数据源兼并,然后防止数据倒灌问题。在UI组件的生命周期结束时,可以调用MediatorLiveData的removeSource()办法,将LiveData的数据源从MediatorLiveData中移除。

在ViewModel中运用LiveData:将LiveData目标作为ViewModel中的成员变量,并在ViewModel中进行数据更新和调查,可以防止LiveData数据倒灌问题。

在UI组件中运用自界说Observer:可以在自界说Observer中进行生命周期判别,当UI组件的生命周期现已结束时,不再更新UI界面。

下面是运用自界说Observer处理LiveData数据倒灌问题的示例代码:


    public class MyObserver<T> implements Observer<T> {
        private boolean mIsStarted = false;
        private Observer<T> mObserver;
        public MyObserver(Observer<T> observer) {
            mObserver = observer;
        }
        public void start() {
            mIsStarted = true;
        }
        public void stop() {
            mIsStarted = false;
        }
        @Overridepublic void onChanged(T t) {
            if (mIsStarted) {
                mObserver.onChanged(t);
            }
        }
    }

在UI组件中运用自界说Observer时,需求在UI组件的生命周期开端和结束时,调用MyObserver的start()和stop()办法,然后控制数据更新的时机,防止LiveData数据倒灌问题。


    public class MyActivity extends AppCompatActivity {
        private LiveData<Integer> mLiveData;
        private MyObserver<Integer> mObserver;
        @Overrideprotected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mLiveData = new MutableLiveData<>();
            mObserver = new MyObserver<>(integer -> {
                // 更新UI界面
            });
            mLiveData.observe(this, mObserver);
        }
        @Overrideprotected void onStart() {
            super.onStart();
            mObserver.start();
        }
        @Overrideprotected void onStop() {
            super.onStop();
            mObserver.stop();
        }
    }

以上是处理LiveData数据倒灌问题的一些办法,也可以经过hook修改livedata源码observer.mLastVersion的值,使得if (observer.mLastVersion >= mVersion)成立,就不会导致没有注册调查者,还能接收到音讯

Android业务架构  基础篇  Jetpack四件套

setValue 不起效

调用LiveData的setValue()办法时,假如LiveData的调查者处于激活状况,那么LiveData会将最新的数据推送给调查者,并在调查者的回调办法中更新UI界面。可是,假如LiveData的调查者处于非激活状况,那么LiveData不会将数据推送给调查者,也不会更新UI界面。因而,假如调用setValue()办法后,UI界面没有发生更新,或许是因为LiveData的调查者处于非激活状况。

LiveData的调查者处于非激活状况的原因或许有以下几种:

  1. 调查者没有与LiveData建立衔接:在调用LiveData的observe()办法时,需求将调查者与LiveData建立衔接,只有建立衔接后,LiveData才能将数据推送给调查者。假如没有建立衔接,那么即便调用了setValue()办法,也无法更新UI界面。
  2. 调查者的生命周期现已结束:LiveData的调查者必须在其生命周期的活动状况中才能接收到数据更新。假如调查者的生命周期现已结束,那么即便调用了setValue()办法,也无法更新UI界面。
  3. 调查者处于非激活状况:LiveData的调查者在其生命周期的激活状况中才能接收到数据更新。假如调查者处于非激活状况,那么即便调用了setValue()办法,也无法更新UI界面。

因而,假如调用setValue()办法后,UI界面没有发生更新,可以检查调查者的衔接状况和生命周期状况,确保LiveData的调查者处于激活状况,而且现已与LiveData建立衔接。假如仍然无法处理问题,可以考虑运用postValue()办法,该办法可以在UI线程空闲时更新LiveData的数据,并在调查者处于激活状况时告诉调查者进行UI更新。

内存走漏

LiveData的调查者(Observer)默许是弱引证,可是假如调查者没有及时撤销调查,或许会导致内存走漏。

屡次调查

假如一个LiveData目标被多个调查者一同调查,那么每个调查者都会收到相同的数据更新,或许会导致UI界面屡次更新,造成功用问题。

生命周期不共同

LiveData是与生命周期相相关的,假如UI组件的生命周期结束了,可是LiveData仍在发送数据更新,那么就会引发反常。

线程安全

LiveData默许在主线程进行数据更新,假如需求在后台线程进行数据更新,就需求运用LiveData的postValue()办法,而不是setValue()办法。

防止重复调查

假如在一个UI组件中屡次调查同一个LiveData目标,或许会导致重复调查,造成功用问题和数据不共同问题。因而,可以运用ViewModel中的getLiveData()办法,确保同一个LiveData目标只被调查一次。

3.2.7 EventBus vs Otto vs RxJava vs LiveData

Android业务架构  基础篇  Jetpack四件套

3.3 ViewModel

Android业务架构  基础篇  Jetpack四件套

3.3.1 ViewModel根底界说

Android Jetpack ViewModel 是一种用于办理 UI 相关数据的组件,它可以在屏幕旋转等装备更改时保存数据偏从头创立 Activity 或 Fragment。ViewModel 是一个以生命周期感知的办法来保存和办理 UI 相关数据的类。

ViewModel 是一个专门用于存储和办理与 UI 相关的数据的类,它们可以在 Activity 或 Fragment 从头创立时坚持数据的完整性,而且可以防止因为 Activity 或 Fragment 生命周期的改动而导致的数据丢掉。ViewModel 还供给了一种在 Activity 和 Fragment 之间同享数据的机制。

在 ViewModel 中存储的数据是不受 Activity 或 Fragment 生命周期的影响的,这意味着在旋转屏幕或许装备更改时,ViewModel 中的数据将不会被铲除,可以坚持完整性,然后可以在 Activity 或 Fragment 从头创立时继续运用。

ViewModel 通常与 LiveData 或 RxJava 等呼应式编程库一同运用,以完结数据的实时更新和呼应。

3.3.2 ViewModel根底运用

如安在 ViewModel 中保存和办理数据,并在 Activity 或 Fragment 从头创立时坚持数据的完整性。

  1. 创立一个名为MyViewModel的ViewModel类


public class MyViewModel extends ViewModel {
    private MutableLiveData<Integer> count = new MutableLiveData<>();
    public void setCount(int count) {
        this.count.setValue(count);
    }
    public LiveData<Integer> getCount() {
        return count;
    }
}

在这个 ViewModel 类中,咱们创立了一个 MutableLiveData 类型的 count 变量,并在 setCount() 办法中设置它的值,一同在 getCount() 办法中将其作为 LiveData 回来,以完结数据的实时更新和呼应。

  1. 在 Activity或Fragment中运用ViewModel


public class MyActivity extends AppCompatActivity {
    private MyViewModel viewModel;
    @Overrideprotected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        viewModel.getCount().observe(this, count -> {
            // 更新 UI
        });
    }
    public void onButtonClicked(View view) {
        int count = viewModel.getCount().getValue() + 1;
        viewModel.setCount(count);
    }
}

在这个 Activity 中,咱们运用 ViewModelProviders.of() 办法获取 MyViewModel 的实例,并将其与该 Activity 绑定。

然后,咱们可以运用 getCount() 办法获取 count 变量的值,并在 setCount() 办法中设置其值。

经过调用 getCount().observe() 办法,咱们可以在 LiveData 数据改动时更新 UI。

这个事例演示了怎么运用 Android Jetpack ViewModel 来办理 UI 相关的数据,并在 Activity 或 Fragment 从头创立时坚持数据的完整性。

运用 ViewModel 可以协助咱们更好地办理和坚持与 UI 相关的数据,并进步运用程序的安稳性和牢靠性。

3.3.3 ViewModel优势下风

优势

  1. 坚持数据的完整性:ViewModel 可以在屏幕旋转等装备更改时保存数据偏从头创立 Activity 或 Fragment,然后坚持数据的完整性,防止因为生命周期改动而导致的数据丢掉。
  2. 简化代码:运用 ViewModel 可以将 UI 相关的数据与 UI 控件别离,防止了在 Activity 或 Fragment 中处理数据逻辑的繁琐代码,使代码愈加明晰、简练。
  3. 支撑数据同享:ViewModel不会跟着Activity的屏幕旋转而毁掉,削减了保护状况的代码本钱(数据的存储和读取、序列化和反序列化);
  4. 支撑呼应式编程:ViewModel 可以与 LiveData 或 RxJava 等呼应式编程库一同运用,完结数据的实时更新和呼应。

下风

  1. 不合适处理长时间运行的使命:ViewModel 主要用于办理 UI 相关的数据,假如需求处理长时间运行的使命,需求在 ViewModel 中运用异步使命或许独自运用 Service。
  2. 数据耐久化需求额定处理:ViewModel 中保存的数据仅仅暂时性的,假如需求长时间保存数据,需求将数据保存到数据库或许 SharedPreferences 中。
  3. 需求了解生命周期:ViewModel 是以生命周期感知的办法来保存和办理数据的,因而需求了解 Activity 或 Fragment 的生命周期以便正确运用 ViewModel。

3.3.4 ViewModel运用场景

下面是一些实际开发过程中或许运用 ViewModel 的运用场景:

  1. 数据耐久化:假如需求在运用程序的不同页面之间同享数据,而且期望在 Activity 或 Fragment 从头创立时坚持数据的完整性,可以运用 ViewModel 来保存和办理数据。

  2. 处理屏幕旋转等装备更改:当运用程序的屏幕旋转或其他装备更改时,Activity 或 Fragment 或许会被毁掉偏从头创立,此刻可以运用 ViewModel 来保存和康复数据,防止因为生命周期改动而导致的数据丢掉。

  3. 别离 UI 与数据:将 UI 相关的数据与 UI 控件别离,防止在 Activity 或 Fragment 中处理数据逻辑的繁琐代码,使代码愈加明晰、简练。

  4. 完结呼应式编程:ViewModel 可以与 LiveData 或 RxJava 等呼应式编程库一同运用,完结数据的实时更新和呼应,然后进步运用程序的功用和用户体会。

  5. 防止内存走漏:将数据存储在 ViewModel 中可以防止因为对 Activity 或 Fragment 的引证而导致的内存走漏,然后进步了运用程序的安稳性和牢靠性。

3.3.5 ViewModel原理剖析

类图

  • ViewModel类是一个笼统类,包括了一个onCleared()办法,该办法会在ViewModel不再被运用时被调用,用于开释资源和铲除状况。

  • AndroidViewModel是ViewModel的一个子类,它包括一个Application目标,用于在ViewModel中拜访运用程序的上下文。

  • ViewModelProvider是一个协助类,用于获取ViewModel实例。

  • ViewModelProvider.Factory是一个接口,用于创立ViewModel实例。

Android业务架构  基础篇  Jetpack四件套

原理

Jetpack ViewModel 源码坐落 androidx.lifecycle.ViewModel 包中,它包括了两个类:ViewModel 和 ViewModelProvider。

每个Activity都会绑定一个ViewModelStore,ViewModelStore经过HashMap保存ViewModel和String称号。ViewModel的耐久化依靠ViewModelStore的存储和获取。

ViewModel 类的主要效果是界说一个用于存储 UI 组件数据的容器,并在 UI 组件生命周期改动时进行办理。详细来说,ViewModel 类承继了 Android 的 ViewModel 类,并添加了一些额定的办法,用于完结以下功用:

  1. 缓存 UI 组件数据
  2. 在 UI 组件毁掉后整理数据
  3. 在 UI 组件重建时康复数据

ViewModelProvider 类的主要效果是创立 ViewModel 实例,并为其供给一个仅有的 key,用于在 Activity 或 Fragment 重建时找回现已存在的 ViewModel 实例。ViewModelProvider 类的完结办法比较简略,主要是经过一个 HashMap 来存储 ViewModel 实例,并供给了一些办法,用于创立和查找 ViewModel 实例。

下面是 ViewModel 类的源码剖析:


open class ViewModel : ViewModelStoreOwner {
    private val mViewModelStore = ViewModelStore()
    @CallSuperoverride fun onCleared() {
        mViewModelStore.clear()
    }
    fun getViewModelStore(): ViewModelStore {
        return mViewModelStore
    }
}

ViewModel 类完结了 ViewModelStoreOwner 接口,并在内部保护了一个 ViewModelStore 目标,用于存储 UI 组件数据。在 ViewModel 被毁掉时,会调用 onCleared() 办法来清空 ViewModelStore 中的数据。getViewModelStore() 办法用于回来 ViewModelStore 目标。

ViewModelStoreOwner 接口的完结代码如下:


interface ViewModelStoreOwner {
    fun getViewModelStore(): ViewModelStore
}

ViewModelProvider 类的源码剖析如下:


class ViewModelProvider private constructor(
        private val mFactory: Factory,
        private val mViewModelStore: ViewModelStore
) {
    // 缓存 ViewModel 实例的 HashMapprivate val mViewModels = HashMap<String, ViewModel>()
    companion object {
        private val sCache = HashMap<String, ViewModelProvider>()
        /**
         * 回来当时 Activity 或 Fragment 的 ViewModelProvider 实例
         */@MainThreadfun of(owner: ViewModelStoreOwner): ViewModelProvider {
            return of(owner, FactoryHolder.DEFAULT_FACTORY)
        }
        /**
         * 回来当时 Activity 或 Fragment 的 ViewModelProvider 实例,可指定 Factory
         */@MainThreadfun of(owner: ViewModelStoreOwner, factory: Factory): ViewModelProvider {
            val store = owner.getViewModelStore()
            // 先从缓存中查找 ViewModelProvider 实例var viewModelProvider = sCache[store.mKey]
            if (viewModelProvider == null) {
                viewModelProvider = ViewModelProvider(factory, store)
                sCache[store.mKey] = viewModelProvider
            }
            return viewModelProvider
        }
    }
    /**
     * 回来指定 key 的 ViewModel 实例,假如不存在则经过 Factory
     */
fun <T : ViewModel?> get(key: String, modelClass: Class<T>): T {
    var viewModel = mViewModels[key]
    if (modelClass.isInstance(viewModel)) {
        @Suppress("UNCHECKED_CAST")
        return viewModel as T
    }
    // 创立新的 ViewModel 实例,并将其添加到缓存中
    viewModel = mFactory.create(modelClass)
    mViewModels[key] = viewModel
    return viewModel
}
/**
 * Factory 接口,用于创立 ViewModel 实例
 */
interface Factory {
    fun <T : ViewModel?> create(modelClass: Class<T>): T
}
/**
 * FactoryHolder 类,用于供给默许的 Factory 完结
 */
private object FactoryHolder {
    val DEFAULT_FACTORY: Factory = object : Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            try {
                return modelClass.newInstance()
            } catch (e: IllegalAccessException) {
                throw RuntimeException(e)
            } catch (e: InstantiationException) {
                throw RuntimeException(e)
            }
        }
    }
 }
}

ViewModelProvider 类包括了一个 HashMap,用于缓存 ViewModel 实例。

它的 of() 办法用于创立 ViewModelProvider 实例,并经过 ViewModelStore 目标来区别不同的 Activity 或 Fragment。get() 办法用于回来指定 key 的 ViewModel 实例,并在缓存中查找是否存在对应的实例。假如缓存中不存在该实例,则调用 Factory 接口来创立一个新的 ViewModel 实例,并将其添加到缓存中。

Factory 接口供给了一个 create() 办法,用于创立 ViewModel 实例。FactoryHolder 类用于供给默许的 Factory 完结,它运用 Java 的反射机制来创立 ViewModel 实例。 最终,需求留意的是,ViewModelStore 和 ViewModelProvider 都是线程安全的,可以在多个线程中一同运用。

这意味着在多线程环境下,可以运用 ViewModel 来缓存和同享数据,然后削减数据的重复加载和提升运用程序的功用。

3.3.6 ViewModel留意事项

运用了过错的 ViewModel:

在某些状况下,或许会需求多个 ViewModel 来办理不同的 UI 数据。假如运用了过错的 ViewModel 来办理数据,或许会导致数据丢掉或逻辑过错。因而,在运用 ViewModel 时应该清楚每个 ViewModel 的效果,防止呈现混杂。

ViewModel 与生命周期的联系

ViewModel 的生命周期不同于 Activity 和 Fragment,它是被体系缓存的,因而或许会呈现数据被铲除的状况。在运用 ViewModel 时应该留意它的生命周期,及时保存数据并康复数据。

运用无效的上下文

ViewModel 需求一个有用的上下文来创立实例,假如运用无效的上下文或许会导致 ViewModel 创立失败。因而,在运用 ViewModel 时应该留意上下文的有用性,防止呈现创立失败的状况。

运用了过错的效果域

ViewModel 的效果域应该与需求办理的 UI 组件的生命周期相同,假如运用了过错的效果域,或许会导致数据被铲除或许生命周期不共同。因而,在运用 ViewModel 时应该选择正确的效果域,防止呈现问题。

3.4 DataBinding

Android业务架构  基础篇  Jetpack四件套

3.4.1 DataBinding根底界说

DataBinding供给了一种声明性的办法将布局文件中的UI组件和运用程序的数据模型绑定在一同。

经过DataBinding,开发者可以将UI组件的值绑定到数据模型中的特点,使得在更新UI时不需求手动更新每个组件的值。

DataBinding库经过生成一个绑定类来完结UI和数据模型之间的绑定。

这个绑定类是在编译时主动生成的,它运用了数据模型和UI组件之间的绑定表达式,以便在运行时履行数据绑定。

3.4.2 DataBinding根底运用

技能需求

运用DataBinding完结一个简略的计数器功用。在每次更新计数器的值时,DataBinding会主动更新TextView的值,使得开发者不需求手动更新UI组件的值,可以愈加专心于事务逻辑的完结

XML

<?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">
    <data>
        <variable
            name="counter"
            type="Integer" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tvCounter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{counter.toString()}" />
        <Button
            android:id="@+id/btnIncrease"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+" />
        <Button
            android:id="@+id/btnDecrease"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="-" />
    </LinearLayout>
</layout>

Code

创立DataBinding实例,并将activity_main.xml布局文件与DataBinding实例绑定。然后,将计数器的值赋给DataBinding实例的counter变量,并为添加和削减按钮设置点击事情。在点击事情中,更新计数器的值,并将新的值赋给DataBinding实例的counter变量。最终,调用DataBinding实例的executePendingBindings()办法,将UI组件的值更新到最新的值

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private var counter = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 创立DataBinding实例并与布局文件绑定
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // 设置初始值
        binding.counter = counter
        // 为按钮设置点击事情
        binding.btnIncrease.setOnClickListener {
            counter++
            binding.counter = counter
            binding.executePendingBindings()
        }
        binding.btnDecrease.setOnClickListener {
            counter--
            binding.counter = counter
            binding.executePendingBindings()
        }
    }
}

3.4.3 DataBinding优势下风

优势

  1. 简化代码

    运用DataBinding可以削减findViewById()办法的调用,使得代码愈加简练和易读。开发者可以经过DataBinding直接拜访布局文件中的UI组件,而不需求手动查找和设置UI组件的特点。

  2. 双向绑定

    DataBinding支撑双向绑定,可以将UI组件的值绑定到数据模型中的特点,也可以将数据模型的特点绑定到UI组件的值。这种双向绑定可以协助开发者愈加方便地完结UI和数据模型之间的同步。

  3. 削减过错

    DataBinding可以削减代码中的过错,因为运用DataBinding可以消除某些手动编写的代码。例如,DataBinding可以主动生成一些代码,协助开发者绑定UI组件和数据模型,削减手动编写代码的机会。

  4. 功用优化

    DataBinding可以带来一些功用优化,例如经过生成绑定类来削减代码的履行时间。DataBinding还支撑懒加载,可以在需求时才加载数据模型和UI组件。

下风

  1. 兼容性问题

    DataBinding库需求运用较新的Android Gradle插件和Android SDK版别,这或许导致一些兼容性问题。假如开发者运用较老的Android版别,或许需求进行一些兼容性处理。

  2. 构建时间添加

    运用DataBinding或许会添加运用程序的构建时间,因为DataBinding库需求生成绑定类。在编译时需求别离和处理xml文件,添加了编译时间。

  3. 难以调试

    因为DataBinding库运用了代码生成,因而在调试时或许会呈现一些困难。例如,开发者或许会发现在运用DataBinding时,调试器或许会越过某些代码行,或许某些变量的值或许无法正确显现。

  4. 增大包的体积

    占用大量内存,每个可观测数据目标都会对应一个监听器WeakListener目标。

3.4.4 DataBinding运用场景

  1. MVVM架构:DataBinding可以协助开发者完结MVVM架构中的视图模型(ViewModel)和视图(View)之间的绑定。经过DataBinding,开发者可以将视图和数据模型别离,进步代码的可读性和可保护性。
  2. 布局优化:DataBinding可以协助开发者完结布局优化,例如经过运用绑定表达式和条件句子来控制UI组件的可见性。经过布局优化,可以使得运用程序的UI愈加灵敏和可定制。
  3. 多语言支撑:DataBinding可以协助开发者完结多语言支撑,例如经过运用绑定表达式来动态设置UI组件的文本。经过多语言支撑,可以使得运用程序的用户界面愈加友好和易用。
  4. 动态主题:DataBinding可以协助开发者完结动态主题,例如经过运用绑定表达式来动态设置UI组件的色彩和样式。经过动态主题,可以使得运用程序的UI愈加漂亮和精美。

3.4.5 DataBinding原理剖析

类图

Android业务架构  基础篇  Jetpack四件套

源码

DataBinding的原理主要包括以下几个方面:

  1. 布局文件的解析和生成:在DataBinding中,布局文件会被解析并生成对应的ViewDataBinding类。这个类包括了布局文件中界说的一切UI组件的引证,以及绑定到这些UI组件的数据目标。
  2. 数据目标的绑定:DataBinding会在编译时生成代码来完结UI组件和数据目标的绑定。生成的代码会被包括在BR类中。这个类包括了运用程序中一切用于绑定的变量的引证。当数据目标发生改动时,DataBinding会主动更新UI组件的值。
  3. 调查者形式的运用:DataBinding运用调查者形式来坚持UI和数据之间的同步。当数据目标发生改动时,DataBinding会主动更新UI组件的值。在DataBinding中,数据目标是被调查的目标,而UI组件是调查者。
  4. 双向绑定的完结:DataBinding支撑双向绑定,即当UI组件的值发生改动时,DataBinding会主动更新数据目标的值。这是经过运用双向绑定适配器来完结的。适配器会在UI组件的值发生改动时主动更新数据目标的值。
  5. 特点转换器的运用:DataBinding还支撑特点转换器,可以用来将数据目标中的值转换为UI组件可以显现的值。例如,可以运用特点转换器将日期格局化为特定的格局。
  6. 数据绑定表达式的运用:DataBinding还支撑数据绑定表达式,可以用来在布局文件中动态计算UI组件的值。例如,可以运用数据绑定表达式将两个文本框中的值相加并将成果显现在第三个文本框中。

总的来说,Android Jetpack DataBinding的原理是根据数据绑定和调查者形式的。它经过生成代码来完结UI组件和数据目标之间的绑定,并运用调查者形式来坚持UI和数据之间的同步。一同,DataBinding还支撑双向绑定、特点转换器和数据绑定表达式等特性,使得它可以满意愈加复杂的UI和数据交互需求。

3.4.6 DataBinding留意事项

  1. 过错:Cannot find the setter for attribute ‘XXX’ with parameter type XXX。这个过错通常是因为绑定表达式中的变量类型不正确或布局文件中的特点称号不正确导致的。可以检查变量类型和特点称号是否正确。
  2. 过错:Could not find accessor XXX。这个过错通常是因为布局文件中运用了不存在的变量或办法导致的。可以检查布局文件中运用的变量和办法是否存在。
  3. 过错:Variable XXX has incompatible type XXX。这个过错通常是因为DataBinding的变量类型和布局文件中的变量类型不共同导致的。可以检查DataBinding代码中变量的类型和布局文件中变量的类型是否共同。
  4. 过错:DataBinding不支撑lambda表达式。假如运用lambda表达式,编译时会呈现过错。可以运用办法引证或匿名内部类来替代lambda表达式。
  5. 功用问题:在运用数据绑定表达式时,要留意表达式的复杂度。复杂的表达式或许会影响运用程序的功用。可以尝试将复杂的表达式分解成更简略的表达式,以进步运用程序的功用。
  6. 混杂问题:假如运用程序运用了代码混杂,那么需求将DataBinding的类排除在混杂范围之外,否则会导致编译过错或运行时反常。
  7. ViewStub问题:在运用DataBinding的布局文件中,不能包括ViewStub,否则会导致编译过错。

3.4.7 ButterKnife VS ViewBinding VS RoboBinding VS DataBinding

Android业务架构  基础篇  Jetpack四件套

四、总结与展望

《Android事务架构 根底篇 Jetpack四件套》一文首要经过4W2H全方位的讲解了Jepack对Android事务开发的价值,然后经过根底界说、根底运用、优下风剖析、运用场景、原理剖析、留意事项等多维度剖析了Jetpack四件套。

在咱们工作中,可保护、可扩展、可测验和可重用的事务架构关于进步运用程序的质量和功率含义非凡,而JetPack是协助开发者快速地安排和办理运用程序的代码的东西包。

这也是小木箱强烈建议咱们学习Jetpack很重要的原因。期望经过这篇文章可以让你意识到Jetpack对事务开发的重要性。

进步篇将介绍MVC、MVP、MVVM和MVI四剑客,同样是Android事务架构中心内容。 今日就到这儿啦,我是 小木箱,咱们下一篇见~