Android Architecture Components是谷歌在Google I/O 2017发布的一套帮助开发者解决安卓架构设计的方案。里面包含两大块内容:
Lifecycle可以让开发者构建能够感知其他组件(主要指Activity、Fragment)生命周期的类。LifeCycle使用两个主要的枚举类来跟踪它所关联组件的生命周期:
那么,如何构建可以感知其他组件生命周期的类呢?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的设计目的就是存放和处理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在SQLite上提供了一个方便访问的抽象层,使我们能更方便的创建我们的缓存数据。它采用注解的方式定义数据库的各项操作,常用的注解如下:
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间也有可能是一对多的关系,比如一个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是数据库访问的抽象层。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(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配合使用从而到达更灵活的效果。
参考资料: