Jetpack系列(二、架构)

一、数据绑定库

数据绑定库是一种支持库,可以使用声明格式将布局中的界面组件绑定在应用的数据源。
布局通常使用该框架方法的代码在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);



你可能感兴趣的:(Jetpack系列(二、架构))