原文连贯:https://mp.weixin.qq.com/s/V2haCRugRYCGDZrA9iw7bQ
前言
==本次次要解说的内容:==
1、Jetpack介绍
2、Jetpack架构组件库的相干用法
一、Jetpack 介绍
1、什么是Jetpack
Google 官网解释:
Jetpack 是一个由多个库组成的套件,可帮忙开发者遵循最佳做法,缩小样板代码并编写可在各种 Android 版本和设施中统一运行的代码,让开发者精力集中编写重要的代码。
Jetpack 是 Google 为解决 Android 开发碎片化,打造成熟衰弱生态圈提出的战略规划,是 Google 对 Android 将来提出的倒退方向,同时它也是泛滥优良 ++Android 组件的汇合++。
2、为何应用 Android Jetpack
Google 官网解释:
- 遵循最佳做法:Android Jetpack 组件采纳最新的设计办法构建,具备向后兼容性,能够缩小解体和内存泄露。
- 打消样板代码:Android Jetpack 能够治理各种繁琐的 Activity(如后台任务、导航和生命周期治理),以便您能够专一于打造杰出的利用。
- 缩小不统一:这些库可在各种 Android 版本和设施中以统一的形式运作,助您升高复杂性。
Jetpack 的劣势:
- [ ] Jetpack 领有基于生命周期感知的能力,能够缩小 NPE(空指针异样) 解体、内存透露,为开发出强壮且晦涩的程序提供强力保障;
- [ ] Jetpack 能够打消大量反复样板式的代码,能够减速 Android 的开发过程,组件可搭配工作,也可独自应用,同时配合 Kotlin 语言个性可能显著进步工作效率;
- [ ] 对立开发模式,摈弃传统的 MVC, MVP;
3、JetPack 的形成
如上图:Jetpack 次要包含 4 个局部,别离是【Architecture:架构】、【UI:界面】、【behavior:行为】和【foundation:根底】。
Architecture,架构组件
目标:帮忙开发者设计持重、可测试且易保护的利用;
- Lifecycle:具备宿主生命周期感知能力的组件
个性:持有组件(如 Activity 或 Fragment)生命周期状态的信息,并且容许其余对象察看此状态;
- LiveData:新一代具备生命周期感知能力的数据订阅、散发组件
个性:反对共享资源、反对黏性事件的散发、不再须要手动解决生命周期、确保界面合乎数据状态;
- ViewModel:具备生命周期感知能力的数据存储组件
个性:页面因配置变更导致的重启,此时数据不失落;能够实现跨页面(跨 Activity)的数据共享;
- SavedState 架构组件原理解析
个性:因内存不足,电量有余导致页面被回收时能够搭配 ViewModel 实现数据存储与复原;
- Room:轻量级 orm 数据库,实质上是一个 SQLite 形象层
个性:应用简略(相似于 Retrofit 库),通过注解的形式实现相干性能,编译时主动生成相干实现类
- DataBinding:只是一种工具,解决的是 View 和数据之间的双向绑定
个性:反对数据与视图双向绑定、数据绑定空平安、缩小模板代码、开释 Activity/Fragment 压力;
- Paging: 列表分页组件,能够轻松实现分页预加载以达到有限滑动的成果
个性:奇妙交融 LiveData、提供多种数据源加载形式;
不足之处:不反对列表数据增删改,列表增加 HeaderView,FooterView 定位不精确;
- Navigation 组件原理剖析:端内对立路由组件
个性:可能为 Activity,Fragment,Dialog,FloatWindow 提供对立的路由导航服务,能够传递参数,指定导航动画,还反对深度链接等次要能力;
有余:非常依赖 xml 配置文件不利于组件化,模块化
- WorkManager:新一代后台任务治理组件,service 能做的事件它都能做
个性:反对周期性任务调度、链式任务调度、丰盛的工作约束条件、程序即使退出,仍旧能保障工作的执行;
Foundationy,根底组件
目标:提供横向性能,例如向后兼容性、测试、平安、Kotlin 语言反对,并包含多个平台开发的组件;
- Android KTX:优化了供 Kotlin 应用的 Jetpack 和 Android 平台 API,帮忙开发者以更简洁、更愉悦、更习用的形式应用 Kotlin 进行 Android 开发;
- AppCompat:帮忙较低版本的 Android 零碎进行兼容;
- Auto:开发 Android Auto 利用的组件,提供了实用于所有车辆的标准化界面和用户交互;
- 检测:从 AndroidStudio 中疾速检测基于 Kotlin 或 Java 的代码;
- 多 Dex 解决:为具备多个 Dex 文件利用提供反对;
- 平安:平安的读写加密文件和共享偏好设置;
- 测试:用于单元和运行时界面测试的 Android 测试框架;
- TV:构建可让用户在大屏幕上体验沉迷式内容的利用;
- Wear OS:开发 Wear 利用的组件;
Behavior,行为组件
目标:帮忙开发者的利用与规范 Android 服务(如告诉、权限、分享)相集成;
- CameraX:帮忙开发简化相机利用的开发工作,提供统一且易于应用的界面,实用于大多数 Android 设施,并可向后兼容至 Android 5.0(API 21);
- DownloadManager:解决长时间运行的 HTTP 下载的零碎服务;
- 媒体和播放:用于媒体播放和路由(包含 Google Cast)的向后兼容 API;
- 告诉:提供向后兼容的告诉 API,反对 Wear 和 Auto;
- 权限:用于检查和申请利用权限的兼容性 API;
- 设置:创立交互式设置,倡议应用 AndroidX Preference Library 库将用户可配置设置集成到利用中;
- 分享操作:能够更轻松地实现敌对的用户分享操作;
- 切片:切片是一种 UI 模板,创立可在利用内部显示利用数据的灵便界面元素;
UI,界面组件
- Animation and Transition:该框架蕴含用于常见成果的内置动画,并容许开发者创立自定义动画和生命周期回调;
- Emoji Compatibility:即使用户没有更新 Android 零碎也能够获取最新的表情符号;
- Fragment:组件化界面的根本单位;
- 布局:用 XML 中申明 UI 元素或者在代码中实例化 UI 元素;
- 调色板:从调色板中提取出有用的信息;
留神:
下面的图之前在官网是存在的,当初官网曾经找不到此图,只有jetpack架构组件的文档入口;
猜测google官网也是旨在强化Architecture架构组件,因为UI、Behavior、Foundation 这三局部大多是对已有内容的收集整理。
所以接下来的关注点次要是在Architecture 架构局部
4、Jetpack 与 AndroidX
参考官网Jetpack应用
1、Jetpack 库能够独自应用,也能够组合应用,以满足利用的不同需要。
2、Jetpack 库在 androidx 命名空间中公布。
androidx介绍:
==androidx 命名空间中的工件蕴含 Android Jetpack 库。与反对库一样,androidx 命名空间中的库与 Android 平台离开提供,并向后兼容各个 Android 版本。==
==AndroidX 对原始 Android 反对库进行了重大改良,后者当初已不再保护。androidx 软件包齐全取代了反对库,不仅提供等同的性能,而且提供了新的库。==
AndroidX 还包含以下性能:
++AndroidX 中的所有软件包都应用统一的命名空间,以字符串 androidx 结尾。反对库软件包已映射到对应的 androidx.* 软件包。无关所有旧类到新类以及旧构建工件到新构建工件的残缺映射,请参阅软件包重构页面。++
++与反对库不同,androidx 软件包会独自保护和更新。从版本 1.0.0 开始,androidx 软件包应用严格的语义版本控制。您能够独自更新我的项目中的各个 AndroidX 库。++
++版本 28.0.0 是反对库的最初一个版本。咱们将不再公布 android.support 库版本。所有新性能都将在 androidx 命名空间中开发。++
所以,目前咱们的我的项目【朴新网校】、【朴新助教】、【数学宝典】都曾经迁徙应用了androidx库。
androidx迁徙参照官网
留神:Jetpack 是各种组件库的统称,AndroidX 是这些组件的对立包名。
二、Jetpack架构组件库
1、利用架构指南
官网举荐利用架构:
在我的项目中使用到的框架的搭建根本基于改图,使用到的jetpack组件库有ViewModel、LiveData、DataBinding;接下来逐个介绍他们的个性和用法。
2、ViewModel & LiveData
ViewModel 类旨在以重视生命周期的形式存储和治理界面相干的数据。ViewModel 类让数据可在产生屏幕旋转等配置更改后持续留存。
LiveData 是一种可察看的数据存储器类。与惯例的可察看类不同,LiveData 具备生命周期感知能力,意指它遵循其余利用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于沉闷生命周期状态的利用组件观察者。
LiveData , ViewModel 组件都是基于Lifecycle来实现的,LiveData&ViewModel也是联合应用的,接下来具体看下如何应用
申明依赖项
dependencies { def lifecycle_version = "2.2.0" // ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" // LiveData implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" }
实现 ViewModel
架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面筹备数据。在配置更改期间会主动保留 ViewModel 对象,以便它们存储的数据立刻可供下一个 Activity 或 Fragment 实例应用。例如,如果您须要在利用中显示用户列表,请确保将获取和保留该用户列表的责任调配给 ViewModel,而不是 Activity 或 Fragment,如以下示例代码所示:
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
而后,您能够从 Activity 拜访该列表,如下所示:
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } }
ViewModel在 Fragment 之间共享数据
应用 ViewModel 对象解决这一常见的难点。这两个 Fragment 能够应用其 Activity 范畴共享 ViewModel 来解决此类通信
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData<Item> getSelected() { return selected; } } //获取 ViewModelProvider 时,会收到雷同的 SharedViewModel 实例(其范畴限定为该 Activity) public class MasterFragment extends Fragment { private SharedViewModel model; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); //通过requireActivity() Fragment 会检索蕴含它们的 Activity model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } //获取 ViewModelProvider 时,会收到雷同的 SharedViewModel 实例(其范畴限定为该 Activity) public class DetailFragment extends Fragment { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); //通过requireActivity() Fragment 会检索蕴含它们的 Activity SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); model.getSelected().observe(getViewLifecycleOwner(), { item -> // Update the UI. }); } }
该办法的劣势:
- Activity 不须要执行任何操作,也不须要对此通信有任何理解。
- 除了 SharedViewModel 约定之外,Fragment 不须要相互了解。如果其中一个 Fragment 隐没,另一个 Fragment 将持续照常工作。
- 每个 Fragment 都有本人的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将持续工作而没有任何问题。
应用 LiveData 对象
官网应用参考
1、创立 LiveData 对象
LiveData 是一种可用于任何数据的封装容器,其中包含可实现 Collections 的对象,如 List。LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 办法进行拜访,如以下示例中所示:
public class NameViewModel extends ViewModel { // Create a LiveData with a String private MutableLiveData<String> currentName; public MutableLiveData<String> getCurrentName() { if (currentName == null) { currentName = new MutableLiveData<String>(); } return currentName; } // Rest of the ViewModel... }
最后,LiveData 对象中的数据并未通过设置。
留神:请确保用于更新界面的 LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,起因如下: 1、防止 Activity 和 Fragment 过于宏大。当初,这些界面控制器负责显示数据,但不负责存储数据状态。 2、将 LiveData 实例与特定的 Activity 或 Fragment 实例分来到,并使 LiveData 对象在配置更改后持续存在。
2、察看 LiveData 对象
在大多数状况下,在利用组件的 onCreate() 办法是开始察看 LiveData 对象起因如下:
1、确保零碎不会从 Activity 或 Fragment 的 onResume() 办法进行冗余调用。
2、确保 Activity 或 Fragment 变为沉闷状态后具备能够立刻显示的数据。一旦利用组件处于 STARTED 状态,就会从它正在察看的 LiveData 对象接管最新值。只有在设置了要察看的 LiveData 对象时,才会产生这种状况。
LiveData 仅在数据产生更改时才发送更新,并且仅发送给沉闷观察者。此行为的一种例外情况是,观察者从非沉闷状态更改为沉闷状态时也会收到更新。此外,如果观察者第二次从非沉闷状态更改为沉闷状态,则只有在自上次变为沉闷状态以来值产生了更改时,它才会收到更新。
以下示例代码阐明了如何开始察看 LiveData 对象:
public class NameActivity extends AppCompatActivity { private NameViewModel model; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Other code to setup the activity... // Get the ViewModel. model = new ViewModelProvider(this).get(NameViewModel.class); // Create the observer which updates the UI. final Observer<String> nameObserver = new Observer<String>() { @Override public void onChanged(@Nullable final String newName) { // Update the UI, in this case, a TextView. nameTextView.setText(newName); } }; // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. model.getCurrentName().observe(this, nameObserver); } }
3、更新 LiveData 对象
LiveData 没有公开可用的办法来更新存储的数据。MutableLiveData 类将公开 setValue(T) 和 postValue(T) 办法,如果您须要批改存储在 LiveData 对象中的值,则必须应用这些办法。通常状况下会在 ViewModel 中应用 MutableLiveData,而后 ViewModel 只会向观察者公开不可变的 LiveData 对象。
设置观察者关系后,您能够更新 LiveData 对象的值(如以下示例中所示),这样当用户点按某个按钮时会触发所有观察者:
button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String anotherName = "John Doe"; model.getCurrentName().setValue(anotherName); } });
留神: 您必须调用 setValue(T) 办法以从主线程更新 LiveData 对象。 如果在 worker 线程中执行代码,则您能够改用 postValue(T) 办法来更新 LiveData 对象。
3、Data Binding
Data Binding是一种反对库,借助该库,您能够应用申明性情式(而非程序化地)将布局中的界面组件绑定到利用中的数据源。
布局通常是应用调用界面框架办法的代码在 Activity 中定义的。例如,以下代码调用 findViewById() 来查找 TextView 微件并将其绑定到 viewModel 变量的 userName 属性:
TextView textView = findViewById(R.id.sample_text); textView.setText(viewModel.getUserName());
以下示例展现了如何在布局文件中应用Data Binding 将文本间接调配到TextView。这样就无需调用上述任何 Java 代码。请留神赋值表达式中 @{} 语法的应用:
<TextView android:text="@{viewmodel.userName}" />
留神:
如果您应用Data Biding的次要目标是取代 findViewById() 调用,请思考改用ViewBinding。
应用过ButterKnife的都晓得,目前ButterKnife作者倡议切换至ViewBindng应用;在许多状况下,ViewBinding可简化实现,进步性能,提供与DataBinding雷同的益处。
Data Binding应用场景:
布局和绑定表达式
数据绑定的布局以根标记 layout 结尾,后跟 data 元素和 view 根元素。如下:
留神:布局表达式应放弃精简,因为它们无奈进行单元测试,并且领有的 IDE 反对也无限。为了简化布局表达式,能够应用自定义绑定适配器。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <!--data 中的 user 变量形容了可在此布局中应用的属性。 --> <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>
==零碎会为每个布局文件生成一个绑定类==。
1、默认状况下,类名称基于布局文件的名称,它会转换为驼峰模式并在开端增加 Binding 后缀。
2、以上布局文件名为 activity_main.xml,因而生成的对应类为 ActivityMainBinding,==且都是ViewDataBinding的子类,所有布局对应的生成的绑定类都能够是ViewDataBinding类==
3、此类蕴含从布局属性(例如,user 变量)到布局视图的所有绑定,并且晓得如何为绑定表达式指定值。
4、倡议的绑定创立办法是在裁减布局时创立,如以下示例所示:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); binding.setUser(user); }
a、Activity 数据绑定 ( DataBinding ) :
1、DataBindingUtil类办法:
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
2、生成的布局绑定类的inflate()办法:
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //DataBindingUtil类办法 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); //生成的布局绑定类的inflate()办法 //ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); User user = new User("Test", "User"); binding.setUser(user); }
b、 Fragment、ListView 或 RecyclerView 适配器中应用数据绑定 ( DataBinding )
DataBindingUtil 或 生成的布局绑定类deinflate() 办法,如以下代码示例所示:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); // or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Data Binding绑定表达式:
xml里反对应用以下表达式:
- 算术运算符 + – / * %
- 字符串连贯运算符 +
- 逻辑运算符 && ||
- 二元运算符 & | ^
- 一元运算符 + – ! ~
- 移位运算符 >> >>> <<
- 比拟运算符 == > < >= <=(请留神,< 须要本义为 <)
- instanceof
- 分组运算符 ()
- 字面量运算符 – 字符、字符串、数字、null
- 类型转换
- 办法调用
- 字段拜访
- 数组拜访 []
- 三元运算符 ?:
不反对以下表达式:
- this
- super
- new
- 显式泛型调用
a、变量
1、生成的数据绑定代码会主动查看有没有 null 值并避免出现 Null 指针异样。
2、例如,在表达式 @{user.name} 中,如果 user 为 Null,则为 user.name 调配默认值 null。
3、如果您援用 user.age,其中 age 的类型为 int,则数据绑定应用默认值 0。
<!--变量给控件赋值--> android:text="@{user.name}" <!--控件给变量赋值(双向绑定)--> android:text="@={user.name}"
b、Null 合并运算符(空运算符)
如果右边运算数不是 null,则 Null 合并运算符 (??) 抉择右边运算数,如果右边运算数为 null,则抉择左边运算数。
android:text="@{user.displayName ?? user.lastName}" //等效于如下三目表达式 android:text="@{user.displayName != null ? user.displayName : user.lastName}"
c、视图援用
1、表达式能够通过以下语法按 ID 援用布局中的其余视图:
2、绑定类将 ID 转换为驼峰式大小写。
3、在以下示例中,TextView 视图援用同一布局中的 EditText 视图:android:text=”@{exampleText.text}”
<EditText android:id="@+id/example_text" android:layout_height="wrap_content" android:layout_width="match_parent"/> <TextView android:id="@+id/example_output" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{exampleText.text}"/>
d、显示暗藏管制
1、首先在 xml 的 data 节点中援用View
2、而后设置visibility
<data> <import type="android.view.View"/> </data> android:visibility="@{student.boy ? View.VISIBLE : View.INVISIBLE}"
e、事件处理
办法援用:
==android:onClick=”@{handlers::onClickFriend}”==
绑定表达式可将视图的点击监听器调配给MyHandlers 类的 onClickFriend() 办法,如下所示:
public class MyHandlers { public void onClickFriend(View view) { ... } }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="com.example.MyHandlers"/> <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}" android:onClick="@{handlers::onClickFriend}"/> </LinearLayout> </layout>
留神:
1、在表达式中,您能够援用合乎监听器办法签名的办法。
2、当表达式求值后果为办法援用时,数据绑定会将办法援用和所有者对象封装到监听器中,并在指标视图上设置该监听器。
3、如果表达式的求值后果为 null,则数据绑定不会创立监听器,而是设置 null 监听器。
4、表达式中的办法签名必须与监听器对象中的办法签名完全一致。
监听器绑定:
==android:onClick=”@{() -> presenter.onSaveClick(task)}”==
绑定表达式可将视图的点击事件绑定打给Presenter 类的 onSaveClick(Task task) 办法,如下所示:
public class Presenter { public void onSaveClick(Task task){} }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="task" type="com.android.example.Task" /> <variable name="presenter" type="com.android.example.Presenter" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.onSaveClick(task)}" /> </LinearLayout> </layout>
以上,咱们尚未定义传递给 onClick(View) 的 view 参数。
监听器绑定提供两个监听器参数选项:您能够疏忽办法的所有参数,也能够命名所有参数。
如果您想命名参数,则能够在表达式中应用这些参数。
例如,下面的表达式能够写成如下模式:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
或者,如果您想在表达式中应用参数,则采纳如下模式:
public class Presenter { public void onSaveClick(View view, Task task){} }
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
监听长按事件,表达式应返回一个布尔值。
public class Presenter { public boolean onLongClick(View view, Task task) { } }
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
留神:
1、监听器绑定这些是在事件产生时进行求值的 lambda 表达式。
2、数据绑定始终会创立一个要在视图上设置的监听器。
3、事件被分派后,监听器会对 lambda 表达式进行求值。
< END >
【Android进化之路】
微信扫描二维码,关注我的公众号。