MVVM下的Jetpack核心组件

这篇具有很好参考价值的文章主要介绍了MVVM下的Jetpack核心组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

Jetpack 架构组件及 “标准化开发模式” 确立,意味着Android 开发已步入成熟阶段,只有对 MVVM 确有深入理解,才能自然而然写出标准化、规范化代码。

本次笔者会浅入浅出的介绍以下内容,由于它是一个我的学习总结记录,所以比较适合对MVVM不是很熟悉,但又想了解下全貌的读者:

  • Jetpack MVVM
  • Jetpack Lifecycle
  • Jetpack LiveData
  • Jetpack ViewModel
  • Jetpack DataBinding

Jetpack MVVM

在正文开始前,先回顾下MVP

MVP,Model-View-Presenter,职责分类如下:

  • Model,数据模型层,用于获取和存储数据。
  • View,视图层,即Activity/Fragment
  • Presenter,控制层,负责业务逻辑。

我们知道,MVP是对MVC的改进,解决了MVC的两个问题:

  • View责任明确,逻辑不再写在Activity中,放到了Presenter中;
  • Model不再持有View

MVP最常用的实现方式是这样的:

View层接收到用户操作事件,通知到PresenterPresenter进行逻辑处理,然后通知Model更新数据,Model 把更新的数据给到PresenterPresenter再通知到View 更新界面。

MVP本质是面向接口编程,它也存在一些痛点:

  • 会引入大量的IViewIPresenter接口,增加实现的复杂度。
  • ViewPresenter相互持有,形成耦合。

随着发展,Jetpack MVVM 就应势而生,它是MVVM 模式在Android 开发中的一个具体实现,是Google 官方提供并推荐的MVVM实现方式。它的分层:

  • Model层:用于获取和存储数据
  • View层:即Activity/Fragment
  • ViewModel层:负责业务逻辑

MVVM的核心是 数据驱动,把解耦做的更彻底(ViewModel不持有view )。

View 产生事件,使用ViewModel进行逻辑处理后,通知Model更新数据,Model把更新的数据给ViewModelViewModel自动通知View更新界面

Jetpack Lifecycle

起源

在没有Lifecycle之前,生命周期的管理都是靠手工维持。比如我们经常会在ActivityonStart初始化某些成员(比如MVPPresenterMediaPlayer)等,然后在onStop中释放这些成员的内部资源。

class MyActivity extends AppCompatActivity {
    private MyPresenter presenter;
​
    public void onStart(...) {
        presenter= new MyPresenter ();
        presenter.start();
    }
​
    public void onStop() {
        super.onStop();
        presenter.stop();
    }
}
​
class MyPresenter{
    public MyPresenter() {
    }
  
    void start(){
       // 耗时操作
      checkUserStatus{
        if (result) {
          myLocationListener.start();
        }
      }
    }
​
    void stop() {
      // 释放资源
      myLocationListener.stop();
    }
}

上述的代码本身是没有太大问题的。它的缺点在于实际生产环境下,会有很多的页面和组件需要响应生命周期的状态变化,就得在生命周期方法中放置大量的代码,这样的方式就会导致代码(如 onStart()onStop())变得臃肿,难以维护。

除此之外还有一个问题就是:

MyPresenter类中onStart里的checkUserStatus是个耗时操作,如果耗时过长,Activity 销毁的时候,还没有执行过来,就已经stop了,然后等一会儿执行过来的时候,myLocationListenerstart,但后面不会再有myLocationListenerstop,这样这个组件的资源就不能正常释放了。如果它内部还持有Activity的引用,还会造成内存泄露。

Lifecycle

于是,Lifecycle就出来了,它通过 “模板方法模式” 和 “观察者模式”,将生命周期管理的复杂操作,放到LifecycleOwner(如 Activity、Fragment 等 “视图控制器” 基类)中封装好。

对于开发者来说,在 “视图控制器” 的类中只需一句 getLifecycle().addObserver(new MyObserver()) ,当Lifecycle的生命周期发生变化时,MyObserver就可以在自己内部感知到。

protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_lifecycle);
   // 使MyObserver感知生命周期
   getLifecycle().addObserver(new MyObserver());
}

看看它是怎么实现的:

# ComponentActivity
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);   
public Lifecycle getLifecycle() {
    return mLifecycleRegistry;
}
​
# LifecycleRegistry
public LifecycleRegistry(@NonNull LifecycleOwner provider) {
    this(provider, true);
}
​
private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
            new FastSafeIterableMap<>();
public void addObserver(@NonNull LifecycleObserver observer) {
  mObserverMap.putIfAbsent(observer, statefulObserver);
  ...
}
public void removeObserver(@NonNull LifecycleObserver observer) {
   mObserverMap.remove(observer);
}
​
void dispatchEvent(LifecycleOwner owner, Event event) {
  State newState = event.getTargetState();
  mState = min(mState, newState);
  mLifecycleObserver.onStateChanged(owner, event);
  mState = newState;
}

正因为Activity实现了LifecycleOwner,所以才能直接使用getLifecycle()

# ComponentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
    // 关键代码:通过ReportFragment完成生命周期事件分发
    ReportFragment.injectIfNeededIn(this); 
    if (mContentLayoutId != 0) {
        setContentView(mContentLayoutId);
    }
}
# ReportFragment
static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
    if (activity instanceof LifecycleOwner) {
        Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
        if (lifecycle instanceof LifecycleRegistry) {
          // 处理生命周期事件,更新当前都状态并通知所有的注册的LifecycleObserver
          ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
        }
    }
}
​
# LifecycleRegistry
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
    enforceMainThreadIfNeeded("handleLifecycleEvent");
    moveToState(event.getTargetState());
}
小结

所以Lifecycle 的存在,是为了解决 “生命周期管理” 一致性的问题。

Jetpack LiveData

起源

在没有LiveData的时候,我们在网络请求回调、跨页面通信等场景分发消息,大多是通过EventBus、接口callback的方式去完成。

比如经常使用的EventBus等消息总线的方式会有问题:

它缺乏一种约束,当我们去使用时,很容易因为随处使用,最后追溯数据来源的难度就会很大。

另外,EventBus在处理生命周期上也很麻烦,由于需要手动去控制,会容易出现生命周期管理不一致的问题。

LiveData

先看下官方的介绍:

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意味着它遵循其他应用组件(如 Activity/Fragment)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

如果观察者的生命周期处于 STARTEDRESUMED状态,则 LiveData 会认为该观察者处于活跃状态,就会将更新通知给活跃的观察者,非活跃的观察者不会收到更改通知。

LiveData观察者模式 的体现,先从LiveDataobserve方法看起:

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
  // LifecycleOwner是DESTROYED状态,直接忽略
  if (owner.getLifecycle().getCurrentState() == DESTROYED) {
      return;
  }
  // 绑定生命周期的Observer
  LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
  ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
  // 让该Observer可以感知生命周期
  owner.getLifecycle().addObserver(wrapper);
}

observeForeverobserve()类似,只不过它会认为观察者一直是活跃状态,不会自动移除观察者。

LiveData很重要的一部分就是数据更新:·

LiveData原生的API提供了2种方式供开发者更新数据, 分别是setValue()postValue(),调用它们都会 触发观察者并更新UI

setValue()方法必须在 主线程 进行调用,而postValue()方法更适合在 子线程 中进行调用。postValue()最终也会调用setValue,只需要看下setValue方法就可以了:

protected void setValue(T value) {
  assertMainThread("setValue");
  mVersion++;
  mData = value;
  dispatchingValue(null);
}
​
void dispatchingValue(@Nullable ObserverWrapper initiator) {
  ...
  for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
          mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
      considerNotify(iterator.next().getValue());
  }
}
​
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    ...
    observer.mObserver.onChanged((T) mData);
}

小问题:我们在使用LiveData有一个优势是不会发生内存泄漏,是怎么做到的呢?

这需要从上面提到的observe方法中寻找答案

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
  LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
  owner.getLifecycle().addObserver(wrapper);
}

传递的第一个是 LifecycleOwner,第二个参数Obserser实际就是我们的观察后的回调。这两个参数被封装成了LifecycleBoundObserver对象。

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event) {
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    if (currentState == DESTROYED) {
      // Destoryed状态下,自动移除mObserver,避免内存泄漏
      removeObserver(mObserver);
      return;
    }
    activeStateChanged(shouldBeActive());
    ...
 }

这里就解释了为什么LiveData能够 自动解除订阅而避免内存泄漏 了,因为它内部能够感应到Activity或者Fragment的生命周期。

PS:这种设计非常巧妙,给我们一个启发点:

在我们初识 Lifecycle 组件对它不是理解很透彻的时候,总是下意识认为它能够对大的对象进行有效生命周期的管理(比如Presenter),实际上,这种生命周期的管理我们完全可以应用到各个功能的基础组件中,比如大到吃内存的MediaPlayer、绘制设计复杂的自定义View,小到随处可见的LiveData,都可以通过实现LifecycleObserver接口达到感应生命周期的能力,并内部释放重资源的目的。

小结

LiveData在感知生命周期的能力下,让应用数据发生变化时通过观察者去更新界面,并且不会出现内存泄露的情况。

Jetpack ViewModel

起源

在没有ViewModel,我们用MVP开发的时候,我们为了实现数据在UI上的展示,往往会写很多UI层和Model层相互调用的代码,这些代码写起来繁琐且一定程度的模版化。另外,某些场景(例如屏幕旋转)销毁和重新创建界面,那么存储在其中的界面相关数据都会丢失,一般都需要手动存储和恢复。

为了解决这两个痛点,ViewModel就出场,用ViewModel用于代替MVP中的Presenter

ViewModel 的概念就是这样被提出来的,它就像一个 状态存储器 ,存储着UI中各种各样的状态。

ViewModel的好处

1.更规范化的抽象接口

Google官方建议ViewModel尽量保证 纯的业务代码,不要持有任何View层(Activity或者Fragment)或Lifecycle的引用,这样保证了ViewModel内部代码的可测试性,避免因为Context等相关的引用导致测试代码的难以编写(比如,MVPPresenter层代码的测试就需要额外成本,比如依赖注入或者Mock,以保证单元测试的进行)。

也正是这样的规范要求,ViewModel不能持有UI层引用,自然也就避免了可能发生的内存泄漏。

2.更便于保存数据

当组件被销毁并重建后,原来组件相关的数据也会丢失。最简单的例子就是屏幕的旋转,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。

ViewModel的扩展类则会在这种情况下自动保留其数据,如果Activity被重新创建了,它会收到被之前相同ViewModel实例。当所属Activity终止后,框架调用ViewModelonCleared()方法释放对应资源。

3.更方便UI组件之间的通信

一个Activity中的多个Fragment相互通讯是很常见的,如果ViewModel的实例化作用域为Activity的生命周期,则两个Fragment可以持有同一个ViewModel的实例,这也就意味着数据状态的共享

接下来,分析它的源码是怎么做到这些的:

我们可以通过ViewModelProvider注入ViewModelStoreOwner,从而为引用ViewModel 的页面(比如Activity)创建一个临时的、单独的 ViewModelProvider 实例。并通过这个ViewModelProvider可以获取到ViewModel

# this: ViewModelStoreOwner(interface)
ViewModelProvider(this).get(viewModelClass)

分创建、获取两步来看,先看创建ViewModelProvider做了什么:

# ViewModelProvider 
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
  // owner.getViewModelStore(),比如:owner是ComponentActivity
  this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
          ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
          : NewInstanceFactory.getInstance());
}
​
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}
​
public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}
​
# ComponentActivity implements ViewModelStoreOwner
public ViewModelStore getViewModelStore() {
    // 为空就创建
    ensureViewModelStore();
    return mViewModelStore;
}
​
void ensureViewModelStore() {
 if (mViewModelStore == null) {
     mViewModelStore = new ViewModelStore();
  }
}

这一步是基石:把ViewModelStoreOwnermViewModelStore绑定到了ViewModelProvider中。简单点说就是同一个ViewModelStoreOwner拿到的是同一个mViewModelStore

如何获取对应的ViewModel

# ViewModelProvider
private final ViewModelStore mViewModelStore;
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
​
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    // 直接返回已存在的viewModel
    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    // 存储viewModel
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}
​
# ViewModelStore
public class ViewModelStore {
  private final HashMap<String, ViewModel> mMap = new HashMap<>();
  final void put(String key, ViewModel viewModel) {
    ViewModel oldViewModel = mMap.put(key, viewModel);
    if (oldViewModel != null) {
        oldViewModel.onCleared();
    }
  }
}

即通过这样的设计,来实现类似于单例的效果:每个页面都可以通过ViewModelProvider 注入Activity 这个ViewModelStoreOwner,来共享跨页面的状态;

同时,又不至于完全沦为简单粗暴的单例:每个页面都可以通过 ViewModelProvider 注入this,来管理私有的状态。

比如下面这个具体的例子:

当应用中某个ViewModel 存在既被ViewModelProvider 传入过 Activity,又被传入过某个 Fragmentthis 情况,实际上是生成了两个不同的 ViewModel实例,属于不同的 ViewModelStoreOwner。当引用被this 持有的ViewModel 的 页面destory 时,被Activity 持有的ViewModel 的页面并不受影响。

小结

ViewModel是为了解决 “状态管理” 和 “页面通信” 问题。有了ViewModel,我们在开发的时候,可以大幅减少UI层和Model层相互调用的代码,将更多的重心投入到业务代码的编写

Jetpack DataBinding

起源

DataBinding 出现以前,想要更新视图就要引用该视图,然后调用setxxx方法:

TextView textView = findViewById(R.id.sample_text);
if (textView != null && viewModel != null) {
    textView.setText(viewModel.getUserName());
}

这种方式有几个不好的地方:

  • 容易出现空指针(存在差异的横、竖两种布局,如横屏存在此 textView 控件,而竖屏没有),引用该视图一般要先判空
  • 需要写模板代码 findViewById
  • 业务复杂的话,一个控件会在多处调用
DataBinding

DataBinding是个受争议比较大的组件。很多人对 DataBinding 的认知就是在xml中写逻辑:

  • xml中写表达式逻辑,出错了debug不了
  • 逻辑写在xml里面的话 xml 就承担了 Presenter/ViewModel 的职责,职责变得混乱了

当然如果站在把逻辑写在xml中的角度看,确实会造成xml中是不能调试的、职责混乱。

但这不是DataBinding的本质。DataBinding,含义是 数据绑定,即 布局中的控件可观察的数据 进行绑定。

<TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@{user.name}"/>

user.nameset 新值时,被绑定了该数据的控件即可获得通知和刷新。就是说,在使用DataBinding 后,唯一的改变是,你无需手动调用视图来 set 新状态,你只需 set 数据本身。

所以,DataBinding 并非是将 UI 逻辑搬到 XML 中写导致而难以调试 ,它只负责绑定数据,将 UI 控件与其需要的终态数据进行绑定。

双向绑定

上面介绍的例子,数据的流向是单向的,只需要监听到数据的变更然后展示到UI上,是个单向绑定。

但有些场景,UI的变化需要影响到ViewModel层的数据状态,比如UI层的EditText,对它进行编辑并需要更新LiveData的数据。这时就需要 双向绑定

Android原生控件中,绝大多数的双向绑定使用场景,DataBinding都已经帮我们实现好了,比如EditText

<EditText
  android:id="@+id/etPassword"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@={fragment.viewModel.password }" />

相比单向绑定,只需要多一个=符号,就能保证View层和ViewModel层的 状态同步

双向绑定使用起来很简单,但定义却稍微比单向绑定麻烦一些,即使原生的控件DataBinding已经帮助我们实现好了,对于三方的控件或者自定义控件,还需要我们自己实现

举个栗子

这里举个下拉刷新SwipeRefreshLayout的例子,来看看双向绑定是怎么实现的:

我们的需求时:当我们为LiveData手动设置值时,SwipeRefreshLayout的UI也会发生对应的变更;反之,当用户手动下拉执行刷新操作时,LiveData的值也会对应的变成为true(代表刷新中的状态):

// refreshing实际是一个LiveData:
val refreshing: MutableLiveData<Boolean> = MutableLiveData()
​
object SwipeRefreshLayoutBinding {
  // 1.@BindingAdapter 在数据发生更改时要执行的操作:
  // 每当LiveData的状态发生了变更,SwipeRefreshLayout的刷新状态也会发生对应的更新。
  @JvmStatic
  @BindingAdapter("app:bind_swipeRefreshLayout_refreshing")
  fun setSwipeRefreshLayoutRefreshing(
          swipeRefreshLayout: SwipeRefreshLayout,
          newValue: Boolean
  ) {
      // 判断值是否变化了,避免无限循环
      if (swipeRefreshLayout.isRefreshing != newValue)
          swipeRefreshLayout.isRefreshing = newValue
  }
  
  // 2.@InverseBindingAdapter: view视图发生更改时要调用的内容
  // 但是它不知道特性何时或如何更改,所以还需要设置视图监听器
  @JvmStatic
  @InverseBindingAdapter(
          attribute = "app:bind_swipeRefreshLayout_refreshing",  
          event = "app:bind_swipeRefreshLayout_refreshingAttrChanged"    // tag
  )
  fun isSwipeRefreshLayoutRefreshing(swipeRefreshLayout: SwipeRefreshLayout): Boolean =
          swipeRefreshLayout.isRefreshing
 }
  
  // 3. @BindingAdapter: 事件监听器与相应的 View 实例相关联
  // 观察view的状态变化,每当swipeRefreshLayout刷新状态被用户的操作改变
  @JvmStatic
  @BindingAdapter(
          "app:bind_swipeRefreshLayout_refreshingAttrChanged",     // tag
          requireAll = false
  )
  fun setOnRefreshListener(
          swipeRefreshLayout: SwipeRefreshLayout,
          bindingListener: InverseBindingListener?
  ) {
      if (bindingListener != null)
          // 监听下拉刷新
          swipeRefreshLayout.setOnRefreshListener {
              bindingListener.onChange()
          }
  }

双向绑定将SwipeRefreshLayout的刷新状态抽象成为了一个LiveData<Boolean>,我们只需要在xml中定义好,之后就可以在ViewModel中围绕这个状态进行代码的编写。

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:bind_swipeRefreshLayout_refreshing="@={fragment.viewModel.refreshing}">
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

注意事项:避免死循环

双向绑定有一个致命的问题,那就是无限循环会导致的ANR异常。

View层UI状态被改变,ViewModel对应发生更新,同时,这个更新又回通知View层去刷新UI,这个刷新UI的操作又会通知ViewModel去更新…

因此,为了保证不会无限的死循环导致AppANR异常的发生,我们需要在最初的代码块中加一个判断,保证只有View状态发生了变更,才会去更新UI。

小结

DataBinding通过让 “控件” 与 “可观察数据” 发生绑定,它的本质是将终态数据 绑定到View ,而不是在xml写逻辑,当该数据被 set 新内容时,被绑定该数据的控件即可被通知和刷新。


为了帮助大家更好的熟知Jetpack 这一套体系的知识点,这里记录比较全比较细致的《Jetpack 入门到精通》(内含Compose) 学习笔记!!! 对Jetpose Compose这块感兴趣的小伙伴可以参考学习下……

Jetpack 全家桶(Compose)

Jetpack 部分

  1. Jetpack之Lifecycle
  2. Jetpack之ViewModel
  3. Jetpack之DataBinding
  4. Jetpack之Navigation
  5. Jetpack之LiveData

MVVM下的Jetpack核心组件,移动开发,Android,设计模式,android,android jetpack,移动开发,安卓,compose

Compose 部分
1.Jetpack Compose入门详解
2.Compose学习笔记
3.Compose 动画使用详解

MVVM下的Jetpack核心组件,移动开发,Android,设计模式,android,android jetpack,移动开发,安卓,compose文章来源地址https://www.toymoban.com/news/detail-642516.html

到了这里,关于MVVM下的Jetpack核心组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Mvvm设计模式的详解与实现

            在开发设计模式中,模式经历了多次迭代,从MVC到MVP,再到如今的MVVM。发现的过程其实很简单,就是为了项目更好的管理。         设计模式严格来说属于软件工程的范畴,但是如今在各大面试中或者开发中,设计模式被问的很多。特别是八股文的二十三种设

    2024年02月10日
    浏览(36)
  • 区块链钱包开发(Android篇),深入解析android核心组件和应用框架

    作用: 1、备份更容易。按照比特币的原则,尽量不要使用同一个地址,一个地址只使用一次,这样会导致频繁备份钱包。HD钱包只需要在创建时保存主密钥,通过主密钥可以派生出所有的子密钥。 2、私钥离线更安全。主私钥离线存储,主公钥在线使用,通过主公钥可以派生

    2024年03月24日
    浏览(43)
  • Android Jetpack组件架构:ViewModel的原理

    本篇文章是关于介绍ViewModel的,由于ViewModel的使用还是挺简单的,这里就不再介绍其的基本应用,我们主要来分析ViewModel的原理。 众所周知,一般使用ViewModel是用来解决两个问题的,第一个就是关于设备配置发生改变时Activity先前状态的保存,在ViewModel出来之前我们一般会使

    2024年02月07日
    浏览(41)
  • Android Jetpack组件的全方位分析

    Jetpack是一个用于简化Android应用程序开发的工具包,包含了一系列的组件和工具。Jetpack包含了很多组件,如LiveData、ViewModel、Room、Data Binding、Navigation等。 Jetpack组件是一种更高级别的抽象,它们可以提供更简洁、更易于使用的API。支持库是Jetpack组件的底层实现。 基本概念和

    2024年02月11日
    浏览(40)
  • Android端MVVM从入门到实战(第一篇) - MVVM和四大官方组件

    MVVM是相对于MVC和MVP的一个概念,是一种架构模式。 1.1 MVC 传统的MVC中,View改变通知Controller进行处理,Controller处理结束后通知Model层更新,Model层更新以后通知View层渲染,指令单项流动,角色分工明确。但是MVC有三个缺点,1、三个角色互相持有对方依赖,因此很难复用其中任

    2024年02月04日
    浏览(39)
  • Android Jetpack组件库(第三部分)---WorkManager

    Android Jetpack 是 Google 推出的一整套帮助 Android 应用程序开发的库、工具包和架构指南,旨在为 Android 应用程序提供更快,更轻松,更稳定的开发体验。自推出以来已经发展成了一个庞大的技术生态系统,包括了许多使用方便、功能强大的库,以下是其中一些新特性、新组件:

    2024年02月07日
    浏览(39)
  • 探索Android Jetpack Compose的Surface组件

    随着声明性 UI 框架 Jetpack Compose 的出现,Android 开发变得更加简洁和直观。在这篇博客中,我们将深入探讨其中的一项基本构建块 —— Surface 组件,了解它如何影响 UI 的显示和设计。 一、Jetpack Compose和Surface组件 二、Surface组件的基本使用 三、影响Surface的属性 一、Jetpack Co

    2024年02月11日
    浏览(55)
  • Android笔记(七)Android JetPack Compose组件搭建Scaffold脚手架

    在去年2022年曾发布一篇关于脚手架的文章:“Android JetPack Compose组件中Scaffold的应用” 。但是Android的版本从12变更到13及以上版本,导致一些细节的实现存在不同。在本文中,将从头开始介绍整个脚手架的搭建过程。 在Android Studio(版本是Graffie)中新建模块,选择“Empty Activ

    2024年02月04日
    浏览(44)
  • Android笔记(六):JetPack Compose常见的UI组件

    Text显示的文本来源可以引用res-values-strings.xml中的资源,如第一个显示文本所示。 点击按钮前: 点击按钮后: 点击第一个圆角按钮不放时,显示为按钮:true Button有两方面需要注意: (1) Buttton有一个参数interactionSource,用来监听组件状态的事件源,通过它获取组件的状态来

    2024年02月04日
    浏览(50)
  • 浅谈下mvc和mvp、mvvm到mvvm+Jetpack

    作者:抓不住老鼠的猫 MVC MVC全名为Model-View-Controller,图解如下 View:负责与用户交汇,显示界面。 Controller:负责接收来自view的请求,处理业务逻辑。 Model:负责数据逻辑,网络请求数据以及本地数据库操作数据等。 在MVC架构中,Controller是业务的主要承载者,几乎所有的业

    2024年02月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包