1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
| /** * author:wulinpeng * date:2020-01-25 22:48 * desc: */ public abstract class BaseTransform extends Transform {
@Override public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation); }
public void triggerScan(TransformInvocation transformInvocation, BytecodeAdapter adapter) throws TransformException, InterruptedException, IOException { //消费型输入,可以从中获取jar包和class文件夹路径。需要输出给下一个任务 Collection<TransformInput> inputs = transformInvocation.getInputs(); //引用型输入,无需输出。 Collection<TransformInput> referencedInputs = transformInvocation.getReferencedInputs(); //OutputProvider管理输出路径,如果消费型输入为空,你会发现OutputProvider == null TransformOutputProvider outputProvider = transformInvocation.getOutputProvider(); for (TransformInput input : inputs) { for (JarInput jarInput : input.getJarInputs()) { File dest = outputProvider.getContentLocation( jarInput.getFile().getAbsolutePath(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR); dealJar(transformInvocation.getContext(), jarInput.getFile(), adapter); FileUtils.copyFile(jarInput.getFile(), dest); } for (DirectoryInput directoryInput : input.getDirectoryInputs()) { File dest = outputProvider.getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY); //将修改过的字节码copy到dest,就可以实现编译期间干预字节码的目的了 FileUtils.getAllFiles(directoryInput.getFile()).filter(new Predicate<File>() {
@Override public boolean apply(@Nullable File input) { return input.getName().endsWith(SdkConstants.DOT_CLASS); } }).forEach(new Consumer<File>() { @Override public void accept(File file) { try { // 修改字节码 byte[] classCode = adapter.acceptBytecode(IOUtils.toByteArray(new FileInputStream(file))); FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(classCode); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }); // 将file copy到目标目录 try { FileUtils.copyDirectory(directoryInput.getFile(), dest); } catch (Throwable t) { t.printStackTrace(); } } } }
/** * 做一次input 到 dest的copy * @param transformInvocation * @throws TransformException * @throws InterruptedException * @throws IOException */ public void flulsh(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { Collection<TransformInput> inputs = transformInvocation.getInputs(); TransformOutputProvider outputProvider = transformInvocation.getOutputProvider(); for (TransformInput input : inputs) { for (JarInput jarInput : input.getJarInputs()) { File dest = outputProvider.getContentLocation( jarInput.getFile().getAbsolutePath(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR); FileUtils.copyFile(jarInput.getFile(), dest); } for (DirectoryInput directoryInput : input.getDirectoryInputs()) { File dest = outputProvider.getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY); // 将file copy到目标目录 FileUtils.copyDirectory(directoryInput.getFile(), dest); } } }
public static void dealJar(Context context, File jarfile, BytecodeAdapter adapter) { File modifyFile = modifyJar(jarfile, context.getTemporaryDir(), true, adapter); if (jarfile.exists()) { jarfile.delete(); } try { FileUtils.copyFile(modifyFile, jarfile); } catch (IOException e) { e.printStackTrace(); } }
static File modifyJar(File jarFile, File tempDir, boolean nameHex, BytecodeAdapter adapter) { try { JarFile file = new JarFile(jarFile); String hexName = ""; if (nameHex) { hexName = DigestUtils.md5Hex(jarFile.getAbsolutePath()).substring(0, 8); } File outputJar = new File(tempDir, hexName + jarFile.getName()); JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(outputJar)); Enumeration enumeration = file.entries(); while (enumeration.hasMoreElements()) { JarEntry jarEntry = (JarEntry) enumeration.nextElement(); InputStream inputStream = file.getInputStream(jarEntry); String entryName = jarEntry.getName(); ZipEntry zipEntry = new ZipEntry(entryName); jarOutputStream.putNextEntry(zipEntry); byte[] modifiedClassBytes = null; byte[] sourceClassBytes = IOUtils.toByteArray(inputStream); if (shouldModifyClass(entryName)) { modifiedClassBytes = adapter.acceptBytecode(sourceClassBytes); jarOutputStream.write(modifiedClassBytes); } else { jarOutputStream.write(sourceClassBytes); } jarOutputStream.closeEntry(); } jarOutputStream.close(); file.close(); return outputJar; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
private static boolean shouldModifyClass(String entryName) { if (!entryName.endsWith(".class")) { return false; } if (entryName.startsWith("android") || entryName.startsWith("java") || entryName.startsWith("kotlin")) { return false; } else { return true; } }
interface BytecodeAdapter { byte[] acceptBytecode(byte[] bytecode); } }
|