Android Architecture Components

Android Architecture Components是谷歌在Google I/O 2017发布的一套帮助开发者解决安卓架构设计的方案。里面包含两大块内容:

  • 生命周期相关的Lifecycle-aware Components
  • 数据库解决方案Room

Lifecycle

简介

Lifecycle可以让开发者构建能够感知其他组件(主要指Activity、Fragment)生命周期的类。LifeCycle使用两个主要的枚举类来跟踪它所关联组件的生命周期:

  • Event事件:从组件或者Lifecycle类分发出来的生命周期,它们和Activity/Fragment生命周期的事件一一对应。(ON_CREATE,ON_START,ON_RESUME,ON_PAUSE,ON_STOP,ON_DESTROY)。
  • State状态:当前组件的生命周期状态(INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED)。
使用

那么,如何构建可以感知其他组件生命周期的类呢?Lifecycle通过LifecycleObserver接口创建感知类,即将要感知生命周期的类实现LifecycleObserver接口,并用注解标注方法对应的生命周期即可,代码如下:

public class LifecycleObserverTest implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {
        Log.e("tag", "==========ON_CREATE===========");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onStart() {
        Log.e("tag", "==========ON_START===========");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
        Log.e("tag", "==========ON_RESUME===========");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
        Log.e("tag", "==========ON_PAUSE===========");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {
        Log.e("tag", "==========ON_STOP===========");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        Log.e("tag", "==========ON_DESTROY===========");
    }
}

那么,感知类又是如何与生命周期类相关联的呢?生命周期类需要实现LifecycleOwner接口,并且通过LifecycleRegistry对象将LifecycleObserver与LifecycleOwner关联起来,代码如下:

public class LifecycleTestActivity extends AppCompatActivity implements LifecycleOwner {
    private LifecycleRegistry registry;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        registry = new LifecycleRegistry(this);
        registry.addObserver(new LifecycleObserverTest());
    }

    @Override
    public Lifecycle getLifecycle() {
        return registry;
    }
}

这样就将感知类与生命周期类关联起来了,感知类就可以在对应生命周期的方法中做相关操作了,上述代码的打印结果如下:

2019-04-04 14:29:13.371 27011-27011/com.example.study E/tag: ==========ON_CREATE===========
2019-04-04 14:29:13.413 27011-27011/com.example.study E/tag: ==========ON_START===========
2019-04-04 14:29:13.469 27011-27011/com.example.study E/tag: ==========ON_RESUME===========
2019-04-04 14:30:43.650 27011-27011/com.example.study E/tag: ==========ON_PAUSE===========
2019-04-04 14:30:43.946 27011-27011/com.example.study E/tag: ==========ON_STOP===========
2019-04-04 14:30:43.947 27011-27011/com.example.study E/tag: ==========ON_DESTROY===========

LifecycleRegistry也可以移除感知类与生命周期类间的关联,可通过它的removeObserver(LifecycleOnserver l)方法实现。

ViewModel&LiveData

简介

ViewModel的设计目的就是存放和处理UI相关的数据,并且这些数据不受配置变化(如:屏幕旋转、组件被系统回收等)的影响。ViewModel大多数情况下是与LiveData配合使用的。LiveData是一种持有可被观察数据的类。和其他可被观察的类不同的是,LiveData是有生命周期感知能力的,这意味着它可以在activity,fragment或者service生命周期是活跃状态时更新这些组件。

使用

LiveData通常是放在ViewModel里面使用的,代码如下:

public class TestViewModel extends ViewModel {
    private MutableLiveData userLiveData;

    public MutableLiveData getUser() {
        if (userLiveData == null) {
            userLiveData = new MutableLiveData<>();
            User user = new User();
            user.setName("Jone");
            user.setAge(20);
            user.setSex("male");
            userLiveData.setValue(user);
        }
        return userLiveData;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
    }
}

MutableLiveData是LiveData的子类,它提供了setValue()和postValue()的方法修改存储在LiveData中的数据,两个方法的区别在于setValue()只能在主线程中调用,而postValue()可以在子线程中调用。那么,如何通过LiveData更新UI中的信息呢?如上代码中的User信息发生改变,如何才能在UI中更新呢?代码如下:

 TestViewModel viewModel = ViewModelProviders.of(this).get(TestViewModel.class);
        viewModel.getUser().observe(this, new Observer() {
            @Override
            public void onChanged(User user) {
                tvName.setText(user.getName());
                tvAge.setText(String.valueOf(user.getAge()));
                tvGender.setText(user.getSex());
            }
        });

        findViewById(R.id.btn_change).setOnClickListener(v -> {
            User user = new User();
            user.setName("Amy");
            user.setSex("female");
            user.setAge(18);
            viewModel.getUser().setValue(user);
        });

ViewModel会在Acitivity finish或者Fragment detach的时候毁掉onCleared()方法销毁。

Room

简介

Room在SQLite上提供了一个方便访问的抽象层,使我们能更方便的创建我们的缓存数据。它采用注解的方式定义数据库的各项操作,常用的注解如下:

  • @Entity(tableName="…"),定义表名
  • @PrimaryKey,定义主键,可通过autoGenerate属性控制是否自增长,默认个为false
  • @ColumInfo(name="…"),定义表中字段名
  • @Ignore,指定Room需要忽略的字段或方法
  • @Embedded,指定嵌入实体
  • @Query(“sql语句”),查询数据
  • @Insert,插入数据
  • @Delete,删除数据
  • @Update,更新数据
  • @Database,定义数据库信息,表信息,数据库版本
使用
Entity

Entity的一般形式如下所示:

@Entity(tableName = "human")
public class Human {
    @PrimaryKey
    @ColumnInfo(name = "id")
    private int id;
    @ColumnInfo(name = "name")
    private String name;
    //  指示 Room 需要忽略的字段或方法
    @Ignore
    public String ignoreText;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIgnoreText() {
        return ignoreText;
    }

    public void setIgnoreText(String ignoreText) {
        this.ignoreText = ignoreText;
    }
}
外键

在Room中不允许Entity对象建立直接的关系,但是它提供了外键的方式使Entity间建立联系。如有一个Pet类需要和Human类建立关系,可通过@ForeignerKey来达到这个目的,代码如下:

@Entity(foreignKeys = @ForeignKey(entity = HumanEntity.class
        , parentColumns = "id", childColumns = "human_id"))
public class Pet {
    @PrimaryKey
    private int petId;

    private String name;

    @ColumnInfo(name = "human_id")
    private int humanId;

    public int getPetId() {
        return petId;
    }

    public void setPetId(int petId) {
        this.petId = petId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHumanId() {
        return humanId;
    }

    public void setHumanId(int humanId) {
        this.humanId = humanId;
    }
}

外键可以允许你定义被引用的Entity更新时发生的行为。如,你可以定义删除Human时对应的Pet也被删除。可以在@ForeignKey中添加onDelete=CASCADE实现。

获取关联的Entity

Entity间也有可能是一对多的关系,比如一个Human对应多个Pet,通过一次查询获取多个关联的Pet。

public class HumanAndPets {
    @Embedded
    private Human human;
    @Relation(parentColumn = "id", entityColumn = "human_id")
    private List pets;

    public Human getHuman() {
        return human;
    }

    public void setHuman(Human human) {
        this.human = human;
    }

    public List getPets() {
        return pets;
    }

    public void setPets(List pets) {
        this.pets = pets;
    }
}

@Dao
public interface HumanPetDao {
    @Query("SELECT * FROM human")
    public List loadHumanAndPets();
}

注:@Relation注解的filed必须是一个List或者Set,且必须被public修饰或有public修饰的setter()方法。这是因为加载数据分为两步:1、父Entity被查询;2、触发用@Relation注解的Entity查询。

对象嵌套对象

有时候需要在类里面把另一个类作为属性,此时就需要用到@Embedded,这样就可以像查询其他列一样查询这个field,如上述所示。

DAO

DAO是数据库访问的抽象层。Room不允许在主线程中访问数据库,除非在builder里面调用了allowMainThreadQuries()。因为访问数据库是耗时的,可能阻塞主线程,引起UI卡顿。使用代码如下所示:

@Dao
public interface HumanDao {

    @Query("SELECT * FROM HUMAN")
    public List loadHuman();

    @Insert
    public void addHuman(Human human);

    @Insert
    public void addHumans(List humans);

    @Update
    public void updateHuman(Human human);

    @Update
    public void updateHumans(List humans);

    @Delete
    public void deleteHuman(Human human);

    @Delete
    public void deleteeHumans(List humans);
}
Database

创建数据库的代码如下所示:

@Database(entities = {Human.class, HumanAndPets.class, Pet.class}
        , version = 1, exportSchema = false)
@TypeConverters(DateConverter.class)//类型转换器
public abstract class AppDatabase extends RoomDatabase {

    public abstract HumanDao getHumanDao();

    private static final String DB_NAME = "test.db";
    private static AppDatabase instance;

    public static AppDatabase getInstance(Context context) {
        if (instance == null) {
            synchronized (AppDatabase.class) {
                if (instance == null) {
                    instance = createDb(context);
                }
            }
        }
        return instance;
    }

    private static AppDatabase createDb(Context context) {
        return Room.databaseBuilder(context, AppDatabase.class, DB_NAME)
                .addCallback(new Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {//数据库创建回调
                        super.onCreate(db);
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {//数据库使用回调
                        super.onOpen(db);
                    }
                })
                .addMigrations(MIGRATION_1_2)//数据库升级迁移
                .allowMainThreadQueries()//允许数据库在主线程中操作
                .build();
    }

    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {

        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            //TODO 执行对应的SQL语句
        }
    };
}

Room还可以和LiveData、RxJava配合使用从而到达更灵活的效果。

参考资料:

  • Android Architecture Components 只看这一篇就够了
  • 理解Android Architecture Components系列

你可能感兴趣的:(Android Architecture Components)