读ActiveAndroid源码(四)

上一章在读DatabaseHelper的初始化时,发现读不动了,因为ModelInfo应该放在前面读。因此,现在开始读ModelInfo。

final class ModelInfo {
    //////////////////////////////////////////////////////////////////////////////////////
    // PRIVATE METHODS
    //////////////////////////////////////////////////////////////////////////////////////

    private Map<Class<? extends Model>, TableInfo> mTableInfos = new HashMap<Class<? extends Model>, TableInfo>();
    private Map<Class<?>, TypeSerializer> mTypeSerializers = new HashMap<Class<?>, TypeSerializer>() {
        {
            put(Calendar.class, new CalendarSerializer());
            put(java.sql.Date.class, new SqlDateSerializer());
            put(java.util.Date.class, new UtilDateSerializer());
            put(java.io.File.class, new FileSerializer());
        }
    };  ……}

  第一个Map mTableInfos储存了类与表参数的的键值对。第二个Map mTypeSerializers储存了SQL的日期,时间,文件的序列化类与接口的键值对。

……
    public ModelInfo(Configuration configuration) {
        if (!loadModelFromMetaData(configuration)) {
            try {
                scanForModel(configuration.getContext());
            }
            catch (IOException e) {
                Log.e("Couldn‘t open source path.", e);
            }
        }

        Log.i("ModelInfo loaded.");
    }
……

  这是ModelInfo的构造方法,里面有两个主要方法一个是loadModelFromMetaData(Configuration),另一个是scanForModel(Context);下面我们一一阅读它们:

    private boolean loadModelFromMetaData(Configuration configuration) {
        if (!configuration.isValid()) {
            return false;
        }

        final List<Class<? extends Model>> models = configuration.getModelClasses();
        if (models != null) {
            for (Class<? extends Model> model : models) {
                mTableInfos.put(model, new TableInfo(model));
            }
        }

        final List<Class<? extends TypeSerializer>> typeSerializers = configuration.getTypeSerializers();
        if (typeSerializers != null) {
            for (Class<? extends TypeSerializer> typeSerializer : typeSerializers) {
                try {
                    TypeSerializer instance = typeSerializer.newInstance();
                    mTypeSerializers.put(instance.getDeserializedType(), instance);
                }
                catch (InstantiationException e) {
                    Log.e("Couldn‘t instantiate TypeSerializer.", e);
                }
                catch (IllegalAccessException e) {
                    Log.e("IllegalAccessException", e);
                }
            }
        }

        return true;
    }

  在上一段代码中我们看到:

……if (!loadModelFromMetaData(configuration)) {
            try {
                scanForModel(configuration.getContext());
            }……

  也就是说,这段代码只会选loadModelFromMetaData或scanForModel中的一个执行,判断的依据则是configuration.isValid()

    public boolean isValid() {
        return mModelClasses != null && mModelClasses.size() > 0;
    }

  显而易见,当configuration中的mModelClasses没有值时,代码会执行loadModelFromMetaData中的主要代码,否则执行scanForModel中的代码。

  先看loadModelFromMetaData中的主要代码:

    private boolean loadModelFromMetaData(Configuration configuration) {
        if (!configuration.isValid()) {
            return false;
        }

        final List<Class<? extends Model>> models = configuration.getModelClasses();
        if (models != null) {
            for (Class<? extends Model> model : models) {
                mTableInfos.put(model, new TableInfo(model));
            }
        }

        final List<Class<? extends TypeSerializer>> typeSerializers = configuration.getTypeSerializers();
        if (typeSerializers != null) {
            for (Class<? extends TypeSerializer> typeSerializer : typeSerializers) {
                try {
                    TypeSerializer instance = typeSerializer.newInstance();
                    mTypeSerializers.put(instance.getDeserializedType(), instance);
                }
                catch (InstantiationException e) {
                    Log.e("Couldn‘t instantiate TypeSerializer.", e);
                }
                catch (IllegalAccessException e) {
                    Log.e("IllegalAccessException", e);
                }
            }
        }

        return true;
    }

  这一段代码,主要是从configuration中取出modelClasses和typeSerializers一个可存对象的列表,一个序列化列表。并为每一个model新建TableInfo,为每一种序列化新建对象。通常情况下,除非我们专门设置了mModelClasses,默认情况下,它是空的。因此,默认情况下,我们的代码不会走到这个分支。所以,通常会进入另一个分支scanForModel:

    private void scanForModel(Context context) throws IOException {
        String packageName = context.getPackageName();
        String sourcePath = context.getApplicationInfo().sourceDir;
        List<String> paths = new ArrayList<String>();

        if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
            DexFile dexfile = new DexFile(sourcePath);
            Enumeration<String> entries = dexfile.entries();

            while (entries.hasMoreElements()) {
                paths.add(entries.nextElement());
            }
        }
        // Robolectric fallback
        else {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> resources = classLoader.getResources("");

            while (resources.hasMoreElements()) {
                String path = resources.nextElement().getFile();
                if (path.contains("bin") || path.contains("classes")) {
                    paths.add(path);
                }
            }
        }

        for (String path : paths) {
            File file = new File(path);
            scanForModelClasses(file, packageName, context.getClassLoader());
        }
    }

   一行一行看:

        String packageName = context.getPackageName();
        String sourcePath = context.getApplicationInfo().sourceDir;
        List<String> paths = new ArrayList<String>();

  首先获取包面和app的安装路径,并初始化路径列表。

        if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
            DexFile dexfile = new DexFile(sourcePath);
            Enumeration<String> entries = dexfile.entries();
       while (entries.hasMoreElements()) {          paths.add(entries.nextElement());       }

  接着可以看到DexFile:google官方文档中这样解释Opens a DEX file from a given filename. This will usually be a ZIP/JAR file with a "classes.dex" inside. 我们直接打开了apk包,用apk包的中路径信息创建了DexFile对象。并用枚举型迭代器Enumeration,将apk中的所有路径加入到paths中。

  通常使用时是不会进else部分的,else部分暂时先不看。

  接下来是

        for (String path : paths) {
            File file = new File(path);
            scanForModelClasses(file, packageName, context.getClassLoader());
        }

  获得 apk中所有路径后,将依次寻找这些路径中的model类。

    private void scanForModelClasses(File path, String packageName, ClassLoader classLoader) {
        if (path.isDirectory()) {
            for (File file : path.listFiles()) {
                scanForModelClasses(file, packageName, classLoader);
            }
        }
        else {
            String className = path.getName();

            // Robolectric fallback
            if (!path.getPath().equals(className)) {
                className = path.getPath();

                if (className.endsWith(".class")) {
                    className = className.substring(0, className.length() - 6);
                }
                else {
                    return;
                }

                className = className.replace(System.getProperty("file.separator"), ".");

                int packageNameIndex = className.lastIndexOf(packageName);
                if (packageNameIndex < 0) {
                    return;
                }

                className = className.substring(packageNameIndex);
            }

            try {
                Class<?> discoveredClass = Class.forName(className, false, classLoader);
                if (ReflectionUtils.isModel(discoveredClass)) {
                    @SuppressWarnings("unchecked")
                    Class<? extends Model> modelClass = (Class<? extends Model>) discoveredClass;
                    mTableInfos.put(modelClass, new TableInfo(modelClass));
                }
                else if (ReflectionUtils.isTypeSerializer(discoveredClass)) {
                    TypeSerializer instance = (TypeSerializer) discoveredClass.newInstance();
                    mTypeSerializers.put(instance.getDeserializedType(), instance);
                }
            }
            catch (ClassNotFoundException e) {
                Log.e("Couldn‘t create class.", e);
            }
            catch (InstantiationException e) {
                Log.e("Couldn‘t instantiate TypeSerializer.", e);
            }
            catch (IllegalAccessException e) {
                Log.e("IllegalAccessException", e);
            }
        }
    }

  方法通过一个递归,遍历目录及子目录下的所有文件:

            if (!path.getPath().equals(className)) {
                className = path.getPath();

                if (className.endsWith(".class")) {
                    className = className.substring(0, className.length() - 6);
                }
                else {
                    return;
                }

                className = className.replace(System.getProperty("file.separator"), ".");

                int packageNameIndex = className.lastIndexOf(packageName);
                if (packageNameIndex < 0) {
                    return;
                }

                className = className.substring(packageNameIndex);
            }

  如果path.getName()与path.getPath不相等,则从修正文件的名字已获取类名,其中System.getProperty("file.separator")的意思是获得当前系统下分隔符的符号。

  接下来:

            try {
                Class<?> discoveredClass = Class.forName(className, false, classLoader);
                if (ReflectionUtils.isModel(discoveredClass)) {
                    @SuppressWarnings("unchecked")
                    Class<? extends Model> modelClass = (Class<? extends Model>) discoveredClass;
                    mTableInfos.put(modelClass, new TableInfo(modelClass));
                }
                else if (ReflectionUtils.isTypeSerializer(discoveredClass)) {
                    TypeSerializer instance = (TypeSerializer) discoveredClass.newInstance();
                    mTypeSerializers.put(instance.getDeserializedType(), instance);
                }
            }
            catch (ClassNotFoundException e) {
                Log.e("Couldn‘t create class.", e);
            }
            catch (InstantiationException e) {
                Log.e("Couldn‘t instantiate TypeSerializer.", e);
            }
            catch (IllegalAccessException e) {
                Log.e("IllegalAccessException", e);
            }

  找到继承于model的类名后,用类的相关信息构造TableInfo类,并将tableInfo的对象放入mTableInfos中储存。至此,ModelInfo类才算是基本地构造完成。

至此,我们发现,在ActiveAndroid框架下,数据及数据的关系存在了三个类之中,分别是Conguration、TableInfo和ModelInfo。下一章,我将试着总结一下这三个类分别储存了哪些信息。

Done.

 

时间: 2024-10-05 06:48:14

读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源码(三)

上一章,我们读完了Conguration这个类.最后我们发现,Conguration就是为了数据库的初始化.其中包含了内存大小.数据库名称.数据库版本.parser信息. public static synchronized void initialize(Configuration configuration) { if (sIsInitialized) { Log.v("ActiveAndroid already initialized."); return; } sContext

读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

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

(转)读过滤器源码

今天按着Artech的文章又研究了研究过滤器的源码,主要究其实现,还有一点不清晰的地方,可惜周末得回趟家,老妈身体小样.周末只能小看一下书了.继续研究,过两天搞两个例子多试下,研究完了源码及其实现,清晰多了.要不用的总感觉不太清楚.还有几篇文章也看了,现在就不贴出了,等些例子的时候会一起给出. 在ActionInvoker对Action的执行过程中,除了通过利用ActionDescriptor对Action方法的执行,以及之前进行的Model绑定与验证之外,还具有一个重要的工作,那就是对相关筛选

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

&lt;转&gt;【读fastclick源码有感】彻底解决tap“点透”,提升移动端点击响应速度

[读fastclick源码有感]彻底解决tap“点透”,提升移动端点击响应速度 前言 读fastclick源码 绑定事件 stopImmediatePropagation 测试入口 帮助理解的图 为什么zepto会点透/fastclick如何解决点透 后记 结语 申明!!!最后发现判断有误,各位读读就好,正在研究中.....尼玛水太深了 前言 近期使用tap事件为老夫带来了这样那样的问题,其中一个问题是解决了点透还需要将原来一个个click变为tap,这样的话我们就抛弃了ie用户当然可以做兼容,

读Zepto源码之样式操作

这篇依然是跟 dom 相关的方法,侧重点是操作样式的方法. 读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的源码为 zepto1.2.0 内部方法 classRE classCache = {} function classRE(name) { return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name