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.
 * <p>
 * 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.
     * <p>
     * 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.
     * <p>
     * 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.
     * <p>
     * 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<String> 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;
}

猜你喜欢

转载自blog.csdn.net/lovebuzhidao/article/details/79578307