-
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 2 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,32 @@ | ||
|
||
package com.qihoo360.replugin.gradle.plugin.inner | ||
|
||
import com.qihoo360.replugin.gradle.plugin.AppConstant | ||
|
||
/** | ||
* @author 247321453 | ||
*/ | ||
public class InjectorVersion { | ||
|
||
def String jarPath | ||
|
||
def String jarMd5 | ||
|
||
def String pluginVersion | ||
|
||
InjectorVersion(){ | ||
|
||
} | ||
|
||
InjectorVersion(String jarPath, String jarMd5, String pluginVersion) { | ||
this.jarPath = jarPath | ||
this.jarMd5 = jarMd5 | ||
this.pluginVersion = pluginVersion | ||
} | ||
|
||
InjectorVersion(File jar){ | ||
this.jarPath = jar.getAbsolutePath() | ||
this.jarMd5 = Util.md5File(jar) | ||
this.pluginVersion = AppConstant.VER | ||
} | ||
} |
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 | ||
|
@@ -65,7 +72,8 @@ public class Util { | |
def projectDir = project.getRootDir().absolutePath | ||
|
||
println ">>> Unzip Jar ..." | ||
|
||
Map<String, InjectorVersion> injectorMap = readJarInjectorHistory(project) | ||
boolean needSave = false | ||
inputs.each { TransformInput input -> | ||
|
||
input.directoryInputs.each { DirectoryInput dirInput -> | ||
|
@@ -95,26 +103,146 @@ public class Util { | |
} | ||
|
||
} else { | ||
//重定向jar | ||
if (jarPath.contains(File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar")) { | ||
// | ||
} else { | ||
String md5 = md5File(jar); | ||
File reJar = new File(project.getBuildDir().path + | ||
File.separator + FD_INTERMEDIATES + File.separator + "replugin-jar" | ||
+ File.separator + md5 + ".jar"); | ||
String reJarPath = reJar.getAbsolutePath() | ||
|
||
boolean needInject = false | ||
if (reJar.exists()) { | ||
//检查修改插件版本 | ||
InjectorVersion injectorVersion = injectorMap.get(jar.getAbsolutePath()); | ||
if (injectorVersion != null) { | ||
if (!AppConstant.VER.equals(injectorVersion.pluginVersion)) { | ||
//版本变化了 | ||
needInject = true | ||
} else { | ||
if (!md5.equals(injectorVersion.jarMd5)) { | ||
//原始jar内容变化 | ||
needInject = true | ||
} | ||
} | ||
} else { | ||
//无记录 | ||
needInject = true | ||
} | ||
} else { | ||
FileUtils.copyFile(jar, reJar) | ||
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. 拷贝jar是不必要的。可以参考源码中对非项目目录下的jar的解压方式。 |
||
needInject = true; | ||
} | ||
//设置重定向jar | ||
setJarInput(jarInput, reJar) | ||
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. 通过反射方式,修改gradle的transform.JarInput字段的重定向jar是不必要的。(实际上可以在不改动默认编译流程的前提下,实现功能。如有疑问,可以私信交流哈) |
||
if (needInject) { | ||
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. 目前看,只在一种buildType下会去动态编译,后续依次编译其他buildType时,就不会去动态编译了。这样是存在潜在问题的,比如用户指定了不同buildType下的manifest中包含有不同acitivity,那么就可能存在这样一种情况:
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. 这个的确 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. 还有一种case,就是一次完整的buildtype遍历编译完成后,用户在manifest中新增注册了jar中的某个Activity,那也会出现不替换jar中的对应顶级父类的情况 |
||
includeJars << reJarPath | ||
map.put(reJarPath, reJarPath) | ||
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. 这里还请与其他jar处理逻辑分支保持一致,保存的是orginal 'jarPath' |
||
|
||
/* 将 jar 包解压,并将解压后的目录加入 classpath */ | ||
// println ">>> 解压Jar${jarPath}" | ||
String jarZipDir = reJar.getParent() + File.separatorChar + reJar.getName().replace('.jar', '') | ||
if (unzip(jarPath, jarZipDir)) { | ||
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. 在原逻辑中的unzip(...)之前所新增的以上全部代码,都可以封装到一个isNeedInject(...)方法中,这样这块核心逻辑分支会整洁很多。 |
||
classPath << jarZipDir | ||
//保存修改的插件版本号 | ||
needSave = true | ||
injectorMap.put(jar.getAbsolutePath(), new InjectorVersion(jar)) | ||
|
||
visitor.setBaseDir(jarZipDir) | ||
Files.walkFileTree(Paths.get(jarZipDir), visitor) | ||
} | ||
// 删除 jar | ||
FileUtils.forceDelete(reJar) | ||
} else { | ||
map.remove(reJarPath) | ||
includeJars.remove(reJarPath) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (needSave) { | ||
saveJarInjectorHistory(project, injectorMap) | ||
} | ||
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) { | ||
File file = new File(project.getBuildDir(), FD_INTERMEDIATES | ||
+ File.separator + "replugin-jar" + File.separator + "version.json"); | ||
if (!file.exists()) { | ||
return new HashMap<String, InjectorVersion>(); | ||
} | ||
Gson gson = new GsonBuilder() | ||
.create(); | ||
FileReader fileReader = new FileReader(file) | ||
JsonReader jsonReader = new JsonReader(fileReader); | ||
Map<String, InjectorVersion> injectorMap = gson.fromJson(jsonReader, new TypeToken<Map<String, InjectorVersion>>() { | ||
}.getType()); | ||
jsonReader.close() | ||
if (injectorMap == null) { | ||
injectorMap = new HashMap<String, InjectorVersion>(); | ||
} | ||
return injectorMap; | ||
} | ||
|
||
visitor.setBaseDir(jarZipDir) | ||
Files.walkFileTree(Paths.get(jarZipDir), visitor) | ||
} | ||
/** | ||
* 保存修改jar的记录 | ||
*/ | ||
def static saveJarInjectorHistory(Project project, Map<String, InjectorVersion> injectorMap) { | ||
Gson gson = new GsonBuilder() | ||
.setPrettyPrinting() | ||
.create(); | ||
File file = new File(project.getBuildDir(), FD_INTERMEDIATES | ||
+ File.separator + "replugin-jar" + File.separator + "version.json"); | ||
if (file.exists()) { | ||
file.delete() | ||
} else { | ||
File dir = file.getParentFile(); | ||
if (!dir.exists()) { | ||
dir.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.
这里方法和变量的命名,还是建议更精准一些,比如InjectorVersion ,里面包含了插件版本信息,和jar的md5信息,类名建议叫JarInfo(或者有更确切的名字也可以),包括injectorMap也建议修改为jarInfoMap,这部分相关的代码和类名尽量不带‘injector’字样,毕竟‘injector’的本意是注入器,这只应该injector模块及相关部分中使用。
另:json文件也建议与此保持一致,这样代码整体一致性更强些。
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.
或者类名叫BuildCacheInfo,更确切些,供参考