读ActiveAndroid源码(三)

  上一章,我们读完了Conguration这个类。最后我们发现,Conguration就是为了数据库的初始化。其中包含了内存大小、数据库名称、数据库版本、parser信息。 

public static synchronized void initialize(Configuration configuration) {
        if (sIsInitialized) {
            Log.v("ActiveAndroid already initialized.");
            return;
        }

        sContext = configuration.getContext();
        sModelInfo = new ModelInfo(configuration);
        sDatabaseHelper = new DatabaseHelper(configuration);

        // TODO: It would be nice to override sizeOf here and calculate the memory
        // actually used, however at this point it seems like the reflection
        // required would be too costly to be of any benefit. We‘ll just set a max
        // object size instead.
        sEntities = new LruCache<String, Model>(configuration.getCacheSize());

        openDatabase();

        sIsInitialized = true;

        Log.v("ActiveAndroid initialized successfully.");
    }

   上一章读到了这里,需要初始化一个DatabaseHelper,作为数据库的辅助类。今天来阅读这个类:

public final class DatabaseHelper extends SQLiteOpenHelper {
    //////////////////////////////////////////////////////////////////////////////////////
    // PUBLIC CONSTANTS
    //////////////////////////////////////////////////////////////////////////////////////

    public final static String MIGRATION_PATH = "migrations";

    //////////////////////////////////////////////////////////////////////////////////////
    // PRIVATE FIELDS
    //////////////////////////////////////////////////////////////////////////////////////

    private final String mSqlParser;

    //////////////////////////////////////////////////////////////////////////////////////
    // CONSTRUCTORS
    //////////////////////////////////////////////////////////////////////////////////////

    public DatabaseHelper(Configuration configuration) {
        super(configuration.getContext(), configuration.getDatabaseName(), null, configuration.getDatabaseVersion());
        copyAttachedDatabase(configuration.getContext(), configuration.getDatabaseName());
        mSqlParser = configuration.getSqlParser();
    }
    ……
}

  由于DatabaseHelper这个类继承了SQLiteOpenHelper,构造时先构造父类:

    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }

  这个构造方法需要输入context、name、version,这三个参数在Conguration类中都有。

  将下来是copyAttachedDatabase方法

    public void copyAttachedDatabase(Context context, String databaseName) {
        final File dbPath = context.getDatabasePath(databaseName);

        // If the database already exists, return
        if (dbPath.exists()) {
            return;
        }

        // Make sure we have a path to the file
        dbPath.getParentFile().mkdirs();

        // Try to copy database file
        try {
            final InputStream inputStream = context.getAssets().open(databaseName);
            final OutputStream output = new FileOutputStream(dbPath);

            byte[] buffer = new byte[8192];
            int length;

            while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
                output.write(buffer, 0, length);
            }

            output.flush();
            output.close();
            inputStream.close();
        }
        catch (IOException e) {
            Log.e("Failed to open file", e);
        }
    }

  这个方法主要是 final InputStream inputStream = context.getAssets().open(databaseName); 获取assets中的数据库资源。如果在相同目录下,已经存在这个数据库就不会进行后面的操作。

  然后,再往下看就是数据库的onCreate方法:

    @Override
    public void onCreate(SQLiteDatabase db) {
        executePragmas(db);
        executeCreate(db);
        executeMigrations(db, -1, db.getVersion());
        executeCreateIndex(db);
    }

  一个一个方法看:

    private void executePragmas(SQLiteDatabase db) {
        if (SQLiteUtils.FOREIGN_KEYS_SUPPORTED) {
            db.execSQL("PRAGMA foreign_keys=ON;");
            Log.i("Foreign Keys supported. Enabling foreign key features.");
        }
    }

  这个方法主要是开始外键可用。

  关于外键,我身为一个野生伪程序员,看了下百度知道

关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键
比如
学生表(学号,姓名,性别,班级)
其中每个学生的学号是唯一的,学号就是一个主键
课程表(课程编号,课程名,学分)
其中课程编号是唯一的,课程编号就是一个主键
成绩表(学号,课程号,成绩)
成绩表中单一一个属性无法唯一标识一条记录,学号和课程号的组合才可以唯一标识一条记录,所以 学号和课程号的属性组是一个主键 

成绩表中的学号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键 

同理 成绩表中的课程号是课程表的外键 

定义主键和外键主要是为了维护关系数据库的完整性,总结一下:
主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。 

  继续看数据库onCreate方法中的第二个方法 executeCreate(db);

  

    private void executeCreate(SQLiteDatabase db) {
        db.beginTransaction();
        try {
            for (TableInfo tableInfo : Cache.getTableInfos()) {
                db.execSQL(SQLiteUtils.createTableDefinition(tableInfo));
            }
            db.setTransactionSuccessful();
        }
        finally {
            db.endTransaction();
        }
    }

  这个方法用来创建表的一些,首先来看看Cache.getTableInfos()这个方法:

    public static synchronized Collection<TableInfo> getTableInfos() {
        return sModelInfo.getTableInfos();
    }

  读到这里就断了,因为我们不知道ModelInfo是什么,在本章开头我们看到:

public static synchronized void initialize(Configuration configuration) {
        if (sIsInitialized) {
            Log.v("ActiveAndroid already initialized.");
            return;
        }

        sContext = configuration.getContext();
        sModelInfo = new ModelInfo(configuration);
        sDatabaseHelper = new DatabaseHelper(configuration);

        // TODO: It would be nice to override sizeOf here and calculate the memory
        // actually used, however at this point it seems like the reflection
        // required would be too costly to be of any benefit. We‘ll just set a max
        // object size instead.
        sEntities = new LruCache<String, Model>(configuration.getCacheSize());

        openDatabase();

        sIsInitialized = true;

        Log.v("ActiveAndroid initialized successfully.");
    }

  我们直接开始分析sDatabaseHelper = new DatabaseHelper(configuration);并没有阅读sModelInfo = new ModelInfo(configuration);导致信息的遗漏,因此我们只好倒过头来读ModelInfo这个类。这个失误有点伤,下次再来读:

Done

时间: 2024-10-23 04:06:04

读ActiveAndroid源码(三)的相关文章

读ActiveAndroid源码(五)

前面几篇,断断续续地囫囵吞枣地读了ActiveAndroid的部分源码,大致了解了ActiveAndroid的注解反射原理.其中很多细节还不算很清楚,加之内容非常多,为了更好地阅读接下来的内容,在此对前面阅读的部分作一个总结. 在之前的几篇中,重点阅读了ActiveAndroid中的三个类:Conguration , ModelInfo , TableInfo.下面将对这三个类的作用做一个简单地总结: 一.Conguration 先看一下Conguration的成员变量: public fina

读ActiveAndroid源码(二)

上一次粗略地读了一下TableInfo这个类,了解了一下ActiveAndroid中注解的使用方法,算是一个预热,这一篇,从正常的顺序开始. 所以,这次从ActiveAndroid的初始化开始阅读. public class Application extends android.app.Application { @Override public void onCreate() { super.onCreate(); ActiveAndroid.initialize(this); } @Ove

读ActiveAndroid源码(四)

上一章在读DatabaseHelper的初始化时,发现读不动了,因为ModelInfo应该放在前面读.因此,现在开始读ModelInfo. final class ModelInfo { ////////////////////////////////////////////////////////////////////////////////////// // PRIVATE METHODS //////////////////////////////////////////////////

读ActiveAndroid源码(一)

首先ActiveAndroid是依靠注解工作的. @Table(name = "UserBean") public class UserBean extends Model { @Column(name = "uid") public String uid; @Column(name = "nick_name") public String nick_name; public String getUid() { return uid; } pub

读spring源码(三)-ClassPathXmlApplicationContext-getBean

这次主要看了下bean的生成过程,发现个画时序图很好用的软件plantuml,充分发挥程序员的能力,能用代码解决的别叨叨别的?? 1.调用ApplicationContext的genBean方法会调用到AbstractApplicationContext的getBean方法,这个方法里面其实就是交由BeanFactory调用getBean 2.DefaultListableBeanFactory中会先根据类型获取beanNames,然后根据beanName调用AbstractBeanFactor

读 zepto 源码之工具函数

Zepto 提供了丰富的工具函数,下面来一一解读. 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目标对象的属性.目标对象的同名属性会被源对象的属性覆盖. $.extend 其实调用的是内部方法 extend, 所以我们先看看内部方法 extend 的具体实现. function extend(target, source, deep) { for (key in source) // 遍历源对象的属性值 if (deep && (i

读 Zepto 源码之内部方法

数组方法 定义 var emptyArray = [] concat = emptyArray.concat filter = emptyArray.filter slice = emptyArray.slice zepto 一开始就定义了一个空数组 emptyArray,定义这个空数组是为了取得数组的 concat.filter.slice 方法 compact function compact(array) { return filter.call(array, function(item)

读jQuery源码之四(Callbacks,Deferred,when)

看了下Sizzle部分源码,核心的原理就是使用正则表达式去匹配,找到对应的原生获取元素的方法,我没有去细究了.大家有兴趣可以自己看看,分享分享! 从2850行开始,继续往下读jQuery源码(2850-3043行) 进入Callbacks(回调函数管理模块)之前,有几个扩展方法 1.dir方法 三个参数:elem——dom元素,dir——指定elem的层级名称(例如parentNode,nextSibling),until——结束判断.返回一个数组,比如获取某个元素的parentNode,如果不

session自定义存储,如何更好地进行session共享;读tomcat7源码,org.apache.catalina.session.FileStore可知

session自定义存储,如何更好地进行session共享: 读tomcat源码,org.apache.catalina.session.FileStore可知 一.详见: 方法1 public void save(Session session) try { ((StandardSession)session).writeObjectData(oos); } finally { oos.close(); } 方法2 public Session load(String id) ois = ne