加壳与类加载器

全面了解类加载器的分类、作用以及源码分析,了解双亲委派机制,动态加载第三方dex的函数。

类加载器的分类及作用

1、类加载器的种类

JVM的类加载器包括3种:

  • Bootstrap ClassLoader(引导类加载器)

    C/C++代码实现的加载器,用于加载指定的JDK的核心类库,比如java.lang、java.uti等这些系统类。

    Java虚拟机的启动就是通过Bootstrap,该Classloader在java里无法获取,用于加载lib下的类。

  • Extensions ClassLoader(拓展类加载器)

    Java中的实现类为ExtClassLoader,提供了除了系统类之外的额外功能,可以在Java里获取,负责加载/lib/ext下的类。

  • Application ClassLoader(应用程序类加载器)

    Java中的实现类为AppClassLoader,是与我们接触最多的类加载器,开发人员写的默认代码就是由它来加载,ClassLoader.getSystemClassLoader返回的就是它。

也可以自定义类加载器,只需要通过集成java.lang.ClassLoader类的方式

2、加载顺序

BootstrapClassLoader => ExtensionsClassLoader => ApplicationClassLoader

3、双亲委派

简而言之,就是一个类加载器收到了加载请求,会优先给父加载器进行加载,层层向上,如果直到最后都没能找到,那么子加载器会自己进行加载。

通俗地来说,就是儿子不愿意干活,而让父亲做,父亲也遵守次规则,直到找到加载过的才会停止,否则就层层下发,让子加载器进行加载。

这样做有什么好处?

  • 避免重复加载
  • 更加安全,无法自定义类来替代系统类,防止核心API库被随意篡改。

4、类加载的时机

  • 隐士加载
  • 显示加载

5、类加载的过程

  • 装载:查找和导入Class文件
  • 链接:其中解析步骤是可以选择的
    • 检查:检查载入的class文件数据的正确性
    • 准备:给类的警惕变量分配存储空间
    • 解析:将符号引用转成直接引用
  • 初始化:即调用< clinit>函数,对静态变量,静态代码块执行初始化工作

6、类加载器的继承关系以及作用

在这里插入图片描述

  • ClassLoader为抽象类;
  • BootClassLoader预加载常用类,单例模式。与Java的不同,它是由Java实现的。
  • BaseDexClassLoader是PathClassLoader、DexClassLoader、InmemoryDexClassLoader的父类,类加载的主要逻辑都是BaseDexClassLoader实现的。
  • SecureClassLoader继承了抽象类ClassLoader,拓展了ClassLoader类加入了权限方面的功能,加强了安全性,其子类URLClassLoader是用URL路径从jar文件中加载类

其中重点关注PathClassLoader和DexClassLoader。

PathClassLoader是Android默认使用的类加载器,一个APK的Activity等类更是在其加载。

DexClassLoader可以加载任意目录下的dex/jar/apk/zip文件,比PathClassLoader更加灵活,是实现热更新、热修复、dex加壳的重点。

在线Android源码网站:

国内:aospxref.com

国外:androidxref.com

验证类加载器顺序以及DexClassLoader加载类使用

验证类加载器顺序

源码里的parent字段很重要,这里使用一个实例来测试。

使用AndroidStudio新建一个test02项目,使用empty Activity。

package com.example.test02;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testClassLoader();
    }
    /*
    I/miniboom: thisClassloader:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.test02-ftL2Lx2X30e9IJk52y3q4w==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.test02-ftL2Lx2X30e9IJk52y3q4w==/lib/arm64, /system/lib64, /vendor/lib64]]]
    this:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.test02-ftL2Lx2X30e9IJk52y3q4w==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.test02-ftL2Lx2X30e9IJk52y3q4w==/lib/arm64, /system/lib64, /vendor/lib64]]]***java.lang.BootClassLoader@b266b1f
    this:java.lang.BootClassLoader@b266b1f***java.lang.BootClassLoader@b266b1f
    root:java.lang.BootClassLoader@b266b1f
    * */
    public void testClassLoader(){
        ClassLoader thisClassloader = MainActivity.class.getClassLoader();
        Log.i("miniboom", "thisClassloader:"+thisClassloader);
        ClassLoader tmpClassloader = null;
        //parent双亲委派中非常重要的一个变量
        ClassLoader parentClassloader = thisClassloader.getParent();
        while (parentClassloader != null){
            Log.i("miniboom","this:"+thisClassloader+"***"+parentClassloader);
            tmpClassloader = thisClassloader.getParent();
            thisClassloader = parentClassloader;
            parentClassloader = tmpClassloader;
        }
        Log.i("miniboom", "root:"+thisClassloader);
    }
}

填入以下代码,运行,结果我写在注释里了,可以很清楚验证类加载的关系,root加载器是

java.lang.BootClassLoader

使用DexClassLoader类加载器加载第三方DEX函数

首先新建一个empty Activity项目,新建一个类。build => 对生成的APK进行提取

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7iTPE8L-1606293451948)(/Users/liyang/Library/Application Support/typora-user-images/image-20201125162719370.png)]

增加读取sd卡的读写权限。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-laneJgON-1606293451953)(/Users/liyang/Library/Application Support/typora-user-images/image-20201125162949326.png)]

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4ntry9l-1606293451957)(/Users/liyang/Library/Application Support/typora-user-images/image-20201125162754184.png)]

用windows或者ubuntu,ubuntu直接将apk提取到此处,windows把.apk改成.zip然后解压即可。

push到真机的/sdcard/3.dex

adb push ./classes.dex /sdcard/3.dex 

搞定之后,运行APP,要在权限管理,对这个APP进行授权读写权限,才可以。

在新建一个invoketest项目,用于读取该3.dex里的TestClass类里的函数。

package com.example.invoketest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Context appContext = this.getApplicationContext();
        testDexClassloader(appContext, "/sdcard/3.dex");

    }
    //com.example.test02.TestClass.testFunc
    public void testDexClassloader(Context context, String dexfilepath){
        //存放优化的dex文件
        File optfile = context.getDir("opt_dex",0);
        //存放so文件
        File libfile = context.getDir("lib_path",0);

        DexClassLoader dexClassLoader = new DexClassLoader(dexfilepath, optfile.getAbsolutePath(),libfile.getAbsolutePath(),
                MainActivity.class.getClassLoader());

        Class<?> clazz = null;

            try {
                //alt + enter解决异常代码问题
                clazz = dexClassLoader.loadClass("com.example.test02.TestClass");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (clazz != null){
                try{
                    Method testFuncMethod = clazz.getDeclaredMethod("testFunc");
                    //因为不是静态方法,先创建实例。
                    Object obj = null;
                    try {
                        obj = clazz.newInstance();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }
                    try {
                        testFuncMethod.invoke(obj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }

        }
    }

运行即可看到testFunc打印的一段话。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值