Google应用框架实践

作者: sunbinqiang | 来源:发表于2017-06-08 23:34 被阅读744次

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

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

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

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

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

    Lifecycle

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

    1. LiveData : 是一个可被观察的数据持有者,应用了观察者设计模式, 与RxJava很类似, 但LiveData的优势是可以和生命周期进行绑定,他有两个观察函数,如果把生命周期对象LifecycleOwner作为参数,当生命周期结束的时候会自动removeObserve, 防止内存泄漏。下面是两段项目中的代码:
    resultData.observe(lifecycleOwner, new Observer<Resource<T[]>>() {
                @Override
                public void onChanged(@Nullable Resource<T[]> 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<Shot[]>() {
                    @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");
                        }
                    }
                });
    
    1. 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<Shot[]> 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<T>, 完美的和界面绑定,当数据库内容发生变化的时候,界面也会相应变化,做到通过model驱动UI变化。

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

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

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


    相关文章

      网友评论

        本文标题:Google应用框架实践

        本文链接:https://www.haomeiwen.com/subject/gdcxqxtx.html