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