zl程序教程

您现在的位置是:首页 >  Java

当前栏目

利用ClassLoader实现检查项目中不符合规范的代码

2023-02-18 16:34:03 时间

转载请以链接形式标明出处: 本文出自:103style的博客

主要实现思路主要是:

  • 通过 context.getClassLoader()或者 Thread.currentThread().getContextClassLoader()获取ClassLoader对象。
  • 通过反射获取私有变量pathList.
  • 通过反射获取pathList的私有变量 数组 dexElements
  • 通过反获取每一个 dexElements的私有变量dexFile,获得DexFile的集合
  • 然后通过DexFile.entries()获取dexFile的所有 指定包名 的类文件
  • 然后对文件进行 代码规范的规则校验。

demo github项目地址

以下贴下主要的代码:

MainActivity中的点击校验方法:

private void startCodeCheck() {
    // 获取 BaseDexClassLoader 中 pathList 变量的 dexElements 变量
    Object dexElements = CodeCheckUtils.getDexElements();

    // 获取当前classloader中的DexFile列表
    ArrayList<DexFile> dexFileList = CodeCheckUtils.getDexFileList(dexElements);

    // 获取 DexFile 列表中  含有 packageName 的类
    List<Class> pkgClass = CodeCheckUtils.getPackageClass(dexFileList, getPackageName());

    //获取不符合规则的类
    List<String> illegalList = getIllegalClass(pkgClass);
    if (illegalList == null || illegalList.size() == 0) {
        return;
    }

    //弹框提示修改
    showIllegalClassDialog(illegalList);
}

demo的 类校验规则

private List<String> getIllegalClass(List<Class> pkgClass) {
    if (pkgClass == null) {
        LogUtils.e(TAG, "pkgClass is null");
        return null;
    }
    List<String> illegalList = new ArrayList<>();

    // TODO: 2019/9/18  这里的检查规则 请根据具体的规则修改
    for (Class itemClass : pkgClass) {
        String name = itemClass.getName();
        LogUtils.d(TAG, name);
        String simpleName = itemClass.getSimpleName();
        if (isSystemOrInnerClass(name, simpleName)) {
            continue;
        }
        name = name.replace(getPackageName(), "");
        String[] arr = name.split("\\.");
        //文件直接在包目录下 子目录下包含文件夹  不合法
        if (arr.length != 3) {
            illegalList.add(simpleName);
            continue;
        }

        if (!arr[2].toLowerCase().endsWith(arr[1].toLowerCase())) {
            illegalList.add(simpleName);
        }
    }
    return illegalList;
}

/**
 * 是否是系统生成的类 或者 内部类
 */
private boolean isSystemOrInnerClass(String name, String simpleName) {
    return name.contains("R$") //资源文件
            | "R".equals(simpleName)//R文件
            | "BuildConfig".equals(simpleName)//配置文件
            | name.contains("$");//内部类
}

CodeCheckUtils

public class CodeCheckUtils {

    private static final String TAG = "CodeCheckUtils";

    /**
     * 获取 BaseDexClassLoader 中 pathList 变量的 dexElements 变量
     */
    public static Object getDexElements() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            LogUtils.e(TAG, "classLoader is null");
            return null;
        }
        LogUtils.d(TAG, "classLoader = " + classLoader.getClass().getName());
        if (!(classLoader instanceof BaseDexClassLoader)) {
            LogUtils.e(TAG, "classLoader not instanceof BaseDexClassLoader");
            return null;
        }
        BaseDexClassLoader baseDexClassLoader = (BaseDexClassLoader) classLoader;
        Class superclass = baseDexClassLoader.getClass().getSuperclass();
        if (superclass == null) {
            LogUtils.e(TAG, "baseDexClassLoader's  superclass is null");
            return null;
        }
        LogUtils.d(TAG, "baseDexClassLoader's  superclass = " + superclass.getName());

        Object declaredField = ReflectUtils.getDeclaredField(baseDexClassLoader, superclass, "pathList");
        if (declaredField == null) {
            LogUtils.e(TAG, "get pathList is null");
            return null;
        }
        return ReflectUtils.getDeclaredField(declaredField, "dexElements");
    }

    /**
     * 获取当前classloader中的DexFile列表
     */
    public static ArrayList<DexFile> getDexFileList(Object dexElements) {
        if (dexElements == null) {
            LogUtils.e(TAG, "get dexElements is null");
            return null;
        }
        int length = 0;
        try {
            length = Array.getLength(dexElements);
        } catch (Exception e) {
            LogUtils.e(TAG, "get dexElements length error");
            e.printStackTrace();
        }

        if (length == 0) {
            LogUtils.e(TAG, "dexElements length is 0");
            return null;
        }

        ArrayList<DexFile> dexFileList = new ArrayList<>();
        Field dexFileField = null;
        for (int i = 0; i < length; i++) {
            Object temp = Array.get(dexElements, i);
            if (dexFileField == null) {
                dexFileField = ReflectUtils.getClassField(temp, "dexFile");
                if (dexFileField == null) {
                    continue;
                }
            }
            Object object = ReflectUtils.getClassFieldValue(temp, dexFileField);
            if (object == null) {
                return null;
            }
            if (object instanceof DexFile) {
                dexFileList.add((DexFile) object);
            } else {
                LogUtils.e(TAG, "");
            }
        }
        return dexFileList;
    }

    /**
     * 返回 DexFile 列表中  含有 packageName 的类
     *
     * @param dexFileList DexFile 列表
     * @param packageName 返回包含此包名的类
     */
    public static List<Class> getPackageClass(ArrayList<DexFile> dexFileList, String packageName) {
        if (dexFileList == null || dexFileList.size() == 0) {
            LogUtils.e(TAG, "dexFileList is empty");
            return null;
        }
        List<Class> pckClass = new ArrayList<>();
        for (DexFile dexFile : dexFileList) {
            Enumeration<String> enumeration = dexFile.entries();
            if (enumeration == null) {
                continue;
            }
            while (enumeration.hasMoreElements()) {
                String element = enumeration.nextElement();
                if (element.contains(packageName)) {
                    try {
                        pckClass.add(Class.forName(element));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return pckClass;
    }
}

demo 源码下载

效果图:

以上