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 all 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,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
}

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

Expand All @@ -207,11 +214,11 @@ public class ReClassTransform extends Transform {
/**
* 初始化 ClassPool
*/
def initClassPool(Collection<TransformInput> inputs) {
def initClassPool(Collection<TransformInput> 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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,38 @@ 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
*/
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) {
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 +63,20 @@ 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 ..."
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 ->
Expand All @@ -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<String>(){
@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<String, JarPatchInfo> 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<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;
}

/**
* 保存修改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()
}

/**
Expand Down