Android的classloader

关系图

classloader是所有classloader的父类(继承关系)

bootclssloader是所有classloader的parent(委托关系)

先做一个demo,代码如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("wanbai", "Activity的类加载加载器:" + Activity.class.getClassLoader() + ">>>code == " + Activity.class.getClassLoader().hashCode() );
        Log.v("wanbai", "Context的类加载加载器:" + Context.class.getClassLoader()  + ">>>code == " + Context.class.getClassLoader().hashCode());
        Log.v("wanbai", "ListView的类加载器:" + ListView.class.getClassLoader()  + ">>>code == " + ListView.class.getClassLoader().hashCode());
        Log.v("wanbai", "------------------------------------------------------------------------------------------------------------------\n");
        Log.v("wanbai", "MainActivity的类加载加载器:" + MainActivity.class.getClassLoader() + ">>>code == " + MainActivity.class.getClassLoader().hashCode() );
        Log.v("wanbai", "应用程序默认加载器1:" + getClassLoader()  + ">>>code == " + getClassLoader().hashCode());
        Log.v("wanbai", "应用程序默认加载器2:" + this.getClass().getClassLoader()  + ">>>code == " + this.getClass().getClassLoader().hashCode());
        Log.v("wanbai", "应用程序默认加载器3:" + MainActivity.this.getClass().getClassLoader()  + ">>>code == " + MainActivity.this.getClass().getClassLoader().hashCode());
        Log.v("wanbai", "系统类加载器:" + ClassLoader.getSystemClassLoader()  + ">>>code == " + ClassLoader.getSystemClassLoader().hashCode());
        Log.v("wanbai", "------------------------------------------------------------------------------------------------------------------\n");

        Log.v("wanbai", "打印应用程序默认加载器的委派机制:");
        ClassLoader classLoader = getClassLoader();
        while (classLoader != null) {
            Log.v("wanbai", "类加载器:" + classLoader);
            classLoader = classLoader.getParent();
        }
        Log.v("wanbai", "------------------------------------------------------------------------------------------------------------------\n");
        Log.v("wanbai", "打印系统加载器的委派机制:");
        classLoader = ClassLoader.getSystemClassLoader();
        while (classLoader != null) {
            Log.v("wanbai", "类加载器:" + classLoader);
            classLoader = classLoader.getParent();
        }
    }
}

打印如下:

Activity的类加载加载器:java.lang.BootClassLoader@c692ed4>>>code == 208219860
Context的类加载加载器:java.lang.BootClassLoader@c692ed4>>>code == 208219860
ListView的类加载器:java.lang.BootClassLoader@c692ed4>>>code == 208219860
------------------------------------------------------------------------------------------------------------------
MainActivity的类加载加载器:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/lib/arm64, /system/lib64, /system/vendor/lib64]]]>>>code == 17152125
应用程序默认加载器1:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/lib/arm64, /system/lib64, /system/vendor/lib64]]]>>>code == 17152125
应用程序默认加载器2:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/lib/arm64, /system/lib64, /system/vendor/lib64]]]>>>code == 17152125
应用程序默认加载器3:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/lib/arm64, /system/lib64, /system/vendor/lib64]]]>>>code == 17152125
系统类加载器:dalvik.system.PathClassLoader[DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64]]]>>>code == 190042994
------------------------------------------------------------------------------------------------------------------
打印应用程序默认加载器的委派机制:
类加载器:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.ndklearn-o6jgffmE45caOq26eG0gWg==/lib/arm64, /system/lib64, /system/vendor/lib64]]]
类加载器:java.lang.BootClassLoader@c692ed4
------------------------------------------------------------------------------------------------------------------
打印系统加载器的委派机制:
类加载器:dalvik.system.PathClassLoader[DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64]]]
类加载器:java.lang.BootClassLoader@c692ed4

activity创建过程:

 ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        }

这个demo说明,

1.凡是属于android系统的类,都是由bootclassloader来加载,如:Application,Activity

2.凡是自定义类,都是pathclassloader来加载,如:MyApplication,MyActivity

3.MainActivity.class.getclassloader 等于 this.getclass().getclassloader 等于MainActivity.this.getclass().getclassloader。

   表示的是,加载MainActivity.class这个类的classloader是什么,

   通过activity的实例化研究我们发现,在ActivityThread.performLaunchActivity里。实例化activity用的classloader,为appContext的classloader。

4.demo中getClassloader,实际调用到contextwrraper,再调用到contextImpl的getclassloader.熟悉activity创建过程就明白,上面的appcontext就是activity的contextImpl实例

   所以,就会理解getclassloader和MainActivity.class.getclassloader为什么会一致了

5.Classloader.getsystemclassloader实际也是返回pathclassloader,但是这个classloader用的极少,后面再做分析

ClassLoader的作用

1.classloader作用就是查找加载类。

   classloader加载类采用双亲委托模式,loadclass向上委托,findclass向下查找

   ClassLoader.java

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;
    }

   一般情况下,classloader的子类,不会覆写loadclass方法。只会覆写findclass方法

   比如我们熟悉的BaseDexClassLoader.java  没有覆写loadclass,findclass方法如下

    @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;
    }

   继续查看DexPathList的findclass方法

 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是DexPathList的内部类,继续查看

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

   继续查看同包下的DexFile.java

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)

   最终通过native方法加载出class

 

分析BootClassLoader

Bootclassloader是Classloader的内部类,是android中所有classloader的parent,可见性为包内可见,但是我们无法使用

在Android系统启动时创建,用于加载一些Framework层的类。

bootclassloader创建于zygoteInit的main方法的preload函数开始

1.main方法内

2.preload内

3.preloadClasses内。注意此时loader为null

4.forName方法内

到此,BootClassloader就创建完成了,并且它是一个单例,

BooClassloader的加载范围

分析PathClassLoader

pathclassloader主要用于加载 已经安装到android系统中的apk文件以及系统类(/data/app 目录),是android默认使用的类加载器

在App启动的时候创建,主要用于加载/data/app/xxx.xxx.xxx/sample-1/base.apk里面的类以及系统类 

(实际上也可以加载外部apk,8.0以后更是更DexClassLoader完全一样了)

 

PathclassLoader创建分为两种,一种是systemserver进程的创建,一种是应用程序进程中的创建,

1.先看应用程序进程中pathclassloader创建过程。

  肯定从应用启动流程中找,看图

1.从 handleBindApplication()开始

app = data.info.makeApplication(data.restrictedBackupMode, null);

2.LoadedApk的makeApplication方法

3.LoadedApk的getClassLoader方法

    @UnsupportedAppUsage
    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader == null) {
                createOrUpdateClassLoaderLocked(null /*addedPaths*/);
            }
            return mClassLoader;
        }
    }

4.LoadedApk的 createOrUpdateClassLoaderLocked方法

5.ApplicationLoaders的getClassLoader方法

6.ClassloaderFactory的createClassLoader方法

    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
            int targetSdkVersion, boolean isNamespaceShared, String classloaderName) {

        final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
                classloaderName);

        boolean isForVendor = false;
        for (String path : dexPath.split(":")) {
            if (path.startsWith("/vendor/")) {
                isForVendor = true;
                break;
            }
        }
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
        String errorMessage = createClassloaderNamespace(classLoader,
                                                         targetSdkVersion,
                                                         librarySearchPath,
                                                         libraryPermittedPath,
                                                         isNamespaceShared,
                                                         isForVendor);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        if (errorMessage != null) {
            throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " +
                                           classLoader + ": " + errorMessage);
        }

        return classLoader;
    }

    public static ClassLoader createClassLoader(String dexPath,
            String librarySearchPath, ClassLoader parent, String classloaderName) {
        if (isPathClassLoaderName(classloaderName)) {
            return new PathClassLoader(dexPath, librarySearchPath, parent);
        } else if (isDelegateLastClassLoaderName(classloaderName)) {
            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);
        }

        throw new AssertionError("Invalid classLoaderName: " + classloaderName);
    }

最终是通过ClassloaderFactory的工厂方法构建成的

 

2.再看systemserver进程中pathclassloader创建过程(ams所在进程,非app应用进程)

patchclassloader的创建过程,也是从zygoteinit类开始的。

forksystemserver中

handlesystemserver是在子进程中被调用的,就是刚启动的这个程序

createPathClassLoader中

经过重重的重载方法,最后new出来了一个PathClassLoader.

分析DexClassLoader

它和pathclassloader,都继承自BaseDexClassLoader.

android用于插件化,热修复,主要就是依靠的dexclassloader。

PathClassLoader 和 DexClassLoader 都能加载外部的 dex/apk,只不过区别是 DexClassLoader 可以指定 optimizedDirectory

也就是 dex2oat 的产物 .odex 存放的位置,而 PathClassLoader 只能使用系统默认位置。但是这个 optimizedDirectory 在 Android 8.0 以后也被舍弃了,

只能使用系统默认的位置了。

8.0以前DexClassLoader构造

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
    }

8.0之后,DexClassLoader也变成PathClassLoader一样了

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

热修复的原理是

1.取出旧的Pathclassloader的 pathlist,再取出其中的的dexElementsA[].

2.用一个新的classloader,加载出dex,再同样取出dexElementsB[].

3.把这B放在A之前,就能简单的做热修复了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值