美文网首页
ClassLoader源码学习 -- PathClassLoad

ClassLoader源码学习 -- PathClassLoad

作者: Amberllo | 来源:发表于2020-03-23 15:13 被阅读0次

    ClassLoader的源码学习路径:
    ClassLoader源码学习-- 学习源码的方法
    ClassLoader源码学习 -- JVM启动之 Launcher,ClassLoader构建
    ClassLoader源码学习-- ClassLoader的创建 -- Android Pie
    ClassLoader源码学习 -- PathClassLoader,DexClassLoader

    上一节,我们学习了ClassLoader的默认创建,现在我们继续深入学习。

    先附上学习demo:
    github地址:
    https://github.com/Amberllo/ClassLoaderStudy
    这里只是简单的把Asset目录下的apk,拷贝到文件夹中,使用ClassLoader直接加载里面的类
    遇到奇怪的问题,Environment.getExtendStorage目录下,无法成功。

    这次学习主要是根据sdk 28, Android 9.0 pie,下载相关的源码。https://www.androidos.net.cn/sourcecode

    image.png

    文件清单:

    /libcore/ojluni/src/main/java/java/lang/ClassLoader.java
    /libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
    /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
    /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
    /libcore/dalvik/src/main/java/dalvik/system/DexFile.java
    /art/runtime/native/dalvik_system_DexFile.cc

    DexClassLoader和PathClassLoader区别

    • 在targetSdk 26, 是不一样,optimizedDirectory用于声明dex2oat后oat存放的目录。
    • 在targetSdk 28, 是完全一样,optimizedDirectory根本没有用到,注释写得很清楚了。

    大量的博客文章表示,DexClassLoader能加载jar, aar, 未安装的apk, PathClassLoader只能装已安装的apk。
    这么说其实是片面的,因为targetSdkVersion 26和28的源码,是不一样的(google偷偷的修bug?)

    https://www.android.net.cn 下搜索DexClassLoader,有两个版本

    image.png
        // sdk 28下构造函数
        public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
            super(dexPath, null, librarySearchPath, parent);
        }
    
        //sdk 26的构造函数
        public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
        }
    

    PathClassLoader,则是在26和28是一样的代码

        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super(dexPath, null, librarySearchPath, parent);
        }
    

    结论可知, 在targetSdkVersion 28的时候,其实DexClassLoader和PathClassLoader,其实是一模一样的。面试的时候注意不要被面试官坑了。
    仅仅就构造函数中,optimizedDirectory这个参数不一样,在api 28是完全一样的

    那么optimizedDirectory真的是可有可无么?

    我们可以直接把焦点,聚焦到BaseDexClassLoader的构造函数:
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String librarySearchPath, ClassLoader parent, boolean isTrusted) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    
            if (reporter != null) {
                reportClassLoaderChain();
            }
        }
    

    optimizedDirectory这个参数,没有参与任何的逻辑

    BaseDexClassLoader的重要参与者,DexPathList

    1. 用于存放接续Dex文件后的Elements数组以外,
    2. findClass的职责,也是pathList里面查找。

    BaseDexClassLoader

        private static volatile Reporter reporter = null;
    
        private final DexPathList pathList;
    
        /**
         * Constructs an instance.
         * Note that all the *.jar and *.apk files from {@code dexPath} might be
         * first extracted in-memory before the code is loaded. This can be avoided
         * by passing raw dex files (*.dex) in the {@code dexPath}.
         *
         * @param dexPath the list of jar/apk files containing classes and
         * resources, delimited by {@code File.pathSeparator}, which
         * defaults to {@code ":"} on Android.
         * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26.
         * @param librarySearchPath the list of directories containing native
         * libraries, delimited by {@code File.pathSeparator}; may be
         * {@code null}
         * @param parent the parent class loader
         */
        public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String librarySearchPath, ClassLoader parent) {
            this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
        }
    
        /**
         * @hide
         */
        public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String librarySearchPath, ClassLoader parent, boolean isTrusted) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    
            if (reporter != null) {
                reportClassLoaderChain();
            }
        }
    

    在英文的注释上面,写得很清楚,翻译的意思是:
    dexPath:jar文件和apk文件所在路径, 用"/"当分隔符,默认值为":"
    optimizedDirectory:API level 26.不生效了
    librarySearchPath:native Library路径,有可能为空
    parent:父classLoader

    系统默认的CreateSystemClassLoader中,传入的参数为:
    String classPath = System.getProperty("java.class.path", ".");
    String librarySearchPath = System.getProperty("java.library.path", "");

    打印得出值:
    classPath = . , 直接为空
    librarySearchPath = /system/lib:/system/product/lib, 系统里面一些.so库

    BaseDexClassLoader的构造函数中,调用了super(parent)

        protected ClassLoader(ClassLoader parent) {
            this(checkCreateClassLoader(), parent);
        }
    
        //checkCreateClassLoader什么都没做,直接一个空
        private static Void checkCreateClassLoader() {
            return null;
        }
    

    看来BaseDexClassLoader的构造函数,super没做什么事。
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);

    在BaseDexClassLoader中,随处可见DexPathList的身影。
    我们 移步 DexPathList.java

    DexPathList 有三个构造函数,区别在dexFiles数组从外部传入,还是从路径生成。

        /** class definition context */
        private final ClassLoader definingContext;
    
        /**
         * List of dex/resource (class path) elements.
         * Should be called pathElements, but the Facebook app uses reflection
         * to modify 'dexElements' (http://b/7726934).
         */
        private Element[] dexElements;
    
        /** List of native library path elements. */
        // Some applications rely on this field being an array or we'd use a final list here
        /* package visible for testing */ NativeLibraryElement[] nativeLibraryPathElements;
    
        /** List of application native library directories. */
        private final List<File> nativeLibraryDirectories;
    
        /** List of system native library directories. */
        private final List<File> systemNativeLibraryDirectories;
    
        /**
         * Exceptions thrown during creation of the dexElements list.
         */
        private IOException[] dexElementsSuppressedExceptions;
    
        DexPathList(ClassLoader definingContext, String dexPath,
                String librarySearchPath, File optimizedDirectory, boolean isTrusted){
            if (definingContext == null) {
                throw new NullPointerException("definingContext == null");
            }
    
            if (dexPath == null) {
                throw new NullPointerException("dexPath == null");
            }
    
            if (optimizedDirectory != null) {
                if (!optimizedDirectory.exists())  {
                    throw new IllegalArgumentException(
                            "optimizedDirectory doesn't exist: "
                            + optimizedDirectory);
                }
    
                if (!(optimizedDirectory.canRead()
                                && optimizedDirectory.canWrite())) {
                    throw new IllegalArgumentException(
                            "optimizedDirectory not readable/writable: "
                            + optimizedDirectory);
                }
            }
    
            this.definingContext = definingContext;
    
            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
            // save dexPath for BaseDexClassLoader
            this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                               suppressedExceptions, definingContext, isTrusted);
    
            // Native libraries may exist in both the system and
            // application library paths, and we use this search order:
            //
            //   1. This class loader's library path for application libraries (librarySearchPath):
            //   1.1. Native library directories
            //   1.2. Path to libraries in apk-files
            //   2. The VM's library path from the system property for system libraries
            //      also known as java.library.path
            //
            // This order was reversed prior to Gingerbread; see http://b/2933456.
            this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
            this.systemNativeLibraryDirectories =
                    splitPaths(System.getProperty("java.library.path"), true);
            List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
            allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
    
            this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
    
            if (suppressedExceptions.size() > 0) {
                this.dexElementsSuppressedExceptions =
                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
            } else {
                dexElementsSuppressedExceptions = null;
            }
    }
    

    其中两个重要的方法,makeDexElements,以及填充systemNativeLibrary和appNativeLibrary,还有可能会抛出的异常。

    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
                List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
          Element[] elements = new Element[files.size()];
          int elementsPos = 0;
          /*
           * Open all files and load the (direct or contained) dex files up front.
           */
          for (File file : files) {
              if (file.isDirectory()) {
                  // We support directories for looking up resources. Looking up resources in
                  // directories is useful for running libcore tests.
                  elements[elementsPos++] = new Element(file);
              } else if (file.isFile()) {
                  String name = file.getName();
    
                  DexFile dex = null;
                  if (name.endsWith(DEX_SUFFIX)) {
                      // Raw dex file (not inside a zip/jar).
                      try {
                          dex = loadDexFile(file, optimizedDirectory, loader, elements);
                          if (dex != null) {
                              elements[elementsPos++] = new Element(dex, null);
                          }
                      } catch (IOException suppressed) {
                          System.logE("Unable to load dex file: " + file, suppressed);
                          suppressedExceptions.add(suppressed);
                      }
                  } else {
                      try {
                          dex = loadDexFile(file, optimizedDirectory, loader, elements);
                      } catch (IOException suppressed) {
                          /*
                           * IOException might get thrown "legitimately" by the DexFile constructor if
                           * the zip file turns out to be resource-only (that is, no classes.dex file
                           * in it).
                           * Let dex == null and hang on to the exception to add to the tea-leaves for
                           * when findClass returns null.
                           */
                          suppressedExceptions.add(suppressed);
                      }
    
                      if (dex == null) {
                          elements[elementsPos++] = new Element(file);
                      } else {
                          elements[elementsPos++] = new Element(dex, file);
                      }
                  }
                  if (dex != null && isTrusted) {
                    dex.setTrusted();
                  }
              } else {
                  System.logW("ClassLoader referenced unknown path: " + file);
              }
          }
          if (elementsPos != elements.length) {
              elements = Arrays.copyOf(elements, elementsPos);
          }
          return elements;
        }
    
    /**
         * Constructs a {@code DexFile} instance, as appropriate depending on whether
         * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
         * the {@code loader} if it is not null.
         */
        private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
                                           Element[] elements)
                throws IOException {
            if (optimizedDirectory == null) {
                return new DexFile(file, loader, elements);
            } else {
                String optimizedPath = optimizedPathFor(file, optimizedDirectory);
                return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
            }
        }
    
        /**
         * Converts a dex/jar file path and an output directory to an
         * output file path for an associated optimized dex file.
         */
        private static String optimizedPathFor(File path,
                File optimizedDirectory) {
            /*
             * Get the filename component of the path, and replace the
             * suffix with ".dex" if that's not already the suffix.
             *
             * We don't want to use ".odex", because the build system uses
             * that for files that are paired with resource-only jar
             * files. If the VM can assume that there's no classes.dex in
             * the matching jar, it doesn't need to open the jar to check
             * for updated dependencies, providing a slight performance
             * boost at startup. The use of ".dex" here matches the use on
             * files in /data/dalvik-cache.
             */
            String fileName = path.getName();
            if (!fileName.endsWith(DEX_SUFFIX)) {
                int lastDot = fileName.lastIndexOf(".");
                if (lastDot < 0) {
                    fileName += DEX_SUFFIX;
                } else {
                    StringBuilder sb = new StringBuilder(lastDot + 4);
                    sb.append(fileName, 0, lastDot);
                    sb.append(DEX_SUFFIX);
                    fileName = sb.toString();
                }
            }
    
            File result = new File(optimizedDirectory, fileName);
            return result.getPath();
        }
    

    根据传入的目录,递归解析出DexFile,把dex文件加载到内存中解析成Elements,核心代码在DexFile.java中

    移步DexFile.java

    /**
         * 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.
         * @param loader
         *  The class loader creating the DEX file object.
         * @param elements
         *  The temporary dex path list elements from DexPathList.makeElements
         */
        private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
                DexPathList.Element[] elements) throws IOException {
            if (outputName != null) {
                try {
                    String parent = new File(outputName).getParent();
                    if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
                        throw new IllegalArgumentException("Optimized data directory " + parent
                                + " is not owned by the current user. Shared storage cannot protect"
                                + " your application from code injection attacks.");
                    }
                } catch (ErrnoException ignored) {
                    // assume we'll fail with a more contextual error later
                }
            }
    
            mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
            mInternalCookie = mCookie;
            mFileName = sourceName;
            //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
        }
        /*
         * Open a DEX file.  The value returned is a magic VM cookie.  On
         * failure, an IOException is thrown.
         */
        private static Object openDexFile(String sourceName, String outputName, int flags,
                ClassLoader loader, DexPathList.Element[] elements) throws IOException {
            // Use absolute paths to enable the use of relative paths when testing on host.
            return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                     (outputName == null)
                                         ? null
                                         : new File(outputName).getAbsolutePath(),
                                     flags,
                                     loader,
                                     elements);
        }
    
    private static native Object openDexFileNative(String sourceName, String outputName, int flags,
                ClassLoader loader, DexPathList.Element[] elements);
    

    openDexFileNative是jni调用,那我们直接找dalvik_system_DexFile.cc

    // TODO(calin): clean up the unused parameters (here and in libcore).
    static jobject DexFile_openDexFileNative(JNIEnv* env,
                                             jclass,
                                             jstring javaSourceName,
                                             jstring javaOutputName ATTRIBUTE_UNUSED,
                                             jint flags ATTRIBUTE_UNUSED,
                                             jobject class_loader,
                                             jobjectArray dex_elements) {
      ScopedUtfChars sourceName(env, javaSourceName);
      if (sourceName.c_str() == nullptr) {
        return 0;
      }
    
      Runtime* const runtime = Runtime::Current();
      ClassLinker* linker = runtime->GetClassLinker();
      std::vector<std::unique_ptr<const DexFile>> dex_files;
      std::vector<std::string> error_msgs;
      const OatFile* oat_file = nullptr;
    
      dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
                                                                   class_loader,
                                                                   dex_elements,
                                                                   /*out*/ &oat_file,
                                                                   /*out*/ &error_msgs);
    
      if (!dex_files.empty()) {
        jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
        if (array == nullptr) {
          ScopedObjectAccess soa(env);
          for (auto& dex_file : dex_files) {
            if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
              dex_file.release();
            }
          }
        }
        return array;
      } else {
        ScopedObjectAccess soa(env);
        CHECK(!error_msgs.empty());
        // The most important message is at the end. So set up nesting by going forward, which will
        // wrap the existing exception as a cause for the following one.
        auto it = error_msgs.begin();
        auto itEnd = error_msgs.end();
        for ( ; it != itEnd; ++it) {
          ThrowWrappedIOException("%s", it->c_str());
        }
    
        return nullptr;
      }
    }
    

    这里核心方法,在 dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat()
    也就是说,dex文件其实是在OatFile里面查找

    通过翻阅应用安装流程


    image.png

    art虚拟机下,应用在安装过程,通过dex2oat,转化成一个ELF格式
    文件存放起来。BaseDexClassLoader加载dex文件探索到这里。

    下面我们分析BaseDexClassLoader的loadClass

    由于BaseDexClassLoader没有重载loadClass方法,其父类java.lang.Class中的classLoader,loadClass源码:

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
                // First, check if the class has already been loaded
                // 1.根据类名,找到类对象
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        //2.用到的方法为 双亲委托法
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        c = findClass(name);
                    }
                }
                return c;
        }
    

    这里非常有意思,如果class不能直接findLoadedClass,居然是优先从parent中寻找??

    其实这是一个jvm的安全机制,名叫双亲委托机制
    举个简单的例子:


    image.png

    假设刚好你的代码里面,有个java.lang.String这个类,那么系统是优先加载你的String还是jdk的String ?

    答案也很明显,那肯定不能让开发者随意用自己的类,替换系统的!!

    所以classLoader会优先委托parent递归找class。实在找不到才用自己的findClass逻辑。

    ClassLoader作为最顶层基类,没有直接实现findClass的规则,留给继承者实现

     /**
         * Returns the class with the given <a href="#name">binary name</a> if this
         * loader has been recorded by the Java virtual machine as an initiating
         * loader of a class with that <a href="#name">binary name</a>.  Otherwise
         * <tt>null</tt> is returned.
         */
     protected final Class<?> findLoadedClass(String name) {
            ClassLoader loader;
            if (this == BootClassLoader.getInstance())
                loader = null;
            else
                loader = this;
            return VMClassLoader.findLoadedClass(loader, name);
        }
    
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
            Class c = pathList.findClass(name, suppressedExceptions);
            if (c == null) {
                ClassNotFoundException cnfe = new ClassNotFoundException(
                        "Didn't find class \"" + name + "\" on path: " + pathList);
                for (Throwable t : suppressedExceptions) {
                    cnfe.addSuppressed(t);
                }
                throw cnfe;
            }
            return c;
        }
    

    也就是说,通过双亲委托模式,递归查找parent的class无效,BaseDexClassLoader把查找class的任务,从pathList里面的dexElements数组里面查找

    
        /**
         * Finds the named class in one of the dex files pointed at by
         * this instance. This will find the one in the earliest listed
         * path element. If the class is found but has not yet been
         * defined, then this method will define it in the defining
         * context that this instance was constructed with.
         *
         * @param name of class to find
         * @param suppressed exceptions encountered whilst finding the class
         * @return the named class or {@code null} if the class is not
         * found in any of the dex files
         */
        public Class<?> findClass(String name, List<Throwable> suppressed) {
            for (Element element : dexElements) {
                Class<?> clazz = element.findClass(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
    
            if (dexElementsSuppressedExceptions != null) {
                suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
            }
            return null;
        }
    

    Element

    public Class<?> findClass(String name, ClassLoader definingContext,
                    List<Throwable> suppressed) {
                return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                        : null;
            }
    

    DexFile

    
    /**
         * See {@link #loadClass(String, ClassLoader)}.
         *
         * This takes a "binary" class name to better match ClassLoader semantics.
         *
         * @hide
         */
        public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
            return defineClass(name, loader, mCookie, this, suppressed);
        }
    
        private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                         DexFile dexFile, List<Throwable> suppressed) {
            Class result = null;
            try {
                result = defineClassNative(name, loader, cookie, dexFile);
            } catch (NoClassDefFoundError e) {
                if (suppressed != null) {
                    suppressed.add(e);
                }
            } catch (ClassNotFoundException e) {
                if (suppressed != null) {
                    suppressed.add(e);
                }
            }
            return result;
        }
    
        private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                                      DexFile dexFile)
    

    dalvik_system_DexFile.cc

    static jclass DexFile_defineClassNative(JNIEnv* env,
                                            jclass,
                                            jstring javaName,
                                            jobject javaLoader,
                                            jobject cookie,
                                            jobject dexFile) {
      std::vector<const DexFile*> dex_files;
      const OatFile* oat_file;
      if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
        VLOG(class_linker) << "Failed to find dex_file";
        DCHECK(env->ExceptionCheck());
        return nullptr;
      }
    
      ScopedUtfChars class_name(env, javaName);
      if (class_name.c_str() == nullptr) {
        VLOG(class_linker) << "Failed to find class_name";
        return nullptr;
      }
      const std::string descriptor(DotToDescriptor(class_name.c_str()));
      const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
      for (auto& dex_file : dex_files) {
        const DexFile::ClassDef* dex_class_def =
            OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
        if (dex_class_def != nullptr) {
          ScopedObjectAccess soa(env);
          ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
          StackHandleScope<1> hs(soa.Self());
          Handle<mirror::ClassLoader> class_loader(
              hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
          ObjPtr<mirror::DexCache> dex_cache =
              class_linker->RegisterDexFile(*dex_file, class_loader.Get());
          if (dex_cache == nullptr) {
            // OOME or InternalError (dexFile already registered with a different class loader).
            soa.Self()->AssertPendingException();
            return nullptr;
          }
          ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
                                                                   descriptor.c_str(),
                                                                   hash,
                                                                   class_loader,
                                                                   *dex_file,
                                                                   *dex_class_def);
          // Add the used dex file. This only required for the DexFile.loadClass API since normal
          // class loaders already keep their dex files live.
          class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
                                                     class_loader.Get());
          if (result != nullptr) {
            VLOG(class_linker) << "DexFile_defineClassNative returning " << result
                               << " for " << class_name.c_str();
            return soa.AddLocalReference<jclass>(result);
          }
        }
      }
      VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
      return nullptr;
    }
    

    遍历dex_files数组里面DexFile, 根据类名获取description以及其hash值,通过OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash) 转换成ClassDef,最后再class_linker中得到结果。

    最后附上学习成功:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
    
                    checkAndCopyFile("app-release.apk");
                    loadApk("com.amberllo.livedemo.business.activity.SplashActivity", "app-release.apk");
                }
            });
    
    
        }
    
        public void checkAndCopyFile(String apkName) {
            try {
                File pluginFile = getExternalFilesDir("plugin");
    
                File apkFile = new File(pluginFile + "/" + apkName);
    
    
                if (apkFile.exists() && apkFile.length()!=0) {
                    return;
                }
                apkFile.delete();
                if (pluginFile != null && !pluginFile.exists() || pluginFile.listFiles().length == 0) {
                    pluginFile.mkdirs();
                }
                InputStream is = null;
                FileOutputStream fos = null;
                is = getAssets().open(apkName);
                fos = new FileOutputStream(apkFile);
                byte[] buffer = new byte[1024];
                int byteCount;
                while ((byteCount = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, byteCount);
                }
                fos.flush();
                is.close();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    
            }
    
    
        }
    
        public void loadApk(String clazzName, String apkName) {
    
            // Step1. 获取到插件apk,通常都是从网络上下载,这里为了演示,直接将插件apk push到手机
            File pluginFile = getExternalFilesDir("plugin");
            if (pluginFile != null && !pluginFile.exists() || pluginFile.listFiles().length == 0) {
                Toast.makeText(this, "插件文件不存在", Toast.LENGTH_SHORT).show();
                return;
            }
            pluginFile =new File(pluginFile + "/" + apkName);
            // Step2. 创建插件的DexClassLoader
    
            PathClassLoader dexClassLoader = new PathClassLoader(pluginFile.getAbsolutePath(),  getClassLoader());
            try {
                Class clazz = dexClassLoader.loadClass(clazzName);
                Toast.makeText(this, clazz.getSimpleName(), Toast.LENGTH_SHORT).show();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:ClassLoader源码学习 -- PathClassLoad

          本文链接:https://www.haomeiwen.com/subject/tdkhlhtx.html