• 读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.

     

  • 相关阅读:
    乘法DAC一点知识
    #4 判断字符串是否为整数
    #3 不使用循环输出1到100
    #2 判断一个字符串是否包含重复字符
    #22 结语
    #1 组成互不相同且不重复的三位数
    #21 Python异常
    #19 re&jieba模块
    2020国庆正睿笔记
    2019正睿csp-s赛前冲刺
  • 原文地址:https://www.cnblogs.com/fishbone-lsy/p/4893482.html
Copyright © 2020-2023  润新知