diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/JarPatchInfo.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/JarPatchInfo.groovy new file mode 100644 index 00000000..e15c4862 --- /dev/null +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/JarPatchInfo.groovy @@ -0,0 +1,30 @@ + +package com.qihoo360.replugin.gradle.plugin.inner + +import com.qihoo360.replugin.gradle.plugin.AppConstant + +/** + * @author 247321453 + */ +public class JarPatchInfo { + + def String jarMd5 + + def String pluginVersion + + def String manifestActivitiesMd5 + + /** + * gson需要 + */ + JarPatchInfo(){ + + } + + JarPatchInfo(File jar,String activitiesMd5){ + this.jarMd5 = Util.md5File(jar) + this.pluginVersion = AppConstant.VER + this.manifestActivitiesMd5 = activitiesMd5 + } + +} diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy index 2f257de6..82fb73cf 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/ReClassTransform.groovy @@ -83,6 +83,7 @@ public class ReClassTransform extends Transform { // Compatible with path separators for window and Linux, and fit split param based on 'Pattern.quote' def variantDir = rootLocation.absolutePath.split(getName() + Pattern.quote(File.separator))[1] println ">>> variantDir: ${variantDir}" + String buildType = variantDir; CommonData.appModule = config.appModule CommonData.ignoredActivities = config.ignoredActivities @@ -91,7 +92,7 @@ public class ReClassTransform extends Transform { if (injectors.isEmpty()) { copyResult(inputs, outputProvider) // 跳过 reclass } else { - doTransform(inputs, outputProvider, config, injectors) // 执行 reclass + doTransform(inputs, buildType, outputProvider, config, injectors) // 执行 reclass } } @@ -116,12 +117,13 @@ public class ReClassTransform extends Transform { * 执行 Transform */ def doTransform(Collection inputs, + buildType, TransformOutputProvider outputProvider, Object config, def injectors) { /* 初始化 ClassPool */ - Object pool = initClassPool(inputs) + Object pool = initClassPool(inputs, buildType) /* 进行注入操作 */ Util.newSection() @@ -175,13 +177,18 @@ public class ReClassTransform extends Transform { includeJars.each { File jar = new File(it) String JarAfterzip = map.get(jar.getParent() + File.separatorChar + jar.getName()) - String dirAfterUnzip = JarAfterzip.replace('.jar', '') - // println ">>> 压缩目录 $dirAfterUnzip" - - Util.zipDir(dirAfterUnzip, JarAfterzip) + if(JarAfterzip != null){ + String dirAfterUnzip = JarAfterzip.replace('.jar', '') + File dirAfterUnzipFile = new File(dirAfterUnzip); + if(dirAfterUnzipFile.exists()){ + // println ">>> 压缩目录 $dirAfterUnzip" - // println ">>> 删除目录 $dirAfterUnzip" - FileUtils.deleteDirectory(new File(dirAfterUnzip)) + Util.zipDir(dirAfterUnzip, JarAfterzip) + + // println ">>> 删除目录 $dirAfterUnzip" + FileUtils.deleteDirectory(dirAfterUnzipFile) + } + } } } @@ -207,11 +214,11 @@ public class ReClassTransform extends Transform { /** * 初始化 ClassPool */ - def initClassPool(Collection inputs) { + def initClassPool(Collection inputs, String buildType) { Util.newSection() def pool = new ClassPool(true) // 添加编译时需要引用的到类到 ClassPool, 同时记录要修改的 jar 到 includeJars - Util.getClassPaths(project, globalScope, inputs, includeJars, map).each { + Util.getClassPaths(project, buildType, globalScope, inputs, includeJars, map).each { println " $it" pool.insertClassPath(it) } diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy index ce7d506b..486ce94e 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/inner/Util.groovy @@ -22,17 +22,21 @@ import com.android.build.api.transform.JarInput import com.android.build.api.transform.TransformInput import com.android.build.gradle.internal.scope.GlobalScope import com.android.sdklib.IAndroidTarget +import com.google.common.reflect.TypeToken +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.stream.JsonReader +import com.qihoo360.replugin.gradle.plugin.AppConstant +import com.qihoo360.replugin.gradle.plugin.manifest.ManifestAPI +import org.apache.commons.codec.digest.DigestUtils import org.apache.commons.io.FileUtils -import com.google.common.base.Charsets -import com.google.common.hash.Hashing import org.gradle.api.Project import java.nio.file.Files import java.nio.file.Paths import java.util.zip.ZipFile -import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES; - +import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES /** * @author RePlugin Team */ @@ -40,14 +44,16 @@ public class Util { /** 生成 ClassPool 使用的 ClassPath 集合,同时将要处理的 jar 写入 includeJars */ def - static getClassPaths(Project project, GlobalScope globalScope, Collection inputs, Set includeJars, Map map) { + static getClassPaths(Project project, String buildType, GlobalScope globalScope, Collection inputs, Set includeJars, Map map) { def classpathList = [] + includeJars.clear() + // android.jar classpathList.add(getAndroidJarPath(globalScope)) // 原始项目中引用的 classpathList - getProjectClassPath(project, inputs, includeJars, map).each { + getProjectClassPath(project, buildType, inputs, includeJars, map).each { classpathList.add(it) } @@ -57,15 +63,20 @@ public class Util { } /** 获取原始项目中的 ClassPath */ - def private static getProjectClassPath(Project project, + def private static getProjectClassPath(Project project, String buildType, Collection inputs, Set includeJars, Map map) { def classPath = [] def visitor = new ClassFileVisitor() - def projectDir = project.getRootDir().absolutePath println ">>> Unzip Jar ..." + def infoMap = readJarInjectorHistory(project, buildType) + final def injectDir = project.getBuildDir().path + + File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType; + + def activityMd5 = genActivityMd5(project, buildType); + boolean needSave = false inputs.each { TransformInput input -> input.directoryInputs.each { DirectoryInput dirInput -> @@ -77,44 +88,136 @@ public class Util { } input.jarInputs.each { JarInput jarInput -> - File jar = jarInput.file + def jar = jarInput.file def jarPath = jar.absolutePath - if (!jarPath.contains(projectDir)) { + def md5 = md5File(jar); + def reJar = new File(injectDir + File.separator + md5 + ".jar"); + def reJarPath = reJar.getAbsolutePath() + boolean needInject = checkNeedInjector(infoMap, jar, reJar, activityMd5, md5); - String jarZipDir = project.getBuildDir().path + - File.separator + FD_INTERMEDIATES + File.separator + "exploded-aar" + - File.separator + Hashing.sha1().hashString(jarPath, Charsets.UTF_16LE).toString() + File.separator + "class"; + //ReClassTransform.copyJar需要用到 + map.put(jarPath, reJarPath) + if (needInject) { + /* 将 jar 包解压,并将解压后的目录加入 classpath */ + // println ">>> 解压Jar${jarPath}" + def jarZipDir = reJar.getParent() + File.separatorChar + reJar.getName().replace('.jar', '') if (unzip(jarPath, jarZipDir)) { - def jarZip = jarZipDir + ".jar" + includeJars << jarPath classPath << jarZipDir + //保存修改的插件版本号 + needSave = true + infoMap.put(jarPath, new JarPatchInfo(jar, activityMd5)) + visitor.setBaseDir(jarZipDir) Files.walkFileTree(Paths.get(jarZipDir), visitor) - map.put(jarPath, jarZip) } + if (reJar.exists()) { + FileUtils.forceDelete(reJar) + } + } + } + } + if (needSave) { + saveJarInjectorHistory(project, buildType, infoMap) + } + return classPath + } - } else { + /** + * activities的md5 + */ + def static genActivityMd5(Project project, String buildType){ + def activityList = new ArrayList<>(); + new ManifestAPI().getActivities(project, buildType).each { + // 处理没有被忽略的 Activity + if (!(it in CommonData.ignoredActivities)) { + // + activityList.add(it) + } + } + Collections.sort(activityList, new Comparator(){ + @Override + int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); + return DigestUtils.md5Hex(activityList.toString()); + } - includeJars << jarPath - map.put(jarPath, jarPath) + /** + * 计算jar的md5 + */ + def static md5File(File jar) { + def fileInputStream = new FileInputStream(jar); + def md5 = DigestUtils.md5Hex(fileInputStream); + fileInputStream.close() + return md5 + } - /* 将 jar 包解压,并将解压后的目录加入 classpath */ - // println ">>> 解压Jar${jarPath}" - String jarZipDir = jar.getParent() + File.separatorChar + jar.getName().replace('.jar', '') - if (unzip(jarPath, jarZipDir)) { - classPath << jarZipDir - visitor.setBaseDir(jarZipDir) - Files.walkFileTree(Paths.get(jarZipDir), visitor) - } + def static checkNeedInjector( Map infoMap, File jar,File reJar,String activityMd5,String md5){ + boolean needInject = true - // 删除 jar - FileUtils.forceDelete(jar) + if (reJar.exists()) { + def info = infoMap.get(jar.getAbsolutePath()); + if (info != null) { + if (activityMd5.equals(info.manifestActivitiesMd5) + && AppConstant.VER.equals(info.pluginVersion) + && md5.equals(info.jarMd5)) { + needInject = false } } } - return classPath + + return needInject; + } + + /** + * 读取修改jar的记录 + */ + def static readJarInjectorHistory(Project project, String buildType) { + final String dir = FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType; + File file = new File(project.getBuildDir(), dir + File.separator + "version.json"); + if (!file.exists()) { + return new HashMap(); + } + Gson gson = new GsonBuilder() + .create(); + FileReader fileReader = new FileReader(file) + JsonReader jsonReader = new JsonReader(fileReader); + Map injectorMap = gson.fromJson(jsonReader, new TypeToken>() { + }.getType()); + jsonReader.close() + if (injectorMap == null) { + injectorMap = new HashMap(); + } + return injectorMap; + } + + /** + * 保存修改jar的记录 + */ + def static saveJarInjectorHistory(Project project,String buildType, Map injectorMap) { + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + final String dir = FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType; + File file = new File(project.getBuildDir(), dir + File.separator + "version.json"); + if (file.exists()) { + file.delete() + } else { + File p = file.getParentFile(); + if (!p.exists()) { + p.mkdirs() + } + } + file.createNewFile() + FileWriter fileWriter = new FileWriter(file); + String json = gson.toJson(injectorMap); + fileWriter.write(json) + fileWriter.close() } /**