Android主流三方庫源碼分析(4、深刻理解GreenDao源碼)

前言

成爲一名優秀的Android開發,須要一份完備的知識體系,在這裏,讓咱們一塊兒成長爲本身所想的那樣~。

前兩篇咱們詳細地分析了Android的網絡底層框架OKHttp和封裝框架Retrofit的核心源碼,若是對OKHttp或Retrofit內部機制不瞭解的能夠看看Android主流三方庫源碼分析(1、深刻理解OKHttp源碼)Android主流三方庫源碼分析(2、深刻理解Retrofit源碼),除了熱門的網絡庫以外,咱們還分析了使用最普遍的圖片加載框架Glide的加載流程,你們讀完這篇源碼分析實力會有很多提高,有興趣能夠看看Android主流三方庫源碼分析(3、深刻理解Glide源碼)。本篇,咱們將會來對目前Android數據庫框架中性能最好的GreenDao來進行較爲深刻地講解。html

1、基本使用流程

一、導入GreenDao的代碼生成插件和庫

// 項目下的build.gradle
buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1' 
    }
}

// app模塊下的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

...

dependencies {
    ...
    compile 'org.greenrobot:greendao:3.2.0' 
}
複製代碼

二、建立一個實體類,這裏爲HistoryData

@Entity
public class HistoryData {

    @Id(autoincrement = true)
    private Long id;

    private long date;

    private String data;
}
複製代碼

三、選擇ReBuild Project,HistoryData會被自動添加Set/get方法,並生成整個項目的DaoMaster、DaoSession類,以及與該實體HistoryData對應的HistoryDataDao。

image

@Entity
public class HistoryData {

    @Id(autoincrement = true)
    private Long id;

    private long date;

    private String data;

    @Generated(hash = 1371145256)
    public HistoryData(Long id, long date, String data) {
        this.id = id;
        this.date = date;
        this.data = data;
    }

    @Generated(hash = 422767273)
    public HistoryData() {
    }

    public Long getId() {
        return this.id;
    }

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

    public long getDate() {
        return this.date;
    }

    public void setDate(long date) {
        this.date = date;
    }

    public String getData() {
        return this.data;
    }

    public void setData(String data) {
        this.data = data;
    }
}
複製代碼

這裏點明一下這幾個類的做用:android

  • DaoMaster:全部Dao類的主人,負責整個庫的運行,內部的靜態抽象子類DevOpenHelper繼承並重寫了Android的SqliteOpenHelper。
  • DaoSession:做爲一個會話層的角色,用於生成相應的Dao對象、Dao對象的註冊,操做Dao的具體對象。
  • xxDao(HistoryDataDao):生成的Dao對象,用於進行具體的數據庫操做。

四、獲取並使用相應的Dao對象進行增刪改查操做

DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, Constants.DB_NAME);
SQLiteDatabase database = devOpenHelper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(database);
mDaoSession = daoMaster.newSession();
HistoryDataDao historyDataDao = daoSession.getHistoryDataDao();

// 省略建立historyData的代碼
...

// 增
historyDataDao.insert(historyData);

// 刪
historyDataDao.delete(historyData);

// 改
historyDataDao.update(historyData);

// 查
List<HistoryData> historyDataList = historyDataDao.loadAll();
複製代碼

本篇文章將會以上述使用流程來對GreenDao的源碼進行逐步分析,最後會分析下GreenDao中一些優秀的特性,讓讀者朋友們對GreenDao的理解有更一步的加深。git

2、GreenDao使用流程分析

一、建立數據庫幫助類對象DaoMaster.DevOpenHelper

DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, Constants.DB_NAME);
複製代碼

建立GreenDao內部實現的數據庫幫助類對象devOpenHelper,核心源碼以下:程序員

public class DaoMaster extends AbstractDaoMaster {

    ...

    public static abstract class OpenHelper extends DatabaseOpenHelper {
    
    ...
    
         @Override
        public void onCreate(Database db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
            createAllTables(db, false);
        }
    }
    
    public static class DevOpenHelper extends OpenHelper {
    
        ...
        
        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }
}
複製代碼

DevOpenHelper自身實現了更新的邏輯,這裏是棄置了全部的表,而且調用了OpenHelper實現的onCreate方法用於建立全部的表,其中DevOpenHelper繼承於OpenHelper,而OpenHelper自身又繼承於DatabaseOpenHelper,那麼,這個DatabaseOpenHelper這個類的做用是什麼呢?github

public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {

    ...
    
    // 關注點1
    public Database getWritableDb() {
        return wrap(getWritableDatabase());
    }
    
    public Database getReadableDb() {
        return wrap(getReadableDatabase());
    }   
    
    protected Database wrap(SQLiteDatabase sqLiteDatabase) {
        return new StandardDatabase(sqLiteDatabase);
    }
    
    ...
    
    // 關注點2
    public Database getEncryptedWritableDb(String password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.wrap(encryptedHelper.getWritableDatabase(password));
    }
    
    public Database getEncryptedReadableDb(String password) {
        EncryptedHelper encryptedHelper = checkEncryptedHelper();
        return encryptedHelper.wrap(encryptedHelper.getReadableDatabase(password));
    }
    
    ...
    
    private class EncryptedHelper extends net.sqlcipher.database.SQLiteOpenHelper {
    
        ...
    
    
        protected Database wrap(net.sqlcipher.database.SQLiteDatabase     sqLiteDatabase) {
            return new EncryptedDatabase(sqLiteDatabase);
        }
    }
複製代碼

其實,DatabaseOpenHelper也是實現了SQLiteOpenHelper的一個幫助類,它內部能夠獲取到兩種不一樣的數據庫類型,一種是標準型的數據庫StandardDatabase,另外一種是加密型的數據庫EncryptedDatabase,從以上源碼可知,它們內部都經過wrap這樣一個包裝的方法,返回了對應的數據庫類型,咱們大體看一下StandardDatabase和EncryptedDatabase的內部實現。sql

public class StandardDatabase implements Database {

    // 這裏的SQLiteDatabase是android.database.sqlite.SQLiteDatabase包下的
    private final SQLiteDatabase delegate;

    public StandardDatabase(SQLiteDatabase delegate) {
        this.delegate = delegate;
    }

    @Override
    public Cursor rawQuery(String sql, String[] selectionArgs) {
        return delegate.rawQuery(sql, selectionArgs);
    }

    @Override
    public void execSQL(String sql) throws SQLException {
        delegate.execSQL(sql);
    }

    ...
}

public class EncryptedDatabaseStatement implements DatabaseStatement     {

    // 這裏的SQLiteStatement是net.sqlcipher.database.SQLiteStatement包下的
    private final SQLiteStatement delegate;

    public EncryptedDatabaseStatement(SQLiteStatement delegate) {
        this.delegate = delegate;
    }

    @Override
    public void execute() {
        delegate.execute();
    }
    
    ...
}
複製代碼

StandardDatabase和EncryptedDatabase這兩個類內部都使用了代理模式給相同的接口添加了不一樣的具體實現,StandardDatabase天然是使用的Android包下的SQLiteDatabase,而EncryptedDatabaseStatement爲了實現加密數據庫的功能,則使用了一個叫作sqlcipher的數據庫加密三方庫,若是你項目下的數據庫須要保存比較重要的數據,則可使用getEncryptedWritableDb方法來代替getdWritableDb方法對數據庫進行加密,這樣,咱們以後的數據庫操做則會以代理模式的形式間接地使用sqlcipher提供的API去操做數據庫數據庫

二、建立DaoMaster對象

SQLiteDatabase database = devOpenHelper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(database);
複製代碼

首先,DaoMaster做爲全部Dao對象的主人,它內部確定是須要一個SQLiteDatabase對象的,所以,先由DaoMaster的幫助類對象devOpenHelper的getWritableDatabase方法獲得一個標準的數據庫類對象database,再由此建立一個DaoMaster對象。json

public class DaoMaster extends AbstractDaoMaster {

    ...

    public DaoMaster(SQLiteDatabase db) {
        this(new StandardDatabase(db));
    }

    public DaoMaster(Database db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(HistoryDataDao.class);
    }
    
    ...
}
複製代碼

在DaoMaster的構造方法中,它首先執行了super(db, SCHEMA_VERSION)方法,即它的父類AbstractDaoMaster的構造方法。緩存

public abstract class AbstractDaoMaster {

    ...

    public AbstractDaoMaster(Database db, int schemaVersion) {
        this.db = db;
        this.schemaVersion = schemaVersion;

        daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
    }
    
    protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }
    
    ...
}
複製代碼

在AbstractDaoMaster對象的構造方法中,除了記錄當前的數據庫對象db和版本schemaVersion以外,還建立了一個類型爲HashMap<Class>, DaoConfig>()的daoConfigMap對象用於保存每個DAO對應的數據配置對象DaoConfig,而且Daoconfig對象存儲了對應的Dao對象所必需的數據。最後,在DaoMaster的構造方法中使用了registerDaoClass(HistoryDataDao.class)方法將HistoryDataDao類對象進行了註冊,實際上,就是爲HistoryDataDao這個Dao對象建立了相應的DaoConfig對象並將它放入daoConfigMap對象中保存起來。安全

三、建立DaoSession對象

mDaoSession = daoMaster.newSession();
複製代碼

在DaoMaster對象中使用了newSession方法新建了一個DaoSession對象。

public DaoSession newSession() {
    return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
複製代碼

在DaoSeesion的構造方法中,又作了哪些事情呢?

public class DaoSession extends AbstractDaoSession {

    ...

    public DaoSession(Database db, IdentityScopeType type, Map<Class<?     extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);

        historyDataDaoConfig = daoConfigMap.get(HistoryDataDao.class).clone();
        historyDataDaoConfig.initIdentityScope(type);

        historyDataDao = new HistoryDataDao(historyDataDaoConfig, this);

        registerDao(HistoryData.class, historyDataDao);
    }
    
    ...
}
複製代碼

首先,調用了父類AbstractDaoSession的構造方法。

public class AbstractDaoSession {

    ...

    public AbstractDaoSession(Database db) {
        this.db = db;
        this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
    }
    
    protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
    }
    
    ...
}
複製代碼

在AbstractDaoSession構造方法裏面建立了一個實體與Dao對象的映射集合。接下來,在DaoSession的構造方法中還作了2件事:

  • 一、建立每個Dao對應的DaoConfig對象,這裏是historyDataDaoConfig,而且根據IdentityScopeType的類型初始化建立一個相應的IdentityScope,根據type的不一樣,它有兩種類型,分別是IdentityScopeObjectIdentityScopeLong,它的做用是根據主鍵緩存對應的實體數據。當主鍵是數字類型的時候,如long/Long、int/Integer、short/Short、byte/Byte,則使用IdentityScopeLong緩存實體數據,當主鍵不是數字類型的時候,則使用IdentityScopeObject緩存實體數據。
  • 二、根據DaoSession對象和每個Dao對應的DaoConfig對象,建立與之對應的historyDataDao對象,因爲這個項目只建立了一個實體類HistoryData,所以這裏只有一個Dao對象historyDataDao,而後就是註冊Dao對象,其實就是將實體和對應的Dao對象放入entityToDao這個映射集合中保存起來了。

四、插入源碼分析

HistoryDataDao historyDataDao = daoSession.getHistoryDataDao();

// 增
historyDataDao.insert(historyData);
複製代碼

這裏首先在會話層DaoSession中獲取了咱們要操做的Dao對象HistoryDataDao,而後插入了一個咱們預先建立好的historyData實體對象。其中HistoryDataDao繼承了AbstractDao<HistoryData, Long> 。

public class HistoryDataDao extends AbstractDao<HistoryData, Long> {
    ...
}
複製代碼

那麼,這個AbstractDao是幹什麼的呢?

public abstract class AbstractDao<T, K> {

    ...
    
    public List<T> loadAll() {
        Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
        return loadAllAndCloseCursor(cursor);
    }
    
    ...
    
    public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(),     true);
    }
    
    ...
    
    public void delete(T entity) {
        assertSinglePk();
        K key = getKeyVerified(entity);
        deleteByKey(key);
    }
    
    ...

}
複製代碼

看到這裏,根據程序員優秀的直覺,你們應該能猜到,AbstractDao是全部Dao對象的基類,它實現了實體數據的操做如增刪改查。咱們接着分析insert是如何實現的,在AbstractDao的insert方法中又調用了executeInsert這個方法。在這個方法中,第二個參裏的statements是一個TableStatements對象,它是在AbstractDao初始化構造器時從DaoConfig對象中取出來的,是一個根據指定的表格建立SQL語句的一個幫助類。使用statements.getInsertStatement()則是獲取了一個插入的語句。而第三個參數則是判斷是不是主鍵的標誌。

public class TableStatements {

    ...

    public DatabaseStatement getInsertStatement() {
        if (insertStatement == null) {
            String sql = SqlUtils.createSqlInsert("INSERT INTO ", tablename, allColumns);
            DatabaseStatement newInsertStatement = db.compileStatement(sql);
            ...
        }
        return insertStatement;
    }

    ...
}
複製代碼

在TableStatements的getInsertStatement方法中,主要作了兩件事:

  • 一、使用SqlUtils建立了插入的sql語句
  • 二、根據不一樣的數據庫類型(標準數據庫或加密數據庫)將sql語句編譯成當前數據庫對應的語句

咱們繼續往下分析executeInsert的執行流程。

private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
    long rowId;
    if (db.isDbLockedByCurrentThread()) {
        rowId = insertInsideTx(entity, stmt);
    } else {
        db.beginTransaction();
        try {
            rowId = insertInsideTx(entity, stmt);
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }
    if (setKeyAndAttach) {
        updateKeyAfterInsertAndAttach(entity, rowId, true);
    }
    return rowId;
}
複製代碼

這裏首先是判斷數據庫是否被當前線程鎖定,若是是,則直接插入數據,不然爲了不死鎖,則開啓一個數據庫事務,再進行插入數據的操做。最後若是設置了主鍵,則在插入數據以後更新主鍵的值並將對應的實體緩存到相應的identityScope中,這一塊的代碼流程以下所示:

protected void updateKeyAfterInsertAndAttach(T entity, long rowId, boolean lock) {
    if (rowId != -1) {
        K key = updateKeyAfterInsert(entity, rowId);
        attachEntity(key, entity, lock);
    } else {
       ...
    }
}

protected final void attachEntity(K key, T entity, boolean lock) {
    attachEntity(entity);
    if (identityScope != null && key != null) {
        if (lock) {
            identityScope.put(key, entity);
        } else {
            identityScope.putNoLock(key, entity);
        }
    }
}
複製代碼

接着,咱們仍是繼續追蹤主線流程,在executeInsert這個方法中調用了insertInsideTx進行數據的插入。

private long insertInsideTx(T entity, DatabaseStatement stmt) {
    synchronized (stmt) {
        if (isStandardSQLite) {
            SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
            bindValues(rawStmt, entity);
            return rawStmt.executeInsert();
        } else {
            bindValues(stmt, entity);
            return stmt.executeInsert();
        }
    }
}
複製代碼

爲了防止併發,這裏使用了悲觀鎖保證了數據的一致性,在AbstractDao這個類中,大量使用了這種鎖保證了它的線程安全性。接着,若是當前是標準數據庫,則直接獲取stmt這個DatabaseStatement類對應的原始語句進行實體字段屬性的綁定和最後的執行插入操做。若是是加密數據庫,則直接使用當前的加密數據庫所屬的插入語句進行實體字段屬性的綁定和執行最後的插入操做。其中bindValues這個方法對應的實現類就是咱們的HistoryDataDao類。

public class HistoryDataDao extends AbstractDao<HistoryData, Long> {

    ...

    @Override
    protected final void bindValues(DatabaseStatement stmt, HistoryData     entity) {
        stmt.clearBindings();

        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindLong(2, entity.getDate());

        String data = entity.getData();
        if (data != null) {
            stmt.bindString(3, data);
        }
    }
    
    @Override
    protected final void bindValues(SQLiteStatement stmt, HistoryData     entity) {
        stmt.clearBindings();

        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindLong(2, entity.getDate());

        String data = entity.getData();
        if (data != null) {
            stmt.bindString(3, data);
        }
    }

    ...
}
複製代碼

能夠看到,這裏對HistoryData的全部字段使用對應的數據庫語句進行了綁定操做。這裏最後再說起一下,若是當前數據庫是加密型時,則會使用最開始說起的DatabaseStatement的加密實現類EncryptedDatabaseStatement應用代理模式去使用sqlcipher這個加密型數據庫的insert方法

五、查詢源碼分析

通過對插入源碼的分析,我相信你們對GreenDao內部的機制已經有了一些本身的理解,因爲刪除和更新內部的流程比較簡單,且與插入源碼有殊途同歸之妙,這裏就再也不贅述了。最後咱們再分析下查詢的源碼,查詢的流程調用鏈較長,因此將它的核心流程源碼直接給出。

List<HistoryData> historyDataList = historyDataDao.loadAll();

public List<T> loadAll() {
    Cursor cursor = db.rawQuery(statements.getSelectAll(), null);
    return loadAllAndCloseCursor(cursor);
}

protected List<T> loadAllAndCloseCursor(Cursor cursor) {
    try {
        return loadAllFromCursor(cursor);
    } finally {
        cursor.close();
    }
}

protected List<T> loadAllFromCursor(Cursor cursor) {
    int count = cursor.getCount();
    ...
    boolean useFastCursor = false;
    if (cursor instanceof CrossProcessCursor) {
        window = ((CrossProcessCursor) cursor).getWindow();
        if (window != null) {  
            if (window.getNumRows() == count) {
                cursor = new FastCursor(window);
                useFastCursor = true;
            } else {
              ...
            }
        }
    }

    if (cursor.moveToFirst()) {
        ...
        try {
            if (!useFastCursor && window != null && identityScope != null) {
                loadAllUnlockOnWindowBounds(cursor, window, list);
            } else {
                do {
                    list.add(loadCurrent(cursor, 0, false));
                } while (cursor.moveToNext());
            }
        } finally {
            ...
        }
    }
    return list;
}
複製代碼

最終,loadAll方法將會調用到loadAllFromCursor這個方法,首先,若是當前的遊標cursor是跨進程的cursor,而且cursor的行數沒有誤差的話,則使用一個加快版的FastCursor對象進行遊標遍歷。接着,不論是執行loadAllUnlockOnWindowBounds這個方法仍是直接加載當前的數據列表list.add(loadCurrent(cursor, 0, false)),最後都會調用到這行list.add(loadCurrent(cursor, 0, false))代碼,很明顯,loadCurrent方法就是加載數據的方法。

final protected T loadCurrent(Cursor cursor, int offset, boolean lock) {
    if (identityScopeLong != null) {
        ...
        T entity = lock ? identityScopeLong.get2(key) : identityScopeLong.get2NoLock(key);
        if (entity != null) {
            return entity;
        } else {
            entity = readEntity(cursor, offset);
            attachEntity(entity);
            if (lock) {
                identityScopeLong.put2(key, entity);
            } else {
                identityScopeLong.put2NoLock(key, entity);
            }
            return entity;
        }
    } else if (identityScope != null) {
        ...
        T entity = lock ? identityScope.get(key) : identityScope.getNoLock(key);
        if (entity != null) {
            return entity;
        } else {
            entity = readEntity(cursor, offset);
            attachEntity(key, entity, lock);
            return entity;
        }
    } else {
        ...
        T entity = readEntity(cursor, offset);
        attachEntity(entity);
        return entity;
    }
}
複製代碼

咱們來理解下loadCurrent這個方法內部的執行策略。首先,若是有實體數據緩存identityScopeLong/identityScope,則先從緩存中取,若是緩存中沒有,會使用該實體對應的Dao對象,這裏的是HistoryDataDao,它在內部根據遊標取出的數據新建了一個新的HistoryData實體對象返回。

@Override
public HistoryData readEntity(Cursor cursor, int offset) {
    HistoryData entity = new HistoryData( //
        cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
        cursor.getLong(offset + 1), // date
        cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2) // data
    );
    return entity;
}
複製代碼

最後,若是是非identityScopeLong緩存類型,便是屬於identityScope的狀況下,則還會在identityScope中將上面得到的數據進行緩存。若是沒有實體數據緩存的話,則直接調用readEntity組裝數據返回便可。

注意:對於GreenDao緩存的特性,可能會出現沒有拿到最新數據的bug,所以,若是遇到這種狀況,可使用DaoSession的clear方法刪除緩存。

3、GreenDao是如何與ReactiveX結合?

首先,看下與rx結合的使用流程:

RxDao<HistoryData, Long> xxDao = daoSession.getHistoryDataDao().rx();
xxDao.insert(historyData)
        .observerOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<HistoryData>() {
            @Override
            public void call(HistoryData entity) {
                // insert success
            }
        });
複製代碼

在AbstractDao對象的.rx()方法中,建立了一個默認執行在io線程的rxDao對象。

@Experimental
public RxDao<T, K> rx() {
    if (rxDao == null) {
        rxDao = new RxDao<>(this, Schedulers.io());
    }
    return rxDao;
}
複製代碼

接着分析rxDao的insert方法。

@Experimental
public Observable<T> insert(final T entity) {
    return wrap(new Callable<T>() {
        @Override
        public T call() throws Exception {
            dao.insert(entity);
            return entity;
        }
    });
}
複製代碼

起實質做用的就是這個wrap方法了,在這個方法裏面主要是調用了RxUtils.fromCallable(callable)這個方法。

@Internal
class RxBase {

    ...

    protected <R> Observable<R> wrap(Callable<R> callable) {
        return wrap(RxUtils.fromCallable(callable));
    }

    protected <R> Observable<R> wrap(Observable<R> observable) {
        if (scheduler != null) {
            return observable.subscribeOn(scheduler);
        } else {
            return observable;
        }
    }

    ...
}
複製代碼

在RxUtils的fromCallable這個方法內部,其實就是使用defer這個延遲操做符來進行被觀察者事件的發送,主要目的就是爲了確保Observable被訂閱後才執行。最後,若是調度器scheduler存在的話,將經過外部的wrap方法將執行環境調度到io線程。

@Internal
class RxUtils {

    @Internal
    static <T> Observable<T> fromCallable(final Callable<T> callable) {
        return Observable.defer(new Func0<Observable<T>>() {

            @Override
            public Observable<T> call() {
                T result;
                try {
                    result = callable.call();
                } catch (Exception e) {
                    return Observable.error(e);
                }
                return Observable.just(result);
            }
        });
    }
}
複製代碼

4、總結

在分析完GreenDao的核心源碼以後,我發現,GreenDao做爲最好的數據庫框架之一,是有必定道理的。首先,它經過使用自身的插件配套相應的freemarker模板生成所需的靜態代碼,避免了反射等消耗性能的操做。其次,它內部提供了實體數據的映射緩存機制,可以進一步加快查詢速度。對於不一樣數據庫對應的SQL語句,也使用了不一樣的DataBaseStatement實現類結合代理模式進行了封裝,屏蔽了數據庫操做等繁瑣的細節。最後,它使用了sqlcipher提供了加密數據庫的功能,在必定程度確保了安全性,同時,結合RxJava,咱們便能更簡潔地實現異步的數據庫操做。GreenDao源碼分析到這裏就真的完結了,下一篇,筆者將會對RxJava的核心源碼進行細緻地講解,以此能讓你們對RxJava有一個更爲深刻的理解。

參考連接:

一、GreenDao V3.2.2源碼

二、GreenDao源碼分析

三、GreenDao源碼分析

讚揚

若是這個庫對您有很大幫助,您願意支持這個項目的進一步開發和這個項目的持續維護。你能夠掃描下面的二維碼,讓我喝一杯咖啡或啤酒。很是感謝您的捐贈。謝謝!


Contanct Me

● 微信:

歡迎關注個人微信:bcce5360

● 微信羣:

微信羣若是不能掃碼加入,麻煩你們想進微信羣的朋友們,加我微信拉你進羣。

● QQ羣:

2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎你們加入~

About me

很感謝您閱讀這篇文章,但願您能將它分享給您的朋友或技術羣,這對我意義重大。

但願咱們能成爲朋友,在 Github掘金上一塊兒分享知識。

相關文章
相關標籤/搜索