美文网首页
Android Architecture Componets B

Android Architecture Componets B

作者: WangGavin | 来源:发表于2017-10-06 19:36 被阅读28次

    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)。

    1. 比较AtomicBoolean和expect的值,如果一致,执行方法内的语句。其实就是一个if语句
    2. 把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;
        }
    }
    

    相关文章

      网友评论

          本文标题:Android Architecture Componets B

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