Skip to content
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

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

无用的import和方法,变量声明等,请移除


}

JarPatchInfo(String jarMd5, String pluginVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

无用的import和方法,变量声明等,请移除

this.jarMd5 = jarMd5
this.pluginVersion = pluginVersion
}

JarPatchInfo(File jar){
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Expand Up @@ -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
Expand All @@ -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
}
}

Expand All @@ -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()
Expand Down Expand Up @@ -207,11 +209,11 @@ public class ReClassTransform extends Transform {
/**
* 初始化 ClassPool
*/
def initClassPool(Collection<TransformInput> inputs) {
def initClassPool(Collection<TransformInput> inputs,String buildType) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
}

Expand All @@ -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 ->
Expand All @@ -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";
Expand All @@ -93,28 +105,140 @@ public class Util {
Files.walkFileTree(Paths.get(jarZipDir), visitor)
map.put(jarPath, jarZip)
}

} else {
//重定向jar
Copy link
Contributor

Choose a reason for hiding this comment

The 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()
Copy link
Contributor

Choose a reason for hiding this comment

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

/**
Expand Down