Android Room封裝成一個類似Redis的快取資料庫的效果

qindachang發表於2018-05-25

如何將Android Room封裝成一個類似Redis的快取資料庫的效果。

Android Room封裝快取。如果你還在用SharedPreferences,試試使用這種方式?

Android Room是一個非常棒的資料庫框架,提供了明確地Dao層來運算元據庫(越來越像spring data jpa啦),這裡我來告訴你如何使用Room封裝出一個類似Redis的使用方式(寫過後端的童鞋應該懂)。主要目標,把它弄成K/V資料庫的效果。

1. 引入

allprojects {
    repositories {
        mavenCentral()
        google()
        jcenter()
        maven { url "https://jitpack.io" }
        maven { url "https://maven.google.com" }
    }
}

dependencies {
    implementation 'android.arch.persistence.room:runtime:1.0.0'
    annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
}
複製程式碼

2. 定義實體

快取表

@Entity(tableName = "cache")
public class CacheEntity {
    @PrimaryKey(autoGenerate = true)
    public int id;
    public String key;
    public String value;

    public int getId() {
        return id;
    }

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

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
複製程式碼

3. 封裝初始化操作

DatabaseConfig.class

@Database(entities = {CacheEntity.class}, version = 1, exportSchema = false)
public abstract class DatabaseConfig extends RoomDatabase {
    public abstract CacheDao mCacheDao();
}
複製程式碼

DatabaseInitialize.class

public class DatabaseInitialize {
    private static DatabaseConfig databaseConfig;

    public DatabaseInitialize(){}

    public static void init(Context context) {
        // 生成資料庫例項
        databaseConfig = Room
                .databaseBuilder(context.getApplicationContext(), DatabaseConfig.class, "huiminyougou")
                .allowMainThreadQueries() // 允許主執行緒中查詢
                .build();
    }

    static DatabaseConfig getDatabaseConfig() {
        if (databaseConfig == null) {
            throw new NullPointerException("DatabaseInitialize.init(context) has not call, remember call this function in your Application.class");
        }
        return databaseConfig;
    }
}
複製程式碼

DatabaseSession.class

public final class DatabaseSession {

    private DatabaseSession(){}

    public static DatabaseConfig get() {
        return DatabaseInitialize.getDatabaseConfig();
    }
}	
複製程式碼

4. dao層

@Dao
public interface CacheDao {
    @Insert
    void insertCaches(CacheEntity... cacheEntities);

    @Update
    void updateCaches(CacheEntity... cacheEntities);

    @Query("SELECT * FROM cache WHERE `key` = :key LIMIT 0,1")
    CacheEntity findByKey(String key);

    @Delete
    void deleteCaches(CacheEntity... cacheEntities);
    
    @Query("SELECT * FROM cache")
    CacheEntity[] findAll();
}
複製程式碼

5. service(utils)層

public final class CacheService {
    private CacheService() {
    }

    private static CacheDao getRepository() {
        return DatabaseSession.get().mCacheDao();
    }

    /**
     * 設定快取
     *
     * @param key
     * @param value
     */
    public static void set(String key, String value) {
        CacheEntity cacheEntity;
        cacheEntity = getRepository().findByKey(key);
        if (cacheEntity == null) {
            cacheEntity = new CacheEntity();
            cacheEntity.setKey(key);
            cacheEntity.setValue(value);
            getRepository().insertCaches(cacheEntity);
        } else {
            cacheEntity.setValue(value);
            getRepository().updateCaches(cacheEntity);
        }
    }

    /**
     * 設定快取
     *
     * @param key
     * @param value
     */
    public static void set(String key, Object value) {
        CacheEntity cacheEntity = getRepository().findByKey(key);
        if (cacheEntity == null) {
            cacheEntity = new CacheEntity();
            cacheEntity.setKey(key);
            String jsonValue = GsonHelper.toJson(value);
            cacheEntity.setValue(jsonValue);
            getRepository().insertCaches(cacheEntity);
        } else {
            String jsonValue = GsonHelper.toJson(value);
            cacheEntity.setValue(jsonValue);
            getRepository().updateCaches(cacheEntity);
        }
    }

    /**
     * 獲取快取
     *
     * @param key
     * @return
     */
    public static String get(String key) {
        CacheEntity cacheEntity = getRepository().findByKey(key);
        if (cacheEntity == null) {
            return null;
        }
        return cacheEntity.getValue();
    }

    /**
     * 獲取快取物件
     *
     * @param key
     * @param classOfT
     * @param <T>
     * @return
     */
    public static <T> T get(String key, Class<T> classOfT) {
        CacheEntity cacheEntity = getRepository().findByKey(key);
        if (cacheEntity == null) {
            return null;
        }
        String jsonValue = cacheEntity.getValue();
        return GsonHelper.toObject(jsonValue, classOfT);
    }

    /**
     * 刪除快取
     *
     * @param key
     */
    public static void delete(String key) {
        CacheEntity cacheEntity = getRepository().findByKey(key);
        if (cacheEntity != null) {
            getRepository().deleteCaches(cacheEntity);
        }
    }

    /**
     * 刪除全部快取
     */
    public static void clearAll() {
        CacheEntity[] cacheEntities = getRepository().findAll();
        if (cacheEntities != null && cacheEntities.length != 0) {
            getRepository().deleteCaches(cacheEntities);
        }
    }
}
複製程式碼

GsonHelper.class

public class GsonHelper {

    private GsonHelper() {
        // no instance
    }
    private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();

    public static String toJson(Object o) {
        return gson.toJson(o);
    }

    public static <T> T toObject(String json, Class<T> classOfT)  {
        return gson.fromJson(json, classOfT);
    }

    public static <T> List<T> toList(String json, Class<? extends T[]> clazz) {
        T[] array = gson.fromJson(json, clazz);
        return Arrays.asList(array);
    }
}
複製程式碼

6. 初始化操作

    @Override
    public void onCreate() {
        super.onCreate();
        DatabaseInitialize.init(this);
    }
複製程式碼

7. 使用方式

這裡以一個網路請求獲取首頁資料為例: 使用的是retrofit+rxjava,在請求成功後,將響應實體存入資料庫中。

    mGoodsRequestService.goodsShowIndex(lng, lat)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new BaseHttpObserver<GoodsIndex>() {
                @Override
                public void onSubscribe(Disposable d) {
                    mView.onRequestSubscribe(d);
                }

                @Override
                public void onSuccess(String msg, GoodsIndex data) {
                    CacheService.set(Constant.CacheKey.HOME_INDEX, data);
                    mView.onGoodsIndexRespond(data);
                    mView.onRequestComplete();
                }

                @Override
                public void onFailure(String msg) {

                }

                @Override
                public void onNetworkError(Throwable e) {
                    mView.onRequestError(e);
                }
            });
複製程式碼

在頁面啟動伊始,從資料庫中拿到快取資料:

    GoodsIndex goodsIndex = CacheService.get(Constant.CacheKey.HOME_INDEX, GoodsIndex.class);
    if (goodsIndex != null) {
        mView.onGoodsIndexRespond(goodsIndex);
    }
複製程式碼

8. 末

這裡的service層處理得並不是很好,但是Android上沒有類似@Autowired這樣的註解,網路很多關於Dagger2的文章也是雲裡霧裡,後面思考一下如何用AOP + Dagger2 封裝一個類似的@Autowired出來。有哪位大佬可以指點一下^_^

相關文章