Git传送门 Android开发中不可避免的会用到很多第三方库, 如果碰到异常 常常有种心有余而力不足的想法,当日,如果是开源的 你可以吧源码导入进来进行修改, 但不可避免的会造成维
Git传送门
Android开发中不可避免的会用到很多第三方库, 如果碰到异常 常常有种心有余而力不足的想法,当日,如果是开源的 你可以吧源码导入进来进行修改,
但不可避免的会造成维护性差, 本文提供一个捕获第三方库异常的思路,也可以扩展为在编译时修改第三方代码,从而插入自己的需求
1.Javassist 简介
javassist是一个修改java字节码的开源库
下面是个很简单的例子,获取一个classPool,设置运行所需要的库,写入到对应位置
具体语法可以参考 Javassist介绍
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath("/usr/local/javalib");
CtClass cc = pool.get("test.Rectangle"); cc.setSuperclass(pool.get("test.Point")); cc.writeFile();
2.在安卓中的应用
gradle 编译安卓项目是通过一个个task来执行任务, 我们可以通过gradle看到很多transform*的task
在编译时插入一个自己的transform 从而实现对源码或第三方jar库拦截 实现修改第三方库
下面时定义一个gradle插件, 注册一个自己的transform
public class MainPlugin implements Plugin<Project>{ void apply(Project project) { project.logger.error("Dhjar start=========================") project.extensions.create("dhjar", LJarConfig) project.android.registerTransform(new JavassistTransform(project)) } }
transform的几个方法
//获取输入类型jar或者class
@Override public Set<QualifiedContent.ContentType> getInputTypes() { return TransformManager.CONTENT_CLASS; } //需要处理的范围, 主项目 子项目 或者三方库 @Override public Set<? super QualifiedContent.Scope> getScopes() { Set<QualifiedContent.Scope> sets = new HashSet<QualifiedContent.Scope>() sets.add(QualifiedContent.Scope.EXTERNAL_LIBRARIES) return sets; } @Override Set<? super QualifiedContent.Scope> getReferencedScopes() { Set<QualifiedContent.Scope> sets = new HashSet<QualifiedContent.Scope>() sets.add(QualifiedContent.Scope.EXTERNAL_LIBRARIES) sets.add(QualifiedContent.Scope.PROVIDED_ONLY) return sets }
他的核心方法就是 分别为获取jar 和source目录
@Override public void transform(TransformInvocation transformInvocation) throws IOException { }
下面就是捕获第三方异常的核心代码 通过插入一个相同的方法包裹上try catch 从而拦截需要捕获的方法, 具体代码可以看开头的链接
private static void modify(CtClass c, ClassPool mClassPool,List<String> methods) { if (c.isFrozen()) { c.defrost() } System.out.println("find class==============="+c.getName()) for(String method : methods){ CtMethod ctMethod = c.getDeclaredMethod(method) String method2 = method+"DhCut" CtMethod ctMethod2 = CtNewMethod.copy(ctMethod,method2,c,null) c.addMethod(ctMethod2) int methodLen = ctMethod.getParameterTypes().length StringBuffer sb = new StringBuffer() sb.append("{try{") if(!ctMethod.getReturnType().getName().contains("void")){ sb.append("return ") } sb.append(method2) sb.append("(") for(int i = 0; i<methodLen;i++){ sb.append("\$"+(i+1)) if(i!=methodLen-1){ sb.append(",") } } sb.append(");}catch(Exception ex){ System.out.println(ex.toString());ex.printStackTrace();}") if(!ctMethod.getReturnType().getName().contains("void")){ sb.append("return ") String result = getReturnValue(ctMethod.getReturnType().getName()) sb.append(result) sb.append(";") } sb.append("}") System.out.println("return type =======" +ctMethod.getReturnType().getName()) ctMethod.setBody(sb.toString()) } }
拦截前得类 此时我们直接调用getString 或造成空指针崩溃
package com.vova.testlibrary; public class TestFile { public int getInt() { return 1; } public float getFloat() { return 0.0F; } public double getDoulbe() { return 0.0D; } public long getLong() { return 0L; } public char getChar() { return ‘a‘; } public short getShort() { return 0; } public double getDouble() { return 0.0D; } public String getString() { String aa = null; int len = aa.length(); return null; } public byte getByte() { return 0; } }View Code
gradle编译效果图 输入test.jar 输出19.jar 打印需要替换的方法
下面是19.jar源码
package com.vova.testlibrary; import java.io.PrintStream; public class TestFile { public int getIntDhCut() { return 1; } public float getFloatDhCut() { return 0.0F; } public double getDoulbe() { return 0.0D; } public long getLongDhCut() { return 0L; } public char getCharDhCut() { return ‘a‘; } public short getShortDhCut() { return 0; } public double getDoubleDhCut() { return 0.0D; } public String getStringDhCut() { String aa = null; int len = aa.length(); return null; } public byte getByteDhCut() { return 0; } public int getInt() { try { return getIntDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0; } public float getFloat() { try { return getFloatDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0.0F; } public long getLong() { try { return getLongDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0L; } public char getChar() { try { return getCharDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return ‘a‘; } public short getShort() { try { return getShortDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0; } public double getDouble() { try { return getDoubleDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0.0D; } public String getString() { try { return getStringDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return null; } public byte getByte() { try { return getByteDhCut(); } catch (Exception localException) { System.out.println(localException.toString()); localException.printStackTrace(); } return 0; } }View Code