Android的classloader_android activity getclassloader_过儿丶的博客-程序员宅基地

技术标签: android 知识点  

关系图

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之前,就能简单的做热修复了

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yang_niuxxx/article/details/115586583

智能推荐

将博客搬至CSDN_牛仔1875_新浪博客-程序员宅基地

因csdn对代码块支持较好,现将博客搬至csdn。个人主页:https://blog.csdn.net/zxylv

Android日期格式化_android 年月日 格式化-程序员宅基地

今天整理一下关于日期格式化的内容,开发中时间总要根据我们的需要去显示不同的格式,我们可以利用java.text.SimpleDateFormat,所谓格式转换,肯定是根据模型转化出我们想要的时间格式,首先我们先看一下模型中各个参数的含义吧,胸API文档中看到这样一个表格SymbolMeaningPresentationExampl_android 年月日 格式化

hdu 2803 The MAX-程序员宅基地

The MAXTime Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2060 Accepted Submission(s): 895Problem DescriptionGiving N integers, V1,

Java自学基础、进阶、项目实战网站推荐-程序员宅基地

模仿天猫官网后端JEE模范天猫官网SSH模范天猫官网SSM模仿天猫官网SpringBoot模仿天猫官网前端一本糊涂账Java项目

【Spring实战学习笔记】第4章 面向切面的Spring_,spring还引入了一个新的bean()指示器-程序员宅基地

目录4.1 什么是面向切面编程4.2 通过切点来选择连接点4.3 使用注解创建切面4.4 在XML中声明切面4.5 注入AspectJ切面4.6 小结在软件开发中,散布于应用中多处的功能被称为横切关注点(cross cutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与..._,spring还引入了一个新的bean()指示器

外卖店优先级(第十届蓝桥杯省赛C++A组 暴力做法)_外卖店优先级c++-程序员宅基地

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;const int N=1e5+10;struct SUM{ int times,num; bool operator <(const SUM &t)const { if(times!=t.times)retur.._外卖店优先级c++

随便推点

ROS Qt5 librviz人机交互界面开发七(发布导航目标点和原点位置)-全网首发_rviz界面怎么设置导航目的地-程序员宅基地

在前面几篇教程中我们已经实现了订阅map话题,这篇我们主要就是实现rviz中的设置目标点和初始点的功能一,实现效果:在地图上选点后librviz就会自动进行变换,发布目标点信息实现如下:可以发现控制台提示已经发布了位置点话题:在下一篇博客继续实现完整导航功能二,核心代码其实核心代码也就两三句,但是目前网上关于librviz的资料少之又少,连官方都没有api说明,只有最简单的一个de......_rviz界面怎么设置导航目的地

jsp和el表达式,以及JSTL标签库-程序员宅基地

1,jsp 1,概念 2,jsp的三种方式 out.write(65);字符 字符串 字符数组1) <% 中间写java代码 out.println("任何类型"); out.write(65); response.getWriter().println(); response.getWrite...

Vue -> 解决 vue-ueditor-wrap 不能显示的问题_vue-ueditor-wrap不显示-程序员宅基地

Vue -> 解决 vue-ueditor-wrap 不能显示的问题_vue-ueditor-wrap不显示

修改selinux导致虚拟机无法开启问题_selinux 改错了 怎么启动虚拟机-程序员宅基地

错误:可以看到虚拟机卡在这里不动了错误原因:误将SELINUX的配置修改到SELINUXTYPE解决方法:重新启动虚拟机,在启动页面按e,进入grub界面然后在红线(UTF-8之后)位置输入selinux=0Ctrl+x 就可以正常进入虚拟机,再把之前修改错误的配置文件改正保存reboot重新启动虚拟机这样就关闭了SELinux..._selinux 改错了 怎么启动虚拟机

我是神:说说我稀里糊涂的第一桶金-程序员宅基地

创业类文章在博客园是细水长流而不绝的,我也来谈谈我赚的第一桶金吧,涉足的行业虽然与咱们的技术不太贴,但怎么着也属于IT范畴——网游——私服~。 准确来说我的故事算不上创业,只能说是迷迷糊糊地被钱砸醒了,这听起来很不错是吧。 我以此文来怀念一下那段时光,并揭示一下自己当时不可告人的恶行,哈哈。 文章比较长、比较琐碎,我尽量说得有趣点,预防瞌睡,打起精神来吧,某些地方自认还是比较精彩的。 背景 ...

Android开发大牛博客_android 开发牛人博客-程序员宅基地

排名不分先后CSDN博文精选:Android系列开发博客资源汇总很多已经和下面的有了重复老罗的Android之旅(CSDN)《Android系统源代码情景分析》一书的作者,研究系统源码。【核心分析】maxleng的专栏又一位Android核心分析,不过已经不在更新了Android中文Wiki提供Android的中文api,还有其他_android 开发牛人博客

推荐文章

热门文章

相关标签