Android Greendao 缓存分析:源码分析

需要掌握的基本:

1.反射

2.泛型

3.注解

4.clone

 

 

greenDAO 使用「Code generation(代码生成)」的方式,这也是其性能能大幅提升的原因。

 

Java的Code Generation: 

拼字节码,读懂JAVA的CLASS文件的字节码规范,然后自己按规范拼一个一模一样的字节码,然后用ClassLoader的defineClass读入并生成

————————————————

 

看完源码:

1.修改数据库的路径

2.创建不同数据库对应不同的table

3.怎么保证强原子性的

4.自己写带有缓存的处理

 

自定义SQL带来的数据不同步问题

有没有insertOrUpdate呢,insertOrReplace有的时候会把之前的字段清空了

 

 

初始化的步骤:

1.helper对象

2.拿到Db对象

3.得到master对象

4.得到dao对象

private void setDatabase() {

    // 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
    // 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO已经帮你做了。
    // 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
    // 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。
    mHelper = new DaoMaster.DevOpenHelper(this, "notes-db", null);

    db = mHelper.getWritableDatabase();

    // 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
    mDaoMaster = new DaoMaster(db);

    mDaoSession = mDaoMaster.newSession();

    setDebugMode(true);

 

 

DaoMaster

DaoMaster是GreenDao的入口

mHelper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
/** Creates underlying database table using DAOs. */
public static void createAllTables(Database db, boolean ifNotExists) {
    UserDao.createTable(db, ifNotExists);
}

/** Drops underlying database table using DAOs. */
public static void dropAllTables(Database db, boolean ifExists) {
    UserDao.dropTable(db, ifExists);
}

/**
 * WARNING: Drops all table on Upgrade! Use only during development.
 * Convenience method using a {@link DevOpenHelper}.
 */
public static DaoSession newDevSession(Context context, String name) {
    Database db = new DevOpenHelper(context, name).getWritableDb();
    DaoMaster daoMaster = new DaoMaster(db);
    return daoMaster.newSession();
}

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

public DaoMaster(Database db) {
    super(db, SCHEMA_VERSION);
    registerDaoClass(UserDao.class);
}

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

public DaoSession newSession(IdentityScopeType type) {
    return new DaoSession(db, type, daoConfigMap);
}

/**
 * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
 */
public static abstract class OpenHelper extends DatabaseOpenHelper {
    public OpenHelper(Context context, String name) {
        super(context, name, SCHEMA_VERSION);
    }

    public OpenHelper(Context context, String name, CursorFactory factory) {
        super(context, name, factory, SCHEMA_VERSION);
    }

    @Override
    public void onCreate(Database db) {
        Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
        createAllTables(db, false);
    }
}

/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
    public DevOpenHelper(Context context, String name) {
        super(context, name);
    }

    public DevOpenHelper(Context context, String name, CursorFactory factory) {
        super(context, name, factory);
    }

    @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);
    }
}

可以看出来:主要是创建数据库,创建表,还有数据库的升级

 

数据库创建和我们原生的一样,用SQLiteOpenHelper 

public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {

getDatabaseLocked

 

  private SQLiteDatabase getDatabaseLocked(boolean writable) {
         // 首先方法接收一个是否可读的参数
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                //数据库没有打开,关闭并且置空
                mDatabase.close().
                mDatabase = null;
            } else if (!writable || !mDatabase.isReadOnly()) {
                //只读或者数据库已经是读写状态了,则直接返回实例
                return mDatabase;
            }
        }

注意:得到数据库都时候,如果没有打开,先关闭并且质空,然后重新开。



DaoMaster构造方法中会把所有的Dao类注册到Map中,每个Dao对应一个DaoConfig配置类。

public abstract class AbstractDaoMaster {
    protected final Database db;
    protected final int schemaVersion;
    protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap;

    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);
    }

 

userDaoConfig = daoConfigMap.get(UserDao.class).clone();

 

public final class DaoConfig implements Cloneable {

Cloneable接口是一个标记接口,也就是没有任何内容,定义如下:

这里分析一下这个接口的用法,clone方法是在Object种定义的,而且是protected型的,只有实现了这个接口,才可以在该类的实例上调用clone方法,否则会抛出CloneNotSupportException。Object中默认的实现是一个浅拷贝,也就是表面拷贝,如

 

public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) {
    this.db = db;
    try {
        this.tablename = (String) daoClass.getField("TABLENAME").get(null);
        Property[] properties = reflectProperties(daoClass);
        this.properties = properties;

        allColumns = new String[properties.length];

        List<String> pkColumnList = new ArrayList<String>();
        List<String> nonPkColumnList = new ArrayList<String>();
        Property lastPkProperty = null;
        for (int i = 0; i < properties.length; i++) {
            Property property = properties[i];
            String name = property.columnName;
            allColumns[i] = name;
            if (property.primaryKey) {
                pkColumnList.add(name);
                lastPkProperty = property;
            } else {
                nonPkColumnList.add(name);
            }
        }
        String[] nonPkColumnsArray = new String[nonPkColumnList.size()];
        nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray);
        String[] pkColumnsArray = new String[pkColumnList.size()];
        pkColumns = pkColumnList.toArray(pkColumnsArray);

        pkProperty = pkColumns.length == 1 ? lastPkProperty : null;
        statements = new TableStatements(db, tablename, allColumns, pkColumns);

        if (pkProperty != null) {
            Class<?> type = pkProperty.type;
            keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class)
                    || type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class)
                    || type.equals(byte.class) || type.equals(Byte.class);
        } else {
            keyIsNumeric = false;
        }

    } catch (Exception e) {
        throw new DaoException("Could not init DAOConfig", e);
    }
}
public class UserDao extends AbstractDao<User, Long> {
AbstractDao
 
public abstract class AbstractDao<T, K> {
protected final DaoConfig config;
protected final Database db;
protected final boolean isStandardSQLite;
protected final IdentityScope<K, T> identityScope;
protected final IdentityScopeLong<T> identityScopeLong;
protected final TableStatements statements;

protected final AbstractDaoSession session;
protected final int pkOrdinal;

private volatile RxDao<T, K> rxDao;
private volatile RxDao<T, K> rxDaoPlain;
 
long insert(T entity) {
    return executeInsert(entity, statements.getInsertStatement(), true);
}
public void update(T entity) {
    assertSinglePk();
    DatabaseStatement stmt = statements.getUpdateStatement();
    if (db.isDbLockedByCurrentThread()) {
        synchronized (stmt) {
            if (isStandardSQLite) {
                updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
            } else {
                updateInsideSynchronized(entity, stmt, true);
            }
        }
    } else {
        // Do TX to acquire a connection before locking the stmt to avoid deadlocks
        db.beginTransaction();
        try {
            synchronized (stmt) {
                updateInsideSynchronized(entity, stmt, true);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

插入的时候用实务的操作

private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
    long rowId;
    if (db.isDbLockedByCurrentThread()) {
        rowId = insertInsideTx(entity, stmt);
    } else {
        // Do TX to acquire a connection before locking the stmt to avoid deadlocks
        db.beginTransaction();
        try {
            rowId = insertInsideTx(entity, stmt);
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }
    if (setKeyAndAttach) {
        updateKeyAfterInsertAndAttach(entity, rowId, true);
    }
    return rowId;
}
 
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();
}
}
}

 

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

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

    String name = entity.getName();
    if (name != null) {
        stmt.bindString(2, name);
    }
}

描述index和数据的关系,最终保存进mBindArgs。到这里,应该就能明白greenDao的核心作用。greenDao将我们熟悉的对象,转换成sql语句和执行参数,再提交SQLite执行。

 

更新数据库:如果当前线程持有,可以直接更新,然后被别的线程持有,要通过实务的操作

}

当前线程获取数据库锁的情况下,直接执行insert操作即可,否则需要使用事务保证操作的原子性和一致性。

public void update(T entity) {
    assertSinglePk();
    DatabaseStatement stmt = statements.getUpdateStatement();
    if (db.isDbLockedByCurrentThread()) {
        synchronized (stmt) {
            if (isStandardSQLite) {
                updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true);
            } else {
                updateInsideSynchronized(entity, stmt, true);
            }
        }
    } else {
        // Do TX to acquire a connection before locking the stmt to avoid deadlocks
        db.beginTransaction();
        try {
            synchronized (stmt) {
                updateInsideSynchronized(entity, stmt, true);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }
}

生成Sql

sql的获取需要用到TableStatements,

实体类:

 

public class UserDao extends AbstractDao<User, Long> {

 

缓存:

}

在执行真正的数据加载前,标记1处先查找缓存,如果有就直接返回,无就去查数据库。标记2处准备sql语句和参数,交给rawQuery查询,得到Cursor。

 

AbstractDao里面:

 

 

 

public T load(K key) {
    assertSinglePk();
    if (key == null) {
        return null;
    }
    if (identityScope != null) {
        T entity = identityScope.get(key);
        if (entity != null) {
            return entity;
        }
    }
    String sql = statements.getSelectByKey();
    String[] keyArray = new String[]{key.toString()};
    Cursor cursor = db.rawQuery(sql, keyArray);
    return loadUniqueAndCloseCursor(cursor);
}

 

 

是一个接口,相当于一个map,增删改查。

 

/**
 * Common interface for a identity scopes needed internally by greenDAO. Identity scopes let greenDAO re-use Java
 * objects.
 * 
 * @author Markus
 * 
 * @param <K>
 *            Key
 * @param <T>
 *            Entity
 */
public interface IdentityScope<K, T> {

    T get(K key);

    void put(K key, T entity);

    T getNoLock(K key);

    void putNoLock(K key, T entity);

    boolean detach(K key, T entity);

    void remove(K key);

    void remove(Iterable<K> key);

    void clear();

    void lock();

    void unlock();

    void reserveRoom(int count);

}

 

 

清除缓存:

identityScope 一个map清除

 

 

public void deleteAll() {
    // String sql = SqlUtils.createSqlDelete(config.tablename, null);
    // db.execSQL(sql);

    db.execSQL("DELETE FROM '" + config.tablename + "'");
    if (identityScope != null) {
        identityScope.clear();
    }
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值