Android Architecture Componets BasicSample学习
1.创建工程 所有模块依赖于Google,jencenter,maven仓库 allprojects { repositories { jcenter() maven { url 'https://maven.google.com' } mavenCentral() } } gradle版本:gradle-4.1-milestone-1-all gradle 的Android插件版本在3.0.0-beta7 distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip classpath 'com.android.tools.build:gradle:3.0.0-alpha7' 工程根目录的gradle设置一些变量,表示模块依赖的框架的版本号,方便以后改动,例如 ext { buildToolsVersion = "26.0.1" supportLibVersion = "26.0.2" runnerVersion = "1.0.1" rulesVersion = "1.0.1" espressoVersion = "3.0.1" archLifecycleVersion = "1.0.0-alpha9" archRoomVersion = "1.0.0-alpha9" constrantLayoutVersion="1.0.2" } Module的gradle里 compile 'com.android.support:design:' + rootProject.supportLibVersion compile 'com.android.support:cardview-v7:' + rootProject.supportLibVersion compile 'com.android.support:recyclerview-v7:' + rootProject.supportLibVersion compile 'com.android.support.constraint:constraint-layout:' + rootProject.constrantLayoutVersion 需求分析,效果展示 这个应用就两个界面,打开应用进入首页,刚开始会加载产品数据,数据是从本地数据库查询而来,然后点击任意一项可以进入产品的详情页,详情页包含对产品的评论 效果展示 主目录 src主目录 db db目录 converter 里面放着一个日期转换器,用了TypeConverter注解 /** * 时间转换 * @param timestamp * @return Date */ @TypeConverter public static Date toDate(Long timestamp){ return timestamp==null?null:new Date(timestamp); } /** * 时间转换 * @param date * @return long */ @TypeConverter public static Long toTimestamp(Date date) { return date == null ? null : date.getTime(); } model 模型,里面都是接口,包含的都是获取数据实体成员的方法,它们都是抽象的。 例:一个Comment的,一个Product的 public interface Comment { int getId();//获取ID int getProductId();//获取产品ID String getText();//获取评论内容 Date getPostedAt();//获取发布时间 } public interface Product { int getId(); String getName(); String getDescription(); int getPrice(); } entity 这个数据库就两张表,一个使产品表,一个使评论表,所以分别对应着各自的实体类 例如一个产品的实体类,它是实现了model里的抽象方法 //Entity注解,设置表名为products @Entity(tableName = "products") public class ProductEntity implements Product{//实现了模型的抽象方法 @PrimaryKey private int id;//id 为主键 private String name;//产品名 private String description;//产品描述 private int price;//产品价格 @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public ProductEntity() { } public ProductEntity(Product product){ this.id=product.getId(); this.name=product.getName(); this.description=product.getDescription(); this.price=product.getPrice(); } } @Entity(tableName = "comments" ,foreignKeys = { @ForeignKey(entity = ProductEntity.class, parentColumns = "id", childColumns = "productId", onDelete = ForeignKey.CASCADE) },indices = { @Index(value = "productId") }) public class CommentEntity implements Comment{ @PrimaryKey(autoGenerate = true) private int id; private int productId; private String text; private Date postedAt; @Override public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } @Override public String getText() { return text; } public void setText(String text) { this.text = text; } @Override public Date getPostedAt() { return postedAt; } public void setPostedAt(Date postedAt) { this.postedAt = postedAt; } public CommentEntity() { } public CommentEntity(Comment comment) { this.id = comment.getId(); this.productId = comment.getProductId(); this.text = comment.getText(); this.postedAt = comment.getPostedAt(); } } dao 里面是各个表的操作接口,都是抽象的,很像Retrofit那个带注解的接口,只不过这里是操作数据库 @Dao public interface CommentDao { @Query("SELECT * FROM comments where productId = :productId") LiveData<List<CommentEntity>> loadComments(int productId); @Query("SELECT * FROM comments where productId = :productId") List<CommentEntity> loadCommentsSync(int productId); @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List<CommentEntity> products); } @Dao public interface ProductDao { @Query("SELECT * FROM products") LiveData<List<ProductEntity>> loadAllProducts(); @Insert(onConflict = OnConflictStrategy.REPLACE) void insertAll(List<ProductEntity> products); @Query("SELECT * FROM products WHERE id=:productId") LiveData<ProductEntity> loadProduct(int productId); @Query("SELECT * FROM products WHERE id =:productId") ProductEntity loadProductSync(int productId); } AppDatabase.java AppDatabase继承RoomDatabase,这个类的作用就是数据库类,通过这个对象可以获取各个表的Dao和数据库设置,@Database注解设置了实体类和数据库版本,@TypeConverters指定了类型转换,就是上面的日期转换,因为数据库没有Data这种类型,但可以用long表示 @Database(entities = {ProductEntity.class, CommentEntity.class},version = 1) @TypeConverters(DateConverter.class) public abstract class AppDataBase extends RoomDatabase { public static final String DATABASENAME="basesample-db";//数据库名 public abstract ProductDao productDao();//获取Dao public abstract CommentDao commentDao();//获取Dao } DatabaseCreator.java 这个是一个辅助类,也是一个单例,用于获取AppDatabase实例,因为很多地方都要用AppDatabase实例嘛,而且数据库还没创建啊,这个类设计到一些知识 实现单例 这里应该用的是双重检查锁,保证创建实例是也是在一个线程里创建 // For Singleton instantiation private static DatabaseCreator sInstance; private static final Object LOCK = new Object(); public synchronized static DatabaseCreator getInstance(Context context) { if (sInstance == null) { synchronized (LOCK) { if (sInstance == null) { sInstance = new DatabaseCreator(); } } } return sInstance; } 原子操作 这里用到了AtomicBoolean,原来看到这里我都很懵逼,后来查查资料,才知道这个叫原子操作,就是compareAndSet(boolean expect, boolean update)。 比较AtomicBoolean和expect的值,如果一致,执行方法内的语句。其实就是一个if语句 把AtomicBoolean的值设成update 这连个操作一气呵成,中间没有人能够阻止,这样的话可以进行多线程控制,比如这里的创建数据库,因为后期可能多个地方会用到creatDb方法,所以要保证数据库只能创建一次且只能在一个线程中创建,当然这里没用SharePreferences,所以这里的"数据库只能创建一次"是指在应用不被杀死的情况下。 private final MutableLiveData<Boolean> mIsDatabaseCreated = new MutableLiveData<>(); private AppDataBase mDb; private final AtomicBoolean mInitializing = new AtomicBoolean(true); public void createDb(Context context) { Log.d("DatabaseCreator", "Creating DB from " + Thread.currentThread().getName()); if (mInitializing.compareAndSet(false, true)) { return; // Already initializing 已经创建了数据库 } mIsDatabaseCreated.setValue(false);//开始创建数据库,观察这个数据可以显示loading new AsyncTask<Context,Void,Void>(){ @Override protected Void doInBackground(Context... contexts) { Log.d("DatabaseCreator", "Starting bg job " + Thread.currentThread().getName()); Context context = contexts[0].getApplicationContext(); // Reset the database to have new data on every run. context.deleteDatabase(DATABASENAME); AppDataBase db= Room.databaseBuilder(context.getApplicationContext(),AppDataBase.class, DATABASENAME).build(); addDelay(); // Add a delay to simulate a long-running operation模拟耗时操作 // Add some data to the database 加一些数据进去 DatabaseInitUtil.initializeDb(db); Log.d("DatabaseCreator", "DB was populated in thread " + Thread.currentThread().getName()); mDb=db; return null; } @Override protected void onPostExecute(Void aVoid) { mIsDatabaseCreated.setValue(true);//创建数据库完毕 Log.d(TAG, "onPostExecute:"); } }.execute(context.getApplicationContext()); } viewmodel viewmodel目录 viewmodel就是提供一个连接model和view的桥梁,只不过提供的是可被观察的数据对象,而且是liveData,可以判断观察者的状态进行通知.如下面的ProductListViewModel,mObservableProducts是一个LiveData,viewmodel获取它是通过database得到,但如果database未初始化的情况也要考虑,所以用了一个Transformations.switchMap(),就是在databaseCreator.isDatabaseCreated()这个liveData为FALSE时返回值为空的liveData,为True时返回database查询到的liveData,这里可能有点绕,但是慢慢想又觉得很妙,因为这个观察模式在一定的生命周期内一直生效,完全是响应式的。 public class ProductListViewModel extends AndroidViewModel{ private static final String TAG = "ProductListViewModel>>>"; private static final MutableLiveData ABSENT=new MutableLiveData(); { ABSENT.setValue(null); } private final LiveData<List<ProductEntity>> mObservableProducts; public ProductListViewModel(@Nullable Application application) { super(application); final DatabaseCreator databaseCreator=DatabaseCreator.getInstance(application); mObservableProducts= Transformations.switchMap(databaseCreator.isDatabaseCreated(), new Function<Boolean, LiveData<List<ProductEntity>>>() { @Override public LiveData<List<ProductEntity>> apply(Boolean input) { if (!Boolean.TRUE.equals(input)){ Log.d(TAG, "apply: 空数据!"); return ABSENT; }else { return databaseCreator.getDatabase().productDao().loadAllProducts(); } } }); databaseCreator.createDb(this.getApplication()); } public LiveData<List<ProductEntity>> getmObservableProducts() { return mObservableProducts; } }