智能座舱进阶-应用框架层-Jetpack主要组件

news/2024/12/22 7:11:46/

Jetpack的分类
在这里插入图片描述

1. DataBinding:以声明方式将可观察数据绑定到界面元素,通常和ViewModel配合使用。
2. Lifecycle:用于管理Activity和Fragment的生命周期,可帮助开发者生成更易于维护的轻量级代码。
3. LiveData: 在底层数据库更改时通知视图。它是一个可观察的数据持有者,与常规observable不同,LiveData是生命周期感知的。
4. Navigation:处理应用内导航。
5. Paging:可以帮助开发者一次加载和显示小块数据,按需加载部分数据可减少网络带宽和系统资源的使用。
6. Room:友好、流畅的访问SQLite数据库。它在SQLite的基础上提供了一个抽象层,允许更强大的数据库访问。
7. ViewModel: 以生命周期的方式管理界面相关的数据,通常和DataBinding配合使用,为开发者实现MVVM架构提供了强有力的支持。
8. WorkManager: 管理Android的后台的作业,即使应用程序退出或设备重新启动也可以运行可延迟的异步任务。

Android 标准架构框架图:
在这里插入图片描述

Android官方架构部分的知识 https://developer.android.google.cn/topic/architecture/intro?hl=zh-cn

ViewBinding&DataBinding篇

ViewBinding介绍和使用

大家还是不要把ViewBinding和DataBinding这两个不要混淆了.
ViewBindling 这是一个负责绑定View到代码,减少 findViewId降低空引用资源ID错误。使用起来也较为简单, 流程是:
第一、 在 app模块下的build.gradle.kts下,打开使用ViewBinder的开关:

Android{
…
buildFeatures {ViewBinding = true}
}

第二就是需要在使用的Activity、fragment、view里面去初始化一下,inflate“

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)}
}

第三步就可以直接引用使用了

binding.button.text="helloworld"

注意: viewBinding 目前能够支持所有Xml的控件进行自动绑定, 包含Activity、Fragment以及其他View等。

ViewBinding原理

开启后会自动生成【xml的文件名 自动大驼峰】+Binding.kt 文件。例如ActivityMainBinding.kt。可以看一下自动生成文件代码,我只看inflate最后执行的部分:

public static ActivityMainBinding bind(@NonNull View rootView) { 
int id; 
id = R.id.tv1; 
TextView tv1 = ViewBindings.findChildViewById(rootView, id);if (tv1 == null) { 
break missingId; 
} 
id = R.id.tv2; 
TextView tv2 = ViewBindings.findChildViewById(rootView, id);
if (tv2 == null) { 
break missingId; 
} 
return new ActivityMainBinding((ConstraintLayout) rootView, tv1, tv2);} 
}

可以看到自动生成的代码里,会把XML里控件的每个id都会塞进去,并且调findViewById进行绑定。

DataBinding的介绍和使用

Android 开发中体现 MVVM 架构思想的 Data Binding,其核心是 观察者模式 的特定实现。首先,它有三个主要的实体:

  1. Data:与 View 相关的数据,它可以是 View 的可观察者对象;
  2. View:展示给用户的视图,如果有交互功能且能更新数据,它可以是 Data 的可观察者对象;
  3. ViewDataBinding:连接 Data 和 View 的中介,当 Data 或 View 作为可观察者对象时,它充当可观察者对象的代理。假如当我们写了一个名为 demo.xml 的 Data Binding 的 layout 文件后,编译工具会生成一个相应的类——DemoBinding,它的原型就是 ViewDataBinding。我们通常通过 DataBindingUtil.inflate(inflater, R.layout.demo, container, false) 来实例化的 DemoBinding 对象,即 ViewDataBinding。

主要是三个方面的功能:
• 将特定的 View 与特定的 Data 进行绑定,便于模块化;
• View 自动感知和响应 Data 的变化,使得处理数据的业务层不必关心 View 的状态,便于解耦;
• Data 也可以自动同步带有交互功能的 View 对数据的修改,使得 UI 层的交互不必担心数据是否能同步 View 状态的问题,仍然便于解耦

使用类似ViewBinding:
第一是,在 app模块下的build.gradle.kts下,打开使用ViewBinder的开关:

Android{
…
buildFeatures {DataBinding = true}
}

第二就可以直接在需要使用Xml文件里使用就行了,但是前提现有已经新简的Data类:

class User(var firstName: String, var lastName: String) {}
<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data> <variable name="user" type="com.example.User"/></data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout></layout>

基础用法很简单, 但是这只是单项View-Data绑定。
比较好的功能是 它还能帮助我们做到通过感知Data的局部变量来进行局部刷新,还有可以可以绑定View,进行双向绑定。使用方法就是将Data转化成observe 对象,并且添加@Bindable 的竹节,通过notifyPropertyChanged()方法来达成局部刷新,如下:

public class DemoData extends BaseObservable {private int element;
// 定义其它成员,省略
@Bindablepublic int getElement() {return this.element;}
public void setElement(int e) {this.element = e;notifyPropertyChanged(BR.element);}
// 省略其它成员操作
}

ViewModel篇

viewModel 的使用, viewModel继承子类使用, 数据存储在内存中,可以和acitivty 生命周期进行绑定,也独立于Activity;onSaveInstanceState(Bundle) 存储的数据,仅在当前应用进程哪有效, 当退出重新进入, Bundle重新恢复成初始状态。或者在 OnStop中保存永久性数据

存储作用域
实例化 ViewModel 时,您会向其传递实现 ViewModelStoreOwner 接口的对象。它可能是 Navigation 目的地、Navigation 图表、activity、fragment 或实现接口的任何其他类型。然后,ViewModel 的作用域将限定为 ViewModelStoreOwner 的 Lifecycle。它会一直保留在内存中,直到其 ViewModelStoreOwner 永久消失。
有一系列类是 ViewModelStoreOwner 接口的直接或间接子类。直接子类为 ComponentActivity、Fragment 和 NavBackStackEntry。如需查看间接子类的完整列表,请参阅 ViewModelStoreOwner 参考文档。
当 ViewModel 的作用域 fragment 或 activity 被销毁时,异步工作会在作用域限定到该 fragment 或 activity 的 ViewModel 中继续进行。这是持久性的关键。
如需了解详情,请参阅下文有关 ViewModel 生命周期的部分

我这里不去赘述 如何使用它, 我曾在上一家公司中对的Alios系统平台, 使用TS语言设计了一个跟viewModel类似的 组件库DataManager .主要的设计原则是观察者绑定和职责分离原则。 本质上来讲ViewModel 就是一个独立的数据储存类, 设计者在里面做了跟Activity的destory来消亡的【非因为配置改变导致的destory】,其实也可以做到完全独立,消亡由开发者自己决定, 这样就会很有趣了,多个activty可以共一个ViewModel实例.只是里面做了一些对象传递的封装,就可以和LiveData进行配合使用。

  • ViewModelStore:用于存储ViewModel实例的类,内部持有一个HashMap保存实例,ViewModelProvider会将创建好的ViewModel实例保存到ViewModelStore中,之后再需要此类ViewModel的实例时就直接从中读取。
  • ViewModelProvider.Factory:前文已经提到,这是用于创建ViewModel实例的工厂,ViewModelProvider当需要ViewModel的实例又在ViewModelStore中没有找到对应实例时就会调用工厂的create方法创建。
  • CreationExtras:前文也已提到,它用于在创建ViewModel实例时从外界向构造过程传递参数,内部持有一个MutableMap,以key-value的形式存储和查找参数

ViewModelStore中的HashMap 设计应该是至少两层以上的,在当初我设计的空间中,这里用的两层, 并且因为是量产项目的原因, 加了一个些状态机key值进行了耦合。 Android官方是直接保存的ViewModel的对象,这样就只有一层了。
每个Acitivty都会有一个ViewModelStoreOwner ,这样在开发者做项目的时候,就不需要关系每个Activity的获取ViewModel的时候,就是绑定的哪一个。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements

// 实现了 ViewModelStoreOwner 接口
ViewModelStoreOwner,
…{
private ViewModelStore mViewModelStore;
// 重写了 ViewModelStoreOwner 接口的唯一的方法 getViewModelStore()
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ “Application instance. You can’t request ViewModel before onCreate call.”);
}
ensureViewModelStore();
return mViewModelStore;
}
ViewModelProvider 的构造方法时 ,获取 ViewModelStore 对象时,实际调用了 MainActivity#getViewModelStore() ,而 getViewModelStore() 实现在 MainActivity 的父类 ComponentActivity 中。
在返回 mViewModelStore 对象之前调用了 ensureViewModelStore()
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}


- onRetainNonConfigurationInstance 方法和 getLastNonConfigurationInstance 是成对出现的,跟 onSaveInstanceState 机制类似,只不过它是仅用作处理配置更改的优化。
- 返回的是 onRetainNonConfigurationInstance 返回的对象Activity 在因配置更改而销毁重建过程中会先调用 onRetainNonConfigurationInstance 保存 viewModelStore 实例。 在重建后可以通过 getLastNonConfigurationInstance 方法获取之前的 viewModelStore 实例。
另外,我們可以看一下 mViewModelStore ,销毁的时机,其实是在生命周期的判断Lifecycle.Event.ON_DESTROY 被摧毁并且 判断是否是配置改变引起的isChangingConfigurations()来进行区分:
getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {// Clear out the available contextmContextAwareHelper.clearAvailableContext();// And clear the ViewModelStoreif (!isChangingConfigurations()) {getViewModelStore().clear();}}}});...}

ViewModel 出现之前,Activity 可以使用 onSaveInstanceState() 方法保存,然后从 onCreate() 中的 Bundle 恢复数据,但此方法仅适合可以序列化再反序列化的少量数据(IPC 对 Bundle 有 1M 的限制),而不适合数量可能较大的数据,如用户信息列表或位图。 ViewModel 的出现完美解决这个问题。

LiveData篇

通常情况下LiveData都是配合viewModel使用,在某个具体的ViewModel类中定义LiveData数据,然后在对应的Activity或Fragment中观察LiveData数据的变化,LiveData的使用使得我们不再将数据保存在Activity或Fragment中,减轻了Activity或Fragment的工作量,使得Activity或Fragment只负责界面的管理和显示,而不在保存数据也不会受到数据的影响。但是也可以使用oberveForever()不绑定生命周期, 那就需要自己手动管理它的消亡。
核心方法

通过 observe(owner,observer) 向 LiveData 注册观察者
• 通过 observe(owner,observer) 向 LiveData 注册观察者,并且把 observer 包装成一个 LifecycleBoundObserver,它是一个具有生命周期边界的观察者,因为这个观察者只有当宿主处于 STARTED 或者 RESUMED 状态的它才会接收数据,其他时候它是不会接收数据的。
• 把包装好的 Observer 注册到 Lifecycle 当中,handlerLifecycleEvent(event) 利用 Lifecycle 能力,它能感知宿主生命周期能力的关键地方。注册时和宿主每次生命周期变化都会回调 onStateChanged() 方法,刚进去的时候会触发方法的同步。
• 会判断这个事件宿主是否被销毁了,从而主动地把 Observer 从 LiveData 中移除掉,流程结束。如果不是 DESTORY,说明宿主当前的状态发生了变化,它会触发 activeStateChanged(boolean newActive) 方法,它会判断当前 Observer 是否处于活跃的状态,如果宿主的状态为 STARTED,RESUMED 则会分发最新数据到每个观察者。
• 进而调用 dispatchingValue(ObserverWrapper) 分发数据,如果 ObserverWrapper 为空则分发数据给 liveData 中存储的所有观察者,如果不为空,则分发数据给该 Observer。
• considerNotify(ObserverWrapper) 中先判断观察者所在的宿主不活跃,则不分发;接着如果 observer 的 mLastVersion 大于或等于 LiveData 的 mVersion 则不分发,防止重复发送数据;最后通过 observer.mObserver.onChanged((T) mData) 分发数据,同步 mVersion 数据。
• 那么 LiveData 先发送数据,后注册的 Observer 能接收到数据吗? 答案是可以的。

普通消息的发送
• postValue() 发送一条数据,它可以在任意线程使用的,里面实际使用了 Handler.post 先把这个事件发送到主线程,然后在调用 setValue() 发送数据;
• setValue() 代表着 LiveData 发送数据,每发送一次 mVersion++,另外 LifecycleBoundObserver 中也有一个,它代表这个 Observer 接收了几次数据,在分发数据的时候,这两个 version 会进行比对,防止数据重复发送;
• setValue() 里面也会触发 dispatchingValue(ObserverWrapper),ObserverWrapper 为 null,dispatchingValue() 它会遍历 Observer 集合里面所有观察者,然后逐一调用 considerNotify(ObserverWrapper) 去做消息的分发。

此外,LiveData和 Room配合使用 时候,
LiveData 就是为了简化room 的Dao查询对象到 viewModel的过程, 返回LiveData对象, 查询数据的操作就不需要自己去创建后台线程 。 LiveData能够自动完成。至于为什么Room的查询使用LiveData对象就不需要自己起子线程。 是因为 当liveData做返回对象的时候,room的 查询方法,会多使用一个ComputableLiveData类, 这里面会判断是否有子线程,如果没有,自己会自动起一个子线程去查询,普通类型就会直接走SupportSQLiteDatabase#query(SupportSQLiteQuery)方法,看下面代码:

并且使用LiveData.observe.能够添加观察者, 绑定fragment周期和数据的更新,只要LiveData的数据发生变化, 就会实时刷新:
普通数据类型的查询:

/*** Wrapper for {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.** @param query The Query which includes the SQL and a bind callback for bind arguments.* @return Result of the query.*/public Cursor query(SupportSQLiteQuery query) {assertNotMainThread();return mOpenHelper.getWritableDatabase().query(query);}

使用LiveData作为返回值时用到了ComputableLiveData类,此类在构造的时候就将RoomDatabase中的mQueryExecutor传入了。如果在构造的时候没有传入自定义的Executor,那么会自动生成一个。会走这个方法:

 if (mQueryExecutor == null) {mQueryExecutor = ArchTaskExecutor.getIOThreadExecutor();}

http://www.ppmy.cn/news/1557134.html

相关文章

2024年云计算的发展趋势如何?

2024年云计算的发展趋势 在这个瞬息万变的科技时代&#xff0c;你是否也曾想过&#xff0c;云计算的发展究竟对我们每一个人意味着什么&#xff1f;它不仅是存储和计算能力的提升&#xff0c;更是整个行业的未来构建与转型之道。接下来&#xff0c;我们将一起探索2024年云计算…

Linux创建普通用户和修改主机名

创建修改用户名和用户组 工作组相关命令 功能命令说明切换用户su username注销用户logout新建用户adduser username 创建用户并分配到用户组useradd -g test username 设置用户密码passwd username查看某一用户w username查看登录用户w查看登陆用户并显示IPwho查看登录历史…

重拾设计模式--适配器模式

文章目录 适配器模式&#xff08;Adapter Pattern&#xff09;概述适配器模式UML图适配器模式的结构目标接口&#xff08;Target&#xff09;&#xff1a;适配器&#xff08;Adapter&#xff09;&#xff1a;被适配者&#xff08;Adaptee&#xff09;&#xff1a; 作用&#xf…

课上测试:商用密码标准实现

文章目录 完成下面任务&#xff08;29分&#xff09;1 在 Ubuntu 或 openEuler 中完成任务&#xff08;推荐openEuler&#xff09;2 简述 GM/T0009 4种数据转换的功能&#xff0c;根据你的理解&#xff0c;每种转换功能给出至少一个例子 &#xff08;8分&#xff09;3 参考课程…

EGO Swarm翻译

目录 摘要 Ⅰ 介绍 Ⅱ 相关工作 A . 单四旋翼局部规划 B . 拓扑规划 C. 分布式无人机集群 Ⅲ 基于梯度的局部规划隐式拓扑轨迹生成 A.无需ESDF梯度的局部路径规划 B.隐式拓扑轨迹生成 Ⅳ 无人机集群导航 A 机间避碰 B. 定位漂移补偿 C. 从深度图像中去除agent Ⅴ …

适用于.net的操作excel的库

目录 目录 知识点概要 知识点具体说明(使用步骤、解释) 使用场景及示例 1. EPPlus 2. NPOI 3. ClosedXml 4. SpreadsheetLight 5.ExcelDataReader 开源库 6. ExcelDataWriter 总结 知识点概要 使用Excel来导入和导出数据&#xff0c;在日常工作中使用频率是相当高的&…

3D造型软件solvespace在windows下的编译

3D造型软件solvespace在windows下的编译 在逛开源社区的时候发现了几款开源CAD建模软件&#xff0c;一直囿于没有合适的建模软件&#xff0c;虽然了解了很多的模拟分析软件&#xff0c;却不能使之成为整体的解决方案&#xff0c;从而无法产生价值。opencascad之流虽然可行&…

FPGA设计-使用 lspci 和 setpci 调试xilinx的PCIe 问题

目录 简介 lspci lspci-TV lspci-vvv 注意事项 lspci -vs lspci -vvvs 设置pci 识别setpci中的寄存器 setpci -s 00:01.0 d0.b42 简介 lspci 和 setpci 命令在 Linux 发行版中本身可用。该命令具有各种级别的输出&#xff0c;并提供非常有用的时间点查看 PCI 总线…