上一章在读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.

 

 

 

 

 

 

 

posted on 2015-10-24 13:29  Fishbonell  阅读(175)  评论(0编辑  收藏  举报