Atlas 源码解析(一)

简述:这篇文章仅仅介绍atlas的基本实现原理

这是atals中bundle加载过程包括class 和 resource

第15步:

   if(!installingBundles.containsKey(location) && ((bundleDir!=null&&bundleDir.exists()) || dexPatchDir!=null&&dexPatchDir.exists())){
            try {
                BundleContext bcontext = new BundleContext();
                bcontext.bundle_tag = bundleUniqueTag;
                bcontext.location = location;
                bcontext.bundleDir = bundleDir;
                bcontext.dexPatchDir = dexPatchDir;
                bundle = new BundleImpl(bcontext);
                if (bundle != null) {
                    bundle.optDexFile();
                }
            } catch (Exception e) {
                if (e instanceof BundleArchive.MisMatchException) {
                    if (bundleDir.exists()) {
                        bundle = null;
                    }
                }

可以看到bundle.optDexFile() 点进去看看

    public /*synchronized*/ void optDexFile() {
    //getArchive()获取bundle压缩文件的描述返回的是BundleArchive对象
    //调用BundleArchive的optDexFile()方法
        this.getArchive().optDexFile();
    }

接着点

    public void optDexFile() {
    //BundleArchive 对象仅仅是对 BundleArchiveRevision的一层封装
    //BundleArchiveRevision才是对Bundle的so文件的真正描述
    //currentRevision 是BundleArchive 的实例指当前Bundle压缩文件对象
        currentRevision.optDexFile();
    }

接着点

    public /*synchronized*/ void optDexFile() {

        if (isDexOpted()){
            return;
        }
        optDexFileLocked();
    }

接着点入optDexFileLocked()

关键代码 Build.VERSION.SDK_INT>=21
 dexFile = (DexFile) RuntimeVariables.sDexLoadBooster.getClass().getDeclaredMethod("loadDex",Context.class,String.class, String.class, int.class, boolean.class).invoke(
                        RuntimeVariables.sDexLoadBooster,
                        RuntimeVariables.androidApplication,
                        bundleFile.getAbsolutePath(),
                        odexFile.getAbsolutePath(),
                        0,
                        interpretOnly);

RuntimeVariables.sDexLoadBooster 是DexLoadBooster() 对象,是在AtlasBridgeApplication 中初始化,在BridgeApplicationDelegate 中赋值给RuntimeVariables.sDexLoadBooster 的。
所以这段反射代码最终执行的方法是DexLoadBooster()的loadDex 方法 代码如下:

    public DexFile loadDex(Context context, String sourcePathName, String outputPathName, int flags,
                           boolean interpretOnly) throws IOException {
        return AndroidRuntime.getInstance().loadDex(context, sourcePathName, outputPathName, flags, interpretOnly);
    }

接着点

 DexFile.loadDex(sourcePathName, outputPathName, flags)

这就是Android代码加载Dex文件的代码了

现在已经将dex文件加载到内存中封装成对象了,接着查BundleClassLoader的代码,这个流程是class文件的加载过程

clazz = ((BundleClassLoader)impl.getClassLoader()).loadOwnClass(classname);
   public Class loadOwnClass(String className) throws ClassNotFoundException {
        Class clazz = findLoadedClass(className);

        if (clazz == null) {
            clazz = findOwnClass(className);
        }
        return clazz;
    }
    private Class findOwnClass(final String classname) {
        try {
            Class clazz = archive.findClass(classname, this);
            return clazz;
        } catch (Exception e) {
            if (e instanceof BundleArchiveRevision.DexLoadException) {
                throw (BundleArchiveRevision.DexLoadException)e;
            }
        }
        return null;
    }
    public Class findClass(String className, ClassLoader cl) throws ClassNotFoundException {
        return currentRevision.findClass(className, cl);
    }
clazz = dexFile.loadClass(className, cl);

到这里我们的上面拿到的DexFile对象用上了,并且调用了loadClass方法
查看loadClass方法

    public Class loadClass(String name, ClassLoader loader) {
        String slashName = name.replace('.', '/');
        return loadClassBinaryName(slashName, loader);
    }

点入:

    public Class loadClassBinaryName(String name, ClassLoader loader) {
        return defineClass(name, loader, mCookie);
    }

点入:

private native static Class defineClass(String name, ClassLoader loader, int cookie);

发现这是一个native方法,到此结束
总结:Atlas 通过反射为每个Bundle创建了一个ClassLoader ,将so文件中的dex文件加载到内存中以后最终调用defineClass方法加载class文件。

附属DexFile源码:

package dalvik.system;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
/**
 * Manipulates DEX files. The class is similar in principle to
 * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
 * 

* Note we don't directly open and read the DEX file here. They're memory-mapped * read-only by the VM. */ public final class DexFile { private int mCookie; private final String mFileName; private final CloseGuard guard = CloseGuard.get(); /** * Opens a DEX file from a given File object. This will usually be a ZIP/JAR * file with a "classes.dex" inside. * * The VM will generate the name of the corresponding file in * /data/dalvik-cache and open it, possibly creating or updating * it first if system permissions allow. Don't pass in the name of * a file in /data/dalvik-cache, as the named file is expected to be * in its original (pre-dexopt) state. * * @param file * the File object referencing the actual DEX file * * @throws IOException * if an I/O error occurs, such as the file not being found or * access rights missing for opening it */ public DexFile(File file) throws IOException { this(file.getPath()); } /** * Opens a DEX file from a given filename. This will usually be a ZIP/JAR * file with a "classes.dex" inside. * * The VM will generate the name of the corresponding file in * /data/dalvik-cache and open it, possibly creating or updating * it first if system permissions allow. Don't pass in the name of * a file in /data/dalvik-cache, as the named file is expected to be * in its original (pre-dexopt) state. * * @param fileName * the filename of the DEX file * * @throws IOException * if an I/O error occurs, such as the file not being found or * access rights missing for opening it */ public DexFile(String fileName) throws IOException { mCookie = openDexFile(fileName, null, 0); mFileName = fileName; guard.open("close"); //System.out.println("DEX FILE cookie is " + mCookie); } /** * Opens a DEX file from a given filename, using a specified file * to hold the optimized data. * * @param sourceName * Jar or APK file with "classes.dex". * @param outputName * File that will hold the optimized form of the DEX data. * @param flags * Enable optional features. */ private DexFile(String sourceName, String outputName, int flags) throws IOException { mCookie = openDexFile(sourceName, outputName, flags); mFileName = sourceName; guard.open("close"); //System.out.println("DEX FILE cookie is " + mCookie); } /** * Open a DEX file, specifying the file in which the optimized DEX * data should be written. If the optimized form exists and appears * to be current, it will be used; if not, the VM will attempt to * regenerate it. * * This is intended for use by applications that wish to download * and execute DEX files outside the usual application installation * mechanism. This function should not be called directly by an * application; instead, use a class loader such as * dalvik.system.DexClassLoader. * * @param sourcePathName * Jar or APK file with "classes.dex". (May expand this to include * "raw DEX" in the future.) * @param outputPathName * File that will hold the optimized form of the DEX data. * @param flags * Enable optional features. (Currently none defined.) * @return * A new or previously-opened DexFile. * @throws IOException * If unable to open the source or output file. */ static public DexFile loadDex(String sourcePathName, String outputPathName, int flags) throws IOException { /* * TODO: we may want to cache previously-opened DexFile objects. * The cache would be synchronized with close(). This would help * us avoid mapping the same DEX more than once when an app * decided to open it multiple times. In practice this may not * be a real issue. */ return new DexFile(sourcePathName, outputPathName, flags); } /** * Gets the name of the (already opened) DEX file. * * @return the file name */ public String getName() { return mFileName; } /** * Closes the DEX file. *

* This may not be able to release any resources. If classes from this * DEX file are still resident, the DEX file can't be unmapped. * * @throws IOException * if an I/O error occurs during closing the file, which * normally should not happen */ public void close() throws IOException { guard.close(); closeDexFile(mCookie); mCookie = 0; } /** * Loads a class. Returns the class on success, or a {@code null} reference * on failure. *

* If you are not calling this from a class loader, this is most likely not * going to do what you want. Use {@link Class#forName(String)} instead. *

* The method does not throw {@link ClassNotFoundException} if the class * isn't found because it isn't reasonable to throw exceptions wildly every * time a class is not found in the first DEX file we look at. * * @param name * the class name, which should look like "java/lang/String" * * @param loader * the class loader that tries to load the class (in most cases * the caller of the method * * @return the {@link Class} object representing the class, or {@code null} * if the class cannot be loaded */ public Class loadClass(String name, ClassLoader loader) { String slashName = name.replace('.', '/'); return loadClassBinaryName(slashName, loader); } /** * See {@link #loadClass(String, ClassLoader)}. * * This takes a "binary" class name to better match ClassLoader semantics. * * @hide */ public Class loadClassBinaryName(String name, ClassLoader loader) { return defineClass(name, loader, mCookie); } private native static Class defineClass(String name, ClassLoader loader, int cookie); /** * Enumerate the names of the classes in this DEX file. * * @return an enumeration of names of classes contained in the DEX file, in * the usual internal form (like "java/lang/String"). */ public Enumeration entries() { return new DFEnum(this); } /* * Helper class. */ private class DFEnum implements Enumeration<String> { private int mIndex; private String[] mNameList; DFEnum(DexFile df) { mIndex = 0; mNameList = getClassNameList(mCookie); } public boolean hasMoreElements() { return (mIndex < mNameList.length); } public String nextElement() { return mNameList[mIndex++]; } } /* return a String array with class names */ native private static String[] getClassNameList(int cookie); /** * Called when the class is finalized. Makes sure the DEX file is closed. * * @throws IOException * if an I/O error occurs during closing the file, which * normally should not happen */ @Override protected void finalize() throws Throwable { try { if (guard != null) { guard.warnIfOpen(); } close(); } finally { super.finalize(); } } /* * Open a DEX file. The value returned is a magic VM cookie. On * failure, an IOException is thrown. */ native private static int openDexFile(String sourceName, String outputName, int flags) throws IOException; /* * Open a DEX file based on a {@code byte[]}. The value returned * is a magic VM cookie. On failure, a RuntimeException is thrown. */ native private static int openDexFile(byte[] fileContents); /* * Close DEX file. */ native private static void closeDexFile(int cookie); /** * Returns true if the VM believes that the apk/jar file is out of date * and should be passed through "dexopt" again. * * @param fileName the absolute path to the apk/jar file to examine. * @return true if dexopt should be called on the file, false otherwise. * @throws java.io.FileNotFoundException if fileName is not readable, * not a file, or not present. * @throws java.io.IOException if fileName is not a valid apk/jar file or * if problems occur while parsing it. * @throws java.lang.NullPointerException if fileName is null. * @throws dalvik.system.StaleDexCacheError if the optimized dex file * is stale but exists on a read-only partition. */ native public static boolean isDexOptNeeded(String fileName) throws FileNotFoundException, IOException; }

你可能感兴趣的:(android)