ART – 双亲委派机制

motivation

了解热修复实现机制,能够hook 热修复中的代码。

热修复:

不通过重新下载安装包,安装apk ,来更新APK的目的。

ClassLoader

用于装载Class的装载器,这个类加载器能达到什么效果?

  1. 用户无需关心类加载的流程。
  2. 实现用时加载。

一些基础的ClassLoader:

BootClassLoader 加载系统基础库,比如 java.lang.String 。

PathClassLoader 用于加载Apk中的dex文件,整个apk的class都可以找到。

DexClassLoader 常用于插件化设计,插件的ClassLoader集成该类。

加载逻辑

  1. 如果一个类加载器加载了一个类,那么这个类加载器将这个类缓存起来。每次加载类的时候,首先会先访问指定类加载器的缓存,若缓存中存在,就直接从缓冲区里返回即可,这可以提高类加载的性能。(未从代码发现该逻辑)
  2. 如果缓冲区找不到,说明是第一次加载,那么如果指定类加载器有父加载器,则优先让父加载器加载该类。所以类加载器具有层级关系。值得注意的是,负责加载 Java 基本类和扩展类的类加载器必须处于类加载器层级中的顶层。父加载器加载类的过程也遵循同样的规则。
  3. 如果没有父加载器或者父加载器也加载不到,则返回当前类加载器,由自己读入字节码并委托虚拟机执行后续加载过程且返回生成的类。

相关代码实现

public abstract class ClassLoader {

...
...
...

protected Class<?> loadClass(String name, boolean resolve)
       throws ClassNotFoundException
  {
           // First, check if the class has already been loaded
           Class<?> c = findLoadedClass(name);
           if (c == null) {
               try {
                   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;
  }
  ...
...
...
}

插件化实例

参考项目:https://github.com/Catherine22/ClassLoader

MyDexClassLoader的声明,关注类的继承关系. MyDexClassLoader <- DexClassLoader <- BaseDexClassLoader <- ClassLoader

package com.catherine.classloader;

import android.content.Context;
import android.util.Log;

import java.net.URL;
import java.util.Enumeration;

import dalvik.system.DexClassLoader;

/**
* Created by Catherine on 2017/2/13.
* yacatherine19@gmail.com
*/

/**
* DexClassLoader:
* A class loader that loads classes from .jar and .apk files containing a classes.dex entry.
* This can be used to execute code not installed as part of an application.
*/
public class MyDexClassLoader extends DexClassLoader {
   private static final String TAG = "MyDexClassLoader";

   /**
    * @param application MyApplication
    * @param dexPath     String: the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android
    * @param soPath     native libraries (path of .so files)
    * @param classloader the parent class loader
    */
   public MyDexClassLoader(MyApplication application, String dexPath, String soPath, ClassLoader classloader) {
       super(dexPath, application.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath(), soPath.replace("files", "lib"),
               classloader);

       Log.i(TAG, "DexClassLoader " + classloader.toString());
       Log.i(TAG, "dexPath " + dexPath);
       Log.i(TAG, "optimizedDirectory " + application.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath());
       Log.i(TAG, "soPath_lib " + soPath);
       Log.i(TAG, "librarySearchPath " + soPath.replace("files", "lib"));

  }

   @Override
   protected Class<?> findClass(String name) throws ClassNotFoundException {
       Log.i(TAG, "findClass:" + name);
       return super.findClass(name);
  }

   @Override
   public String findLibrary(String name) {
       Log.i(TAG, "findLibrary:" + name);
       return super.findLibrary(name);
  }

   @Override
   protected URL findResource(String name) {
       Log.i(TAG, "findResource:" + name);
       return super.findResource(name);
  }

   @Override
   protected Enumeration<URL> findResources(String name) {
       Log.i(TAG, "findResources:" + name);
       return super.findResources(name);
  }

   @Override
   protected synchronized Package getPackage(String name) {
       Log.i(TAG, "getPackage:" + name);
       return super.getPackage(name);
  }

   @Override
   public Class<?> loadClass(String name) throws ClassNotFoundException{
       Log.d(TAG, "loadClass:" + name);
       return super.loadClass(name);
  }

   @Override
   protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
       Log.d(TAG, "loadClass:" + name + " : " + resolve);
       return super.loadClass(name, resolve);
  }
}

看看DexClassLoader

public class DexClassLoader extends BaseDexClassLoader {
  /**
    * Creates a {@code DexClassLoader} that finds interpreted and native
    * code. Interpreted classes are found in a set of DEX files contained
    * in Jar or APK files.
    *
    * <p>The path lists are separated using the character specified by the
    * {@code path.separator} system property, which defaults to {@code :}.
    *
    * @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
    * @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 DexClassLoader(String dexPath, String optimizedDirectory,
          String librarySearchPath, ClassLoader parent) {
      super(dexPath, null, librarySearchPath, parent);
  }
}

MyDexClassLoader加载APK:

Object layerP = new MyDexClassLoader(this, dexPath, cc.getFilesDir().getAbsolutePath(),
      (ClassLoader) sm.get());

使用loadClass加载MyDexClassLoader的class ,

getClassLoader().loadClass(MyConfig.APK2_ACTIVITY_MAIN);

真实调用到ClassLoader的loadClass

一个问题

在使用MyDexClassLoader 前:

使用MyDexClassLoader后:

莫非这个getClassLoader返回的ClassLoader 会变化吗?

getClassLoader 官网介绍返回类的加载器,类没变,类加载器没变,但是类加载的继承关系变了。。。有点意思 。

https://developer.android.com/reference/java/lang/Class#getClassLoader()

巧的是,刚好出现了两个继承关系。

这里用getClassLoader.loadClass(“com.catherine.resource1.MainActivity”)或者

getClassLoader.getParent().loadClass(“com.catherine.resource1.MainActivity”)都成功load到了目标类。

且都调用到MyDexClassLoader的loadClasss方法, 符合双亲委派模型,

另一个问题

class 加载流程呢?

明确继承关系 MyDexClassLoader.getParent == BootClassLoader

在demo中MyDexClassLoader中调用getParent方法直接返回了BootClassLoader,

因为用继承关系时给的参数就是BootClassLoader。

https://blog.csdn.net/mr_liabill/article/details/50497055

参考链接得知, 双亲委派关系在加载类时(loadClass) , 先检测是否已经加载过了,没有加载过, 再交给父类加载? ,但是这里是bootClassLoader能是由它来加载吗,它加载的话,不就所有的ClassLoader都可以用了吗?怀疑可能还是有路径的限制。

https://github.com/huanzhiyazi/articles/issues/30

这还有一篇比较好的文章讲这个双亲委托机制,但是也没有回答android 双亲委派的加载流程。

可能得再看下。

再来一个问题

用默认ClassLoader加载java.lang.String 按照我们的理解,

但是实际上我们用loadClass,

直接就进行了返回,甚至没有走到MyDexClassLoader,????

只能说算是初步了解双亲委派模型, 后续熟练android底层调试后,再开展更细节的研究。

怀疑缓存

ART – Dex加载流程

ART – 字节码加载流程

分类: 安卓

pareto

未来什么方向不管,先做自己喜欢做的事情。

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注