Google应用框架实践

相信大家最近应该看过一篇比较火的文章:Google 官方应用架构的最佳实践指南, 本文就是按照官方应用架构写的一个实例Drible, 也是基于我之前写的Dribbble的demo,打算重新写一个Dribbble App。

下面就再总(粘)结(贴)一下新的架构的亮点,以及我个人在使用过程中的体验。

新推出的框架主要是围绕以下两条准则,开发体验更棒的App:

第一条准则:「不要在应用程序组件中保存任何应用数据或状态,并且组件间也不应该相互依赖」。
第二条准则:「通过 model 驱动应用 UI,并尽可能的持久化」。

为了更好的遵循这两条准则, 官方推出的两个组件: Lifecycle 和 Room

Lifecycle

这里官方给我们提供了几种新的结构,一起来看一下:

  1. LiveData : 是一个可被观察的数据持有者,应用了观察者设计模式, 与RxJava很类似, 但LiveData的优势是可以和生命周期进行绑定,他有两个观察函数,如果把生命周期对象LifecycleOwner作为参数,当生命周期结束的时候会自动removeObserve, 防止内存泄漏。下面是两段项目中的代码:

    resultData.observe(lifecycleOwner, new Observer>() {
             @Override
             public void onChanged(@Nullable Resource result) {
                 if (result.status == Resource.SUCCESS) {
                     //手动移除Observer
                     resultData.removeObserver(this);
                     requestSuccess(result.data);
                 } else if (result.status == Resource.ERROR) {
                     resultData.removeObserver(this);
                     requestFailed(result.message);
                 }
             }
         });
    dbLiveData.observeForever(new Observer() {
                 @Override
                 public void onChanged(@Nullable Shot[] shots) {
                     if (shots != null && shots.length > 0) {
                         Log.d("shotRepo", "db shots changed not null");
                         dbLiveData.removeObserver(this);
                         shotsLiveData.setValue(Resource.success(shots));
                     } else {
                         Log.d("shotRepo", "db shots changed null");
                     }
                 }
             });
  2. ViewModel: 这个对象说起来比较复杂,但使用起来非常简单, 以下内容主要来自官网:
    Google设计ViewModel主要是用来存储和管理UI相关的数据, 那他是如何管理数据的呢? 简单的说,就是数据不该被回收的时候,不会被回收; 应该被回收的时候, 数据就会自动被清理掉。
    什么是不该被回收的情况呢? 官方做了一些说明, 例如屏幕旋转, activity被回收重新创建的情况, 虽然系统提供了onSaveInstanceState()可以保存数据, 但是我们肯定碰到过在saveInstanceState()保存过多数据, 结果抛出异常TransactionTooLargeException, 因为官方建议在saveInstanceState中只保存一些状态值,数据不能超过系统约定的值(API25是1Mb)。那这样的情况下,再回到这个页面的时候就只能再次请求网络获取数据了, 用户就会看到Loading界面,体验不佳。
    当然ViewModel自然也不会一直持有数据对象, 当绑定的界面activity被销毁的时候, ViewModel就会调用onCleared清除数据。
    总结一下:ViewModel是一种简单而高效的方法,实现了数据层和界面层之间的分离,保证数据层不受界面层生命周期的影响。
    ps: ViewModel还可以实现不同的Fragment共享同一个ViewModel数据对象(这方面还没实践,不做深入展开)
    附一张官方的ViewModel生命周期图:


Room

官方描述是“a SQLite object mapping library”, 是一个数据库和java中的对象映射的组件,我们来看下面这段代码就明白了:

@Entity(tableName = "shots")
public class Shot implements Parcelable {

    @PrimaryKey
    private int id;
    private String title;
    private String description;
    private int width;
    private int height;
    private int page;
}

以上只是在原来bean对象的基础上加了几个注解, 我们就可以生成对应的数据库的一张表,里面的字段就对应表中的字段,同时也通过注解的方式定义主键,外键等。
第二步, 我们需要定义一些数据库操作的方法, 直接定义一个抽象类注解方法的sql语句即可:

@Dao
public interface ShotDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertAll(Shot[] shots);
    @Query("select * from shots where shots.page = :pageId order by shots.ind asc")
    LiveData loadShots(int pageId);
    @Query("select * from shots where shots.page = :pageId order by shots.ind asc")
    Shot[] loadShotSync(int pageId);
    @Query("delete from shots where shots.page = :pageId")
    void deleteShots(int pageId);
}

最后,声明数据库的名称,以及数据库包含的表, 并且我们在程序启动的时候,需要调用方法创建一个数据库:

@Database(entities = {Shot.class}, version = 1)
@TypeConverters(DribleConverter.class)
public abstract class DribleDatabase extends RoomDatabase {
    static final String DATABASE_NAME = "drible_db";
    public abstract ShotDao shotDao();
}
//
DribleDatabase db = Room.databaseBuilder(context.getApplicationContext(),
                        DribleDatabase.class, DATABASE_NAME).build();

我们项目中肯定也会用到缓存,但是我之前接触的缓存总存在一些问题, 例如网络缓存,只能针对一些数据不怎么变化的内容,而那些用户操作后的数据,再使用网络缓存,则会造成错误,例如收藏了一个商品,那么再展现商品的时候,收藏图标应该被点亮。

而Room组件这样把所有的数据字段都映射到数据库中,如果用户收藏了商品,那么直接更新数据库的这条内容的这个字段值, 修改数据库即可。

另外Room数据库返回的数据还支持LiveData, 完美的和界面绑定,当数据库内容发生变化的时候,界面也会相应变化,做到通过model驱动UI变化。

最后,说说我的实践项目, 地址如下:
https://github.com/binqiangsun/Drible

Dribbble 是一个面向创作家、艺术工作者、设计师等创意类作品的人群,提供作品在线服务,供网友在线查看已经完成的作品或者正在创作的作品的交流网站。
这个项目就是根据官方提供的Api接口实现的APP

  1. 目前已经按照应用架构实现了首页流的展示,但是由于首页流是分页的, 所以还是存在一点问题, 我在github中都有记录;
  2. 项目中有一个service module, 这个是我专门分层出来的封装一些基础功能的module, 例如Retrofit的封装, recycler adapter的基类等;
  3. 我会持续按照架构的思想完成整个App

你可能感兴趣的:(Android开发)