一、数据绑定库
数据绑定库是一种支持库,可以使用声明格式将布局中的界面组件绑定在应用的数据源。
布局通常使用该框架方法的代码在Activity中定义;
例如:在一下代码调用findViewByid()来查找TextView空间并将其绑定到viewModel变量的userNama属性:
对TextView赋值
//普通方式
findViewById(R.id.sample_text).apply {
text = viewModel.userName
}
//数据绑定方式
1.1布局和绑定表达式
1.2使用可观察的数据对象
数据绑定库提供了观察数据更改情况的类和方法,不必操心底层数据源发生改变时刷新界面。你可以将变量或者属性设为可观察
可观察字段:ObservableBoolean、ObservableByte、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble、ObservableParcelable
可观察的集合:ObservableArrayMap、ObservableArrayList
可观察的对象:
实现了Observable
接口的类允许注册一个监听器,当可观察对象的属性更改时通知这个监听器。
DataBinding库提供了BaseObservable
类,它实现了监听器注册机制。继承了BaseObservable的数据类负责通知属性何时更改。
private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
1.3生成的绑定类
数据绑定卡可以生成用于访问布局变量和View视图的Binding类,DataBing库会为每个布局文件生成一个binding类,生成一个binding类将布局中的View与变量连接起来;
该类的名称基于布局文件的名称,将布局名称装换为pascal格式并向其添加Binding后缀,例如activity_main.layout
相应生成MainActivityBinding
类。
有时候预先不知道绑定类型,在这种情况下,可以使用DataBindingUtil类创建绑定;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//方式一
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
//方式二
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
//方式三(最常用)
T createDataBinding = DataBindingUtil.setContentView(this, layoutId)
}
1.4绑定适配器
每个布局表达式都会与对应的适配器绑定,要求必须进行框架调用来设置对应的属性监听器;例如setText()设置文本属性,或者setOnClickListener()设置点击事件监听属性。
自定义适配器,例如
举例一:设置View隐藏显示
@BindingAdapter("app:goneUnless")
public static void goneUnless(View view, Boolean visible) {
view.visibility = visible ? View.VISIBLE : View.GONE;
}
举例二:设置ImageView加载图片
//代码中
@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
//布局中调用
1.5将布局视图绑定到架构组件
AndoridX库中包含了架构组件,DataBinding可以与架构组件无缝协作,进一步简化UI的开发;
1.5.1使用LiveData通知UI有关数据更新
与上面提到的Observable观察者对象不同,LiveData对象知道订阅数据更改的观察者的生命周期。
在使用LiveData对象前,需要指定生命周期所有者来定义LiveData对象的范围,下面以Activity作为生命周期所有者为例:
class ViewModelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Inflate view and obtain an instance of the binding class.
UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);
// Specify the current activity as the lifecycle owner.
binding.setLifecycleOwner(this);
}
}
我们可以使用ViewModel组件将数据绑定到布局,在ViewModel组件中,我们可以使用LiveData对象来转换数据或合并多个数据源;
class ScheduleViewModel extends ViewModel {
LiveData username;
public ScheduleViewModel() {
String result = Repository.userName;
userName = Transformations.map(result, result -> result.value);
}
1.5.2使用ViewModel管理与UI相关数据
使用ViewModel库可以将UI逻辑移出布局并放入易于测试的组件中,确保View在需要从数据源绑定和解绑;
class ViewModelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// Obtain the ViewModel component.
UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
// Inflate view and obtain an instance of the binding class.
UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);
// Assign the component to a property in the binding class.
binding.viewmodel = userModel;
}
}
二、Lifecycle+LiveData+ViewModel库
Lifecycle:生命周期感知组件可执行操作来响应另一个组件(Activity或Fragment)的生命周期状态的变化。
LiveData:在底层数据库更改时通知视图;
ViewModel:以注重生命周期的方式管理界面相关数据;
Lifecycle
是一个类,用于存储有个组件(Activity、Fragment)的生命周期状态信息,并允许其他对象观察此状态;
2.1通过注解监控组件生命周期
你可以桐楠格Lifeceycle类的addObserver()方法并专递观察者的实例来添加观察者;
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void connectListener() {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void disconnectListener() {
...
}
}
myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
Lifecycles+LiveData+ViewModel 在项目中的组合使用代码案例 https://codelabs.developers.google.com/codelabs/android-lifecycles/index.html?index=..%2F..%2Findex#0
三、Navigation导航
支持用户导航、进入、退出应用中不同内容片断的交互,导航组件主要由三部分组成:
- 导航图:在一个集中位置包含所有导航相关信息XML资源,应用内单个内容区域以及用户可以通过应用获取的可能路劲;
- NavHost:显示导航图中目标的空白容器,导航组件包含一个默认的NavHost实现(NavHostFragment),可显示Fragment目标;
- NavController:在NavHost中管理应用导航的对象,当用户在整个应用中移动时,NavController会安排NavHost中的目标内容交换;
优势:
- 处理Fragment事务;
- ViewModel支持,在范围内先订导航图,在图表目标之间共享与界面相关的数据;
- 实现处理深层链接;
- 默认情况下,正确处理往返操作;
官方代码实现流程:https://codelabs.developers.google.com/codelabs/android-navigation/index.html?index=..%2F..%2Findex#0
四、Paging分页组件
分页库支持当加载大数据时,一次加载或显示一小块数据,按需载入部分数据减少网络带宽和系统资源使用量;减少内存资源的消耗
PagedList类
,用于加载应用数据块或页面,随着所需数据的增加,系统会将分页到现有的PagedList对象中,如果任何已加载的数据发生改变,会冲LiveData或RXJava2
的对象向可观察数据存储发出一个新的PagedList实例。随着PagedList对象的生成,应用界面会呈现其内容,同时还会考虑界面控件的生命周期;
下面使用PagedList与LiveData存储加载和显示数据:
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData> concertList;
// Creates a PagedList object with 50 items per page.
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), 50).build();
}
}
每个PageList实例对象都对应DataSource对象加载应用数据的最新快照。数据从后端或者数据库流向PagedList对象;
使用 LiveData 观察分页数据+Room持久性库:
@Dao
public interface ConcertDao {
// The Integer type parameter tells Room to use a PositionalDataSource
// object, with position-based loading under the hood.
@Query("SELECT * FROM concerts ORDER BY date DESC")
DataSource.Factory concertsByDate();
}
public class ConcertViewModel extends ViewModel {
private ConcertDao concertDao;
public final LiveData> concertList;
public ConcertViewModel(ConcertDao concertDao) {
this.concertDao = concertDao;
concertList = new LivePagedListBuilder<>(
concertDao.concertsByDate(), /* page size */ 50).build();
}
}
public class ConcertActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConcertViewModel viewModel =
new ViewModelProvider(this).get(ConcertViewModel.class);
RecyclerView recyclerView = findViewById(R.id.concert_list);
ConcertAdapter adapter = new ConcertAdapter();
viewModel.concertList.observe(this, adapter::submitList);
recyclerView.setAdapter(adapter);
}
}
public class ConcertAdapter
extends PagedListAdapter {
protected ConcertAdapter() {
super(DIFF_CALLBACK);
}
@Override
public void onBindViewHolder(@NonNull ConcertViewHolder holder,
int position) {
Concert concert = getItem(position);
if (concert != null) {
holder.bindTo(concert);
} else {
// Null defines a placeholder item - PagedListAdapter automatically
// invalidates this row when the actual object is loaded from the
// database.
holder.clear();
}
}
private static DiffUtil.ItemCallback DIFF_CALLBACK =
new DiffUtil.ItemCallback() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
@Override
public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
return oldConcert.getId() == newConcert.getId();
}
@Override
public boolean areContentsTheSame(Concert oldConcert,
Concert newConcert) {
return oldConcert.equals(newConcert);
}
};
}
官方代码流程:https://codelabs.developers.google.com/codelabs/android-paging/index.html?index=..%2F..%2Findex#0
五、Room持久性库
基于SQLite基础上提供的一个抽象层,让用户利用SQLiter强大功能同事获取更强健的数据库访问机制。Room包含三个主要组件:
- 数据库:数据库持有者,用于保留持久关系型数据的底层连接;
- Entity:数据库中的表;
- DAO:访问数据库的方法;
工作流程:使用Room数据库来获取与该数据库关联的数据访问对象DAO,然后,应用使用每个DAO对象从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中,最后,应用使用实体获取和设置与数据库中的表列相对应的值;
下面是代码举例:
//实体对象User
@Entity
public class User {
@PrimaryKey
public int uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
}
//对应的DAO数据库-----UesrDao
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
//AppDatabase
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
//使用一下代码获取已创建的数据库的实例:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
官方提供代码:https://github.com/android/architecture-components-samples
六:WorkManager任务调度
用处:可以清楚调度,即时在应用退出或设备重启时,仍应运行的可延迟异步任务。
应用场景:
- 向后端服务器上传日志和分析数据(应用崩溃时);
- 定期将应用数据与服务器同步
不适用场景:
- 应用进程结束时能够安全终止的运行中后台工作;
- 需要立即执行的任务;
代码应用:
6.1创建后台任务
任务使用Worker类定义,doWork()方法在WorkManager提供的后台线程上同步运行;
doWork()返回的Result会通知WorkManager任务;
- 已完成:Result.success();
- 已失败:Result.failure();
- 需要稍后重试:Result.retry();
//上次图像的Worker
public class UploadWorker extends Worker {
public UploadWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
// Do the work here--in this case, upload the images.
uploadImages()
// Indicate whether the task finished successfully with the Result
return Result.success()
}
}
6.2可以配置运行任务的方式和时间
Work定义是工作单元,WorkRequest定义工作运行方式和时间。任务可以是一次性或者周期性的;
一次性使用OneTimeWorkRequest
,周期性使用:PeriodicWorkRequest
示例:
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
.build()
``
#####6.3提交任务给系统
WorkManager.getInstance(myContext).enqueue(uploadWorkRequest);