-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
重定向修改jar #301
base: dev
Are you sure you want to change the base?
重定向修改jar #301
Changes from 4 commits
cce6904
58e5a0d
82142dd
e27e49d
c03c6aa
a4c067f
d4853cb
c2ca290
0ca937c
a49598f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
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 | ||
|
||
JarPatchInfo(){ | ||
|
||
} | ||
|
||
JarPatchInfo(String jarMd5, String pluginVersion) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 无用的import和方法,变量声明等,请移除 |
||
this.jarMd5 = jarMd5 | ||
this.pluginVersion = pluginVersion | ||
} | ||
|
||
JarPatchInfo(File jar){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 无用的import和方法,变量声明等,请移除 |
||
this.jarMd5 = Util.md5File(jar) | ||
this.pluginVersion = AppConstant.VER | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<TransformInput> inputs, | ||
buildType, | ||
TransformOutputProvider outputProvider, | ||
Object config, | ||
def injectors) { | ||
|
||
/* 初始化 ClassPool */ | ||
Object pool = initClassPool(inputs) | ||
Object pool = initClassPool(inputs, buildType) | ||
|
||
/* 进行注入操作 */ | ||
Util.newSection() | ||
|
@@ -207,11 +209,11 @@ public class ReClassTransform extends Transform { | |
/** | ||
* 初始化 ClassPool | ||
*/ | ||
def initClassPool(Collection<TransformInput> inputs) { | ||
def initClassPool(Collection<TransformInput> inputs,String buildType) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 此行代码格式化一下 |
||
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) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,11 +22,18 @@ 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 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.lang.reflect.Field | ||
import java.nio.file.Files | ||
import java.nio.file.Paths | ||
import java.util.zip.ZipFile | ||
|
@@ -40,14 +47,16 @@ public class Util { | |
|
||
/** 生成 ClassPool 使用的 ClassPath 集合,同时将要处理的 jar 写入 includeJars */ | ||
def | ||
static getClassPaths(Project project, GlobalScope globalScope, Collection<TransformInput> inputs, Set<String> includeJars, Map<String, String> map) { | ||
static getClassPaths(Project project, String buildType,GlobalScope globalScope, Collection<TransformInput> inputs, Set<String> includeJars, Map<String, String> map) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 代码格式化 |
||
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 +66,18 @@ public class Util { | |
} | ||
|
||
/** 获取原始项目中的 ClassPath */ | ||
def private static getProjectClassPath(Project project, | ||
def private static getProjectClassPath(Project project,String buildType, | ||
Collection<TransformInput> inputs, | ||
Set<String> includeJars, Map<String, String> map) { | ||
def classPath = [] | ||
def visitor = new ClassFileVisitor() | ||
def projectDir = project.getRootDir().absolutePath | ||
|
||
println ">>> Unzip Jar ..." | ||
|
||
Map<String, JarPatchInfo> infoMap = readJarInjectorHistory(project, buildType) | ||
final String injectDir = project.getBuildDir().path + | ||
File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar"+ File.separator + buildType; | ||
boolean needSave = false | ||
inputs.each { TransformInput input -> | ||
|
||
input.directoryInputs.each { DirectoryInput dirInput -> | ||
|
@@ -79,9 +91,9 @@ public class Util { | |
input.jarInputs.each { JarInput jarInput -> | ||
File jar = jarInput.file | ||
def jarPath = jar.absolutePath | ||
|
||
if (!jarPath.contains(projectDir)) { | ||
|
||
if (jarPath.contains(File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar")) { | ||
// | ||
}else if (!jarPath.contains(projectDir)) { | ||
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"; | ||
|
@@ -93,28 +105,140 @@ public class Util { | |
Files.walkFileTree(Paths.get(jarZipDir), visitor) | ||
map.put(jarPath, jarZip) | ||
} | ||
|
||
} else { | ||
//重定向jar | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里严格说,不是重定向文件,只是将文件解压到临时目录。请移除该注释。 |
||
String md5 = md5File(jar); | ||
File reJar = new File(injectDir + File.separator + md5 + ".jar"); | ||
jarPath = reJar.getAbsolutePath() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里不要复用之前的jarPath 变量,重新定义个reJarPath |
||
|
||
boolean needInject = false | ||
if (reJar.exists()) { | ||
//检查修改插件版本 | ||
JarPatchInfo info = infoMap.get(jar.getAbsolutePath()); | ||
if (info != null) { | ||
if (!AppConstant.VER.equals(info.pluginVersion)) { | ||
//版本变化了 | ||
needInject = true | ||
} else { | ||
if (!md5.equals(info.jarMd5)) { | ||
//原始jar内容变化 | ||
needInject = true | ||
} | ||
} | ||
} else { | ||
//无记录 | ||
needInject = true | ||
} | ||
} else { | ||
needInject = true; | ||
} | ||
//设置重定向jar | ||
setJarInput(jarInput, reJar) | ||
if (needInject) { | ||
/* 将 jar 包解压,并将解压后的目录加入 classpath */ | ||
// println ">>> 解压Jar${jarPath}" | ||
String jarZipDir = reJar.getParent() + File.separatorChar + reJar.getName().replace('.jar', '') | ||
if (unzip(jar.getAbsolutePath(), jarZipDir)) { | ||
|
||
includeJars << jarPath | ||
classPath << jarZipDir | ||
//保存修改的插件版本号 | ||
needSave = true | ||
infoMap.put(jar.getAbsolutePath(), new JarPatchInfo(jar)) | ||
|
||
visitor.setBaseDir(jarZipDir) | ||
Files.walkFileTree(Paths.get(jarZipDir), visitor) | ||
|
||
map.put(jarPath, jarPath) | ||
} | ||
// 删除 jar | ||
if (reJar.exists()) { | ||
FileUtils.forceDelete(reJar) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (needSave) { | ||
saveJarInjectorHistory(project, buildType, infoMap) | ||
} | ||
return classPath | ||
} | ||
|
||
includeJars << jarPath | ||
map.put(jarPath, jarPath) | ||
/** | ||
* 计算jar的md5 | ||
*/ | ||
def static md5File(File jar) { | ||
FileInputStream fileInputStream = new FileInputStream(jar); | ||
String 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 | ||
/** | ||
* 读取修改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<String, JarPatchInfo>(); | ||
} | ||
Gson gson = new GsonBuilder() | ||
.create(); | ||
FileReader fileReader = new FileReader(file) | ||
JsonReader jsonReader = new JsonReader(fileReader); | ||
Map<String, JarPatchInfo> injectorMap = gson.fromJson(jsonReader, new TypeToken<Map<String, JarPatchInfo>>() { | ||
}.getType()); | ||
jsonReader.close() | ||
if (injectorMap == null) { | ||
injectorMap = new HashMap<String, JarPatchInfo>(); | ||
} | ||
return injectorMap; | ||
} | ||
|
||
visitor.setBaseDir(jarZipDir) | ||
Files.walkFileTree(Paths.get(jarZipDir), visitor) | ||
} | ||
/** | ||
* 保存修改jar的记录 | ||
*/ | ||
def static saveJarInjectorHistory(Project project,String buildType, Map<String, JarPatchInfo> 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() | ||
} | ||
|
||
// 删除 jar | ||
FileUtils.forceDelete(jar) | ||
} | ||
/** | ||
* 反射,修改引用的jar路径 | ||
*/ | ||
def static setJarInput(JarInput jarInput, File rejar) { | ||
Field fileField = null; | ||
Class<?> clazz = jarInput.getClass(); | ||
while (fileField == null && clazz != Object.class) { | ||
try { | ||
fileField = clazz.getDeclaredField("file"); | ||
} catch (Exception e) { | ||
//ignore | ||
clazz = clazz.getSuperclass(); | ||
} | ||
} | ||
return classPath | ||
if (fileField != null) { | ||
fileField.setAccessible(true); | ||
fileField.set(jarInput, rejar); | ||
} | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
无用的import和方法,变量声明等,请移除