“【小木箱成长营】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 是一组 Android 软件组件、工具和指南,它们可以帮助开发者构建高质量、稳定的 Android 应用程序。Jetpack 中包含多个库,它们旨在解决 Android 应用程序开发中的常见问题,并提供一致的 API 和开发体验。
Jetpack 中包含的库包括:
Jetpack 适用于开发各种类型的 Android 应用程序,包括单页面应用程序、多页面应用程序、后台任务应用程序等。下面是一些适合使用 Jetpack 的场景:
构建大型应用程序:Jetpack 提供了一些库,如 ViewModel、LiveData 和 Navigation,可以帮助开发者更好地管理应用程序的生命周期、状态和导航,使得构建大型应用程序更加容易。
处理后台任务:Jetpack 中的 WorkManager 库提供了一种简单、可靠的方式来管理后台任务,如数据同步、推送通知、文件上传等。
数据库访问:Jetpack 中的 Room 库提供了一个抽象层,可以让开发者方便地访问和管理 SQLite 数据库,使得数据存储和访问更加容易。
响应式编程:Jetpack 中的 LiveData 和 Data Binding 库提供了响应式编程的功能,可以让数据在数据源发生变化时自动更新 UI,提高应用程序的性能和可靠性。
代码重用:Jetpack 中的各种库都旨在解决 Android 应用程序开发中的常见问题,并提供一致的 API 和开发体验,使得代码重用更加容易。
以下是使用 Jetpack 的一些好处:
Jetpack 适用于各种规模的 Android 开发团队,特别是那些希望提高应用程序质量、开发效率和可维护性的团队。以下是一些团队适合使用 Jetpack 的场景:
以下是使用 Jetpack 的一般步骤:
添加 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 库和应用程序需求进行相应的配置和代码实现。
使用 Jetpack 可以带来以下业务价值:
综上所述,使用 Jetpack 可以带来多种业务价值,可以提高应用程序的质量、性能和开发效率,同时简化应用程序架构和支持向后兼容性,可以使应用程序更易于维护和升级。
Android Jetpack Lifecycle 是 Android Jetpack 组件库中的一部分,Lifecycle 是基于 Android Framework 中的 Lifecycle 概念而构建的。
Lifecycle 提供了一种轻松管理组件(如 Activity 和 Fragment)生命周期的方式,同时也支持自定义组件的生命周期。
Jetpack Lifecycle 提供了一组类和接口,使得开发者可以在组件的生命周期各个阶段执行相应的操作。
这些类和接口包括:
使用 Jetpack Lifecycle,可以更容易地避免内存泄漏和其他生命周期相关的问题。
例如,可以在组件被销毁时自动释放资源、取消网络请求等操作。
此外,Jetpack Lifecycle 还提供了一种方式来创建自定义的生命周期状态,以更好地满足 App 的需求。
总之,Jetpack Lifecycle 是 Android Jetpack 组件库中的一个重要组件,可以帮助开发者更轻松地管理组件的生命周期,从而提高 App 的质量和性能。
在 App 的主 Activity 中实现一个简单的计时器,当 Activity 处于前台时,计时器会不断递增,当 Activity 被销毁时,计时器将停止。
具体实现步骤如下:
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.4.0"
}
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;
}
}
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 可以更方便地实现这些操作,同时避免了一些常见的生命周期问题。
使用 LifeCycle 组件可以更方便地管理 App 组件的生命周期,避免了一些常见的生命周期问题,如内存泄漏和空指针异常等。
使用 LifeCycle 组件可以将 App 的业务逻辑分解为模块化的组件,每个组件负责管理自己的生命周期,便于代码复用和维护。
使用 LifeCycle 组件可以规范化 App 的开发,使代码更易于阅读、理解和维护。
LifeCycle 组件支持多个组件进行生命周期管理,可以轻松地在多个组件之间共享状态和数据。
Jetpack LifeCycle 组件的实际开发应用场景包括:
LifecycleOwner 表示拥有生命周期的组件,比如 Activity 和 Fragment。
Lifecycle 表示组件的生命周期,LifecycleObserver 表示一个组件的生命周期观察者。
LifecycleRegistry 是 Lifecycle 接口的一个实现类,它维护了一个生命周期状态机,用于记录组件的生命周期状态和生命周期事件。
LifecycleRegistry 提供了一系列方法,用于管理组件的生命周期状态和生命周期事件。当组件的生命周期事件发生变化时,LifecycleRegistry 会自动更新状态机,并通知所有的 LifecycleObserver 观察者对象,以便它们可以相应地更新自己的状态。
LifecycleOwner 可以通过 getLifecycle()方法获取到一个 Lifecycle 对象,然后将自己的生命周期观察者对象添加到 Lifecycle 对象中,从而实现对组件生命周期的监听。
当组件的生命周期事件发生变化时,Lifecycle 会自动通知所有的生命周期观察者对象,以便它们可以相应地更新自己的状态。
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 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 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 等,它们都是用于管理和处理组件生命周期事件的。
Lifecycle 组件在 onCreate() 方法中尚未初始化完成,因此在该方法中使用它们可能会导致崩溃或不可预测的行为。建议在 onStart() 方法中使用 Lifecycle 组件。
手动调用 onDestroy() 方法会破坏 Lifecycle 组件的生命周期,从而导致应用程序行为异常。Lifecycle 组件应该由系统自动管理,应该避免手动干预。
Fragment 自身就是一个 LifecycleOwner,因此不应该在 Fragment 中创建其他的 LifecycleOwner。这样会导致多个 LifecycleOwner 之间的状态不同步,从而导致应用程序出现问题。
Android Jetpack LiveData 是一种用于管理应用程序界面和数据交互的组件。
LiveData 是一种可观察的数据持有者,用于在应用程序组件(如 Activity、Fragment 和 Service)之间共享数据,并在数据发生更改时通知观察者。
LiveData 可以确保 UI 与数据的同步更新,避免了一些常见的错误,如内存泄漏和 UI 组件无法正确更新的问题。
LiveData 具有生命周期感知功能,可以自动感知应用程序组件的生命周期,并在组件处于活动状态时更新 UI,而在组件处于非活动状态时停止更新,从而有效地减少了资源消耗。
LiveData 还提供了线程安全的访问数据的机制,避免了多线程并发访问的问题。
如何 TextView 控件的显示内容呢?
首先,在 XML 布局文件中添加一个 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 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() {
@Overridepublic void onChanged(String s) {
mTextView.setText(s);
}
});
}
}
在上述代码中,我们创建了一个 LiveData 对象,并将其与一个 TextView 控件关联。当 LiveData 对象中的值发生变化时,我们使用 Observer 来监听这个变化,然后更新 TextView 的显示内容。
最后,我们可以在代码中通过 setValue() 或 postValue() 方法来更新 LiveData 对象的值,从而更新 TextView 的显示内容。例如:
((MutableLiveData) mLiveData)
.setValue("Hello, world!");
以上就是一个简单的 LiveData 案例,用于更新 TextView 控件的显示内容。当 LiveData 中的值发生变化时,TextView 控件会自动更新显示内容。
LiveData 的应用场景包括但不限于以下几个方面:
LiveData 是一种可以感知生命周期的数据持有者,它可以让数据更新时通知 UI 界面进行更新,同时也能够避免因为生命周期问题带来的内存泄漏。
LiveData 的简化类图:
在上面的类图中,Observer 是 LiveData 的观察者接口,LiveData 是可观察数据的持有者类。LiveData 具有生命周期感知能力,可以根据其生命周期状态自动管理数据的订阅和取消订阅。MutableLiveData 是 LiveData 的可变子类,允许更新 LiveData 持有的数据。LiveData 的 observe()方法用于注册观察者,setValue()和 postValue()方法用于更新 LiveData 数据。
LiveData 的核心代码在 androidx.lifecycle.LiveData 类中,下面对 LiveData 的源码进行简要分析:
LiveData 类是一个抽象类,它有一个泛型类型 T,表示 LiveData 中存储的数据类型。LiveData 类内部维护了一个数据源(mData)和一个观察者列表(mObservers),当 LiveData 中的数据发生改变时,会通知所有注册的观察者进行 UI 更新。
public abstract class LiveData {
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, ObserverWrapper> mObservers = new SafeIterableMap<>();
// ...
}
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);
// ...
}
解注册则是通过 removeObserver()方法进行的,该方法接受一个 Observer 对象,用于从观察者列表中删除相应的观察者。
public void removeObserver(@NonNull Observer super T> observer) {
assertMainThread("removeObserver");
// ...mObservers.remove(observerWrapper);
// ...
}
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, 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 更新。
除了以上核心方法之外,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 更新,避免了因为生命周期问题带来的内存泄漏。
要解决 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 implements Observer {
private boolean mIsStarted = false;
private Observer mObserver;
public MyObserver(Observer 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 mLiveData;
private MyObserver 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)成立,就不会导致没有注册观察者,还能接收到消息
调用 LiveData 的 setValue()方法时,如果 LiveData 的观察者处于激活状态,那么 LiveData 会将最新的数据推送给观察者,并在观察者的回调方法中更新 UI 界面。但是,如果 LiveData 的观察者处于非激活状态,那么 LiveData 不会将数据推送给观察者,也不会更新 UI 界面。因此,如果调用 setValue()方法后,UI 界面没有发生更新,可能是因为 LiveData 的观察者处于非激活状态。
LiveData 的观察者处于非激活状态的原因可能有以下几种:
因此,如果调用 setValue()方法后,UI 界面没有发生更新,可以检查观察者的连接状态和生命周期状态,确保 LiveData 的观察者处于激活状态,并且已经与 LiveData 建立连接。如果仍然无法解决问题,可以考虑使用 postValue()方法,该方法可以在 UI 线程空闲时更新 LiveData 的数据,并在观察者处于激活状态时通知观察者进行 UI 更新。
LiveData 的观察者(Observer)默认是弱引用,但是如果观察者没有及时取消观察,可能会导致内存泄漏。
如果一个 LiveData 对象被多个观察者同时观察,那么每个观察者都会收到相同的数据更新,可能会导致 UI 界面多次更新,造成性能问题。
LiveData 是与生命周期相关联的,如果 UI 组件的生命周期结束了,但是 LiveData 仍在发送数据更新,那么就会引发异常。
LiveData 默认在主线程进行数据更新,如果需要在后台线程进行数据更新,就需要使用 LiveData 的 postValue()方法,而不是 setValue()方法。
如果在一个 UI 组件中多次观察同一个 LiveData 对象,可能会导致重复观察,造成性能问题和数据不一致问题。因此,可以使用 ViewModel 中的 getLiveData()方法,保证同一个 LiveData 对象只被观察一次。
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 等响应式编程库一起使用,以实现数据的实时更新和响应。
如何在 ViewModel 中保存和管理数据,并在 Activity 或 Fragment 重新创建时保持数据的完整性。
public class MyViewModel extends ViewModel {
private MutableLiveData count = new MutableLiveData<>();
public void setCount(int count) {
this.count.setValue(count);
}
public LiveData getCount() {
return count;
}
}
在这个 ViewModel 类中,我们创建了一个 MutableLiveData 类型的 count 变量,并在 setCount() 方法中设置它的值,同时在 getCount() 方法中将其作为 LiveData 返回,以实现数据的实时更新和响应。
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 相关的数据,并提高应用程序的稳定性和可靠性。
下面是一些实际开发过程中可能使用 ViewModel 的使用场景:
Jetpack ViewModel 是一个用于管理 UI 组件生命周期的组件,它的设计目的是为了在 Activity 或 Fragment 重建时能够保留数据,避免因为重建导致的数据丢失或重复加载。在本文中,我们将会对 Jetpack ViewModel 的源码进行分析,深入了解其实现原理。
Jetpack ViewModel 源码位于 androidx.lifecycle.ViewModel 包中,它包含了两个类:ViewModel 和 ViewModelProvider。
ViewModel 类的主要作用是定义一个用于存储 UI 组件数据的容器,并在 UI 组件生命周期变化时进行管理。具体来说,ViewModel 类继承了 Android 的 ViewModel 类,并添加了一些额外的方法,用于实现以下功能:
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()
companion object {
private val sCache = HashMap()
/**
* 返回当前 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 get(key: String, modelClass: Class): 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 create(modelClass: Class): T
}
/**
* FactoryHolder 类,用于提供默认的 Factory 实现
*/
private object FactoryHolder {
val DEFAULT_FACTORY: Factory = object : Factory {
override fun create(modelClass: Class): 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 来缓存和共享数据,从而减少数据的重复加载和提升应用程序的性能。
在某些情况下,可能会需要多个 ViewModel 来管理不同的 UI 数据。如果使用了错误的 ViewModel 来管理数据,可能会导致数据丢失或逻辑错误。因此,在使用 ViewModel 时应该清楚每个 ViewModel 的作用,避免出现混淆。
ViewModel 的生命周期不同于 Activity 和 Fragment,它是被系统缓存的,因此可能会出现数据被清除的情况。在使用 ViewModel 时应该注意它的生命周期,及时保存数据并恢复数据。
ViewModel 需要一个有效的上下文来创建实例,如果使用无效的上下文可能会导致 ViewModel 创建失败。因此,在使用 ViewModel 时应该注意上下文的有效性,避免出现创建失败的情况。
ViewModel 的作用域应该与需要管理的 UI 组件的生命周期相同,如果使用了错误的作用域,可能会导致数据被清除或者生命周期不一致。因此,在使用 ViewModel 时应该选择正确的作用域,避免出现问题。
DataBinding 提供了一种声明性的方式将布局文件中的 UI 组件和应用程序的数据模型绑定在一起。
通过 DataBinding,开发者可以将 UI 组件的值绑定到数据模型中的属性,使得在更新 UI 时不需要手动更新每个组件的值。
DataBinding 库通过生成一个绑定类来实现 UI 和数据模型之间的绑定。
这个绑定类是在编译时自动生成的,它使用了数据模型和 UI 组件之间的绑定表达式,以便在运行时执行数据绑定。
使用 DataBinding 实现一个简单的计数器功能。在每次更新计数器的值时,DataBinding 会自动更新 TextView 的值,使得开发者不需要手动更新 UI 组件的值,可以更加专注于业务逻辑的实现
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
name="counter"
type="Integer" />
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:id="@+id/tvCounter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{counter.toString()}" />
创建 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()
}
}
}
简化代码
使用 DataBinding 可以减少 findViewById()方法的调用,使得代码更加简洁和易读。开发者可以通过 DataBinding 直接访问布局文件中的 UI 组件,而不需要手动查找和设置 UI 组件的属性。
双向绑定
DataBinding 支持双向绑定,可以将 UI 组件的值绑定到数据模型中的属性,也可以将数据模型的属性绑定到 UI 组件的值。这种双向绑定可以帮助开发者更加方便地实现 UI 和数据模型之间的同步。
减少错误
DataBinding 可以减少代码中的错误,因为使用 DataBinding 可以消除某些手动编写的代码。例如,DataBinding 可以自动生成一些代码,帮助开发者绑定 UI 组件和数据模型,减少手动编写代码的机会。
性能优化
DataBinding 可以带来一些性能优化,例如通过生成绑定类来减少代码的执行时间。DataBinding 还支持懒加载,可以在需要时才加载数据模型和 UI 组件。
兼容性问题
DataBinding 库需要使用较新的 Android Gradle 插件和 Android SDK 版本,这可能导致一些兼容性问题。如果开发者使用较老的 Android 版本,可能需要进行一些兼容性处理。
构建时间增加
使用 DataBinding 可能会增加应用程序的构建时间,因为 DataBinding 库需要生成绑定类。在较大的应用程序中,这可能会导致构建时间明显增加。
难以调试
由于 DataBinding 库使用了代码生成,因此在调试时可能会出现一些困难。例如,开发者可能会发现在使用 DataBinding 时,调试器可能会跳过某些代码行,或者某些变量的值可能无法正确显示。
DataBinding 的原理主要包括以下几个方面:
总的来说,Android Jetpack DataBinding 的原理是基于数据绑定和观察者模式的。它通过生成代码来完成 UI 组件和数据对象之间的绑定,并使用观察者模式来保持 UI 和数据之间的同步。同时,DataBinding 还支持双向绑定、属性转换器和数据绑定表达式等特性,使得它可以满足更加复杂的 UI 和数据交互需求。
《Android 业务架构 · 基础篇 · Jetpack 四件套》一文首先通过 4W2H 全方位的讲解了 Jepack 对 Android 业务开发的价值,然后通过基础定义、基础使用、优劣势分析、应用场景、原理分析、注意事项等多维度分析了 Jetpack 四件套。
在我们工作中,可维护、可扩展、可测试和可重用的业务架构对于提高应用程序的质量和效率意义非凡,而 JetPack 是帮助开发者快速地组织和管理应用程序的代码的工具包。
这也是小木箱强烈建议大家学习 Jetpack 很重要的原因。希望通过这篇文章能够让你意识到 Jetpack 对业务开发的重要性。
提高篇将介绍 MVC、MVP、MVVM 和 MVI 四剑客,同样是 Android 业务架构核心内容。 今天就到这里啦,我是 **小木箱**,我们下一篇见~
本文由 mdnice 多平台发布