1.禁用task

    gradle.taskGraph.whenReady {
tasks.each { task ->
if (task.name.equalsIgnoreCase("transformClassesWithDelegateProguardTransformForRelease")
|| task.name.equalsIgnoreCase("transformClassesWithApplicationInjectForRelease"))
{
task.enabled = false
println '####disable task.. '+task.name
}
}
}

2.插入task或者脚本

  • task的​​doFirst​​​ 或​​doLast​
project.afterEvaluate {

File buildGradleFile = project.getBuildFile()
println "kblog:buildDir = " + project.getBuildDir().getAbsolutePath()
println "kblog:rootDir 2 " + buildGradleFile.getText()

Task buildRelease = project.tasks.findByName("buildRelease")
if (buildRelease) {
buildRelease.doLast {
File mappingFile = new File(project.getBuildDir().getAbsolutePath() + "/intermediates/bundle/mapping.txt")
if (mappingFile.exists()) {
mappingFile.renameTo(project.getBuildDir().getAbsolutePath() + "/intermediates/bundle/mapping.mochuan.4mtl.xml")
}
}
}

Task assembleRelease = project.tasks.findByName("assembleRelease")
if (assembleRelease) {
assembleRelease.doLast {
File mappingFile = new File(project.getBuildDir().getAbsolutePath() + "/intermediates/bundle/mapping.txt")
if (mappingFile.exists()) {
mappingFile.renameTo(project.getBuildDir().getAbsolutePath() + "/intermediates/bundle/mapping.mochuan.4mtl.xml")
}
}
}

}
  • dependOn
mOrganizeImportsTask.dependsOn project.tasks.findByName(REPLACE_TASK_NAME)

3.使用transform复制so

  • 将部分aar中的armeabi-v7a中的文件,复制到armeabi
import com.android.annotations.NonNull
import com.android.build.api.transform.Format
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformException
import com.android.build.api.transform.TransformInvocation

import com.android.build.gradle.internal.pipeline.ExtendedContentType

//@version mochuan.zhb on 2019/6/15.
//@Author Zheng Haibo
//@Blog github.com/nuptboyzhb
//@Company Alibaba Group
//@Description 将部分aar中的armeabi-v7a中的文件,复制到armeabi;
public class NativeTransform extends Transform {

public def projectContext;

public static final String ABI_ARMEABI = "armeabi";
public static final String ABI_ARMEABI_V7A = "armeabi-v7a";


@Override
public String getName() {
return "copyNativeLibraries"
}

@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
Set<QualifiedContent.ContentType> set = EnumSet.of(ExtendedContentType.NATIVE_LIBS);
return set;
}

@Override
public Set<QualifiedContent.Scope> getScopes() {
Set<QualifiedContent.Scope> set = EnumSet.of(
QualifiedContent.Scope.EXTERNAL_LIBRARIES,
QualifiedContent.Scope.PROJECT,
QualifiedContent.Scope.SUB_PROJECTS
)
return set;
}

@Override
public boolean isIncremental() {
return false;
}

@Override
public void transform(@NonNull TransformInvocation transformInvocation)
throws TransformException, InterruptedException, IOException {

def outputProvider = transformInvocation.outputProvider
if (!transformInvocation.isIncremental()) {
outputProvider.deleteAll()
}

transformInvocation.inputs.forEach { transformInput ->

transformInput.directoryInputs.forEach { directoryInput ->
def dir = directoryInput.file.toPath()
def output = outputProvider.getContentLocation(
directoryInput.name, inputTypes, directoryInput.scopes, Format.DIRECTORY)

projectContext.fileTree(dir).forEach { input ->
def path = dir.relativize(input.toPath())
def destDir = output.toPath().resolve(path).toFile().parentFile

def abiName = input.parentFile.name

println 'kblog:path = ' + path + ",abiName = " + abiName

if (ABI_ARMEABI_V7A == abiName) {
def v5Dir = new File(destDir.parentFile, ABI_ARMEABI)

projectContext.copy { spec ->
spec.from(input)
spec.into(v5Dir)
}
println 'kblog:copy ' + path + " into " + v5Dir.getAbsolutePath()
projectContext.copy { spec ->
spec.from(input)
spec.into(destDir)
}
} else if (ABI_ARMEABI == abiName) {
def v7Dir = new File(destDir.parentFile, ABI_ARMEABI_V7A)
if (!(new File(v7Dir, input.name)).isFile()) {
// copy if not present
projectContext.copy { spec ->
spec.from(input)
spec.into(v7Dir)
}
}
projectContext.copy { spec ->
spec.from(input)
spec.into(destDir)
}
} else {
// just copy
projectContext.copy { spec ->
spec.from(input)
spec.into(destDir)
}
}
}
}
}

}


}

def transform = new NativeTransform()
transform.projectContext = project
project.android.registerTransform(transform)

4.使用Transform检查类

  • 用于检查依赖的类是否有重复,如果重复,则随机删除一个
import org.apache.commons.io.FileUtils;
import com.android.annotations.NonNull
import com.android.build.api.transform.Format
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.TransformOutputProvider
import com.android.build.api.transform.TransformException
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.internal.pipeline.TransformManager

import java.util.jar.JarFile
import java.util.zip.ZipEntry;


//@version mochuan.zhb on 2021/6/23.
//@Author Zheng Haibo
//@Blog github.com/nuptboyzhb
//@Company Alibaba Group
//@Description
public class FilterJarTransform extends Transform {

public def projectContext;


@Override
public String getName() {
return "filterJar"
}

@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_JARS;
}

@Override
public Set<? super QualifiedContent.Scope> getScopes() {
return TransformManager.SCOPE_FULL_PROJECT;
}

@Override
public boolean isIncremental() {
return false;
}

private Map<String, String> clazzJarMap = new HashMap<>();

public static class FileModel{
public FileModel(File file1,JarInput jarInput1){
this.jarInput = jarInput1;
this.file = file1;
}
public File file;
public JarInput jarInput;
}

@Override
public void transform(@NonNull TransformInvocation transformInvocation)
throws TransformException, InterruptedException, IOException {

List<FileModel> inputFiles = new ArrayList<>();
for (TransformInput input : transformInvocation.getInputs()) {
for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
if (directoryInput.getFile().isDirectory()) {
inputFiles.add(new FileModel(directoryInput.getFile(),null));
}
}
for (JarInput jarInput : input.getJarInputs()) {
if (jarInput.getFile().isFile()) {
inputFiles.add(new FileModel(jarInput.getFile(),jarInput));
}
}
}
Map<String, String> multiJarPath = new HashMap<>();
inputFiles.each { fileModel ->
println('process file = ' + fileModel.file.getAbsolutePath())
TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
if (fileModel.file.isFile() && fileModel.file.getName().endsWith(".jar")) {
JarFile jarFile = new JarFile(fileModel.file);
Enumeration<? extends ZipEntry> entries = jarFile.entries();
boolean isMultiJar = false;
while (entries.hasMoreElements()) {
ZipEntry element = entries.nextElement();
//element.setExtra();
String currentClass = element.getName();
String path = clazzJarMap.get(currentClass);
if (path != null && path.length() > 0) {
println('find multi-class: ' + currentClass)
isMultiJar = true;
multiJarPath.put(path, fileModel.file.getAbsolutePath());
} else {
clazzJarMap.put(currentClass, fileModel.file.getAbsolutePath());
}
}
if (!isMultiJar) {
if(fileModel.jarInput != null){
File dest = outputProvider.getContentLocation(
fileModel.jarInput.getFile().getAbsolutePath(),
fileModel.jarInput.getContentTypes(),
fileModel.jarInput.getScopes(),
Format.JAR);
FileUtils.copyFile(fileModel.file, dest);
}else{
//
println("TODO:///"+fileModel.file.getAbsolutePath())
}
}
}
}

for (Map.Entry<String, String> entry : multiJarPath) {
println('### multi-jar: ' + entry.getKey() + "," + entry.getValue())
}

}

}

def transform = new FilterJarTransform()
transform.projectContext = project
project.android.registerTransform(transform)

5.Hook所有task的输入输出

/**
* @version mochuan.zhb on 2019/7/3.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description task执行的顺序以及所有的input/output
*/
public class TaskIOPlugin implements Plugin<Project> {

private Map<String, TaskNode> taskExecuteMap = new LinkedHashMap<>();

private File buildDir;

private static long gradleStartTime = 0L;

@Override
public void apply(Project project) {

buildDir = project.getBuildDir();
project.getGradle().addBuildListener(new BuildListener() {
@Override
public void buildStarted(Gradle gradle) {
myLog("buildStarted")
}

@Override
public void settingsEvaluated(Settings settings) {
myLog("settingsEvaluated")
}

@Override
public void projectsLoaded(Gradle gradle) {
myLog("projectsLoaded")
}

@Override
public void projectsEvaluated(Gradle gradle) {
gradleStartTime = System.currentTimeMillis();
myLog("projectsEvaluated")
}

@Override
public void buildFinished(BuildResult buildResult) {
myLog("buildFinished")
printTaskMap();
}
});
taskExecuteMap.clear()
project.getGradle().getTaskGraph().addTaskExecutionListener(new TaskExecutionListener() {
@Override
public void beforeExecute(Task task) {
TaskNode taskNode = taskExecuteMap.get(getTaskName(task))
if (taskNode != null) {
println("== taskNode added ...")
return;
}
List<String> inputFileList = new ArrayList<>();
task.getInputs().files.each { item ->
inputFileList.add(item.getAbsolutePath())
}
taskNode = new TaskNode();
taskNode.startTime = System.currentTimeMillis()
taskNode.name = getTaskName(task);
taskNode.clazz = task.getClass().getName()
taskNode.input = inputFileList
taskExecuteMap.put(getTaskName(task), taskNode)
}

@Override
public void afterExecute(Task task, TaskState taskState) {
List<String> outputList = new ArrayList<>();
task.getOutputs().files.each { item ->
outputList.add(item.getAbsolutePath())
}
TaskNode taskNode = taskExecuteMap.get(getTaskName(task))
if (taskNode == null) {
taskNode = new TaskNode();
taskNode.name = getTaskName(task);
taskNode.clazz = task.getClass().getName()
}
taskNode.endTime = System.currentTimeMillis()
taskNode.output = outputList
taskExecuteMap.put(getTaskName(task), taskNode)
}
});

project.getGradle().getTaskGraph().addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
@Override
public void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
myLog("---graphPopulated---")
}
});

}

private static String getTaskName(Task task) {
String taskName = String.format("%s:%s", task.getProject().getName(), task.getName());
println("== afterExecute > " + taskName)
return taskName;
}

private static void myLog(String content) {
myLog(content, false)
}

private static void myLog(String content, boolean node) {
if (node) {
println(" [node]:" + content)
} else {
println("[====task-io===]:" + content)
}
}

private void printTaskMap() {
println("start task io file.")
StringBuilder stringBuilder = new StringBuilder()
StringBuilder taskList = new StringBuilder();
boolean lastItem = false
int taskCount = 0;
List<TimeData> timeDataList = new ArrayList<>();
taskExecuteMap.each { item ->
taskCount++
TaskNode taskNode = item.value
long time = taskNode.endTime - taskNode.startTime
if (time > 0) {
TimeData timeData = new TimeData();
timeData.taskName = taskNode.name;
timeData.time = time;
timeData.startTime4Charts = taskNode.startTime - gradleStartTime
timeDataList.add(timeData);
}
taskList.append(taskCount).append(" ").append(taskNode.name).append("(" + time + " ms)").append(" ::<" + taskNode.clazz + ">").append("\n")
taskList.append(" ↓").append("\n")
stringBuilder.append(":").append(taskNode.name).append("\n")
stringBuilder.append(' input→').append("\n")
int count = 0
taskNode.input.each { i ->
count++
if (count == taskNode.input.size()) {
stringBuilder.append(" └──" + i).append("\n")
lastItem = true
} else {
stringBuilder.append(" ├──" + i).append("\n")
lastItem = false
}
if (new File(i).isDirectory()) {
printChildFiles(new File(i), stringBuilder, lastItem);
}
}
stringBuilder.append(' output→').append("\n")
count = 0
taskNode.output.each { o ->
count++
if (count == taskNode.output.size()) {
stringBuilder.append(" └──" + o).append("\n")
lastItem = true
} else {
stringBuilder.append(" ├──" + o).append("\n")
lastItem = false
}
if (new File(o).isDirectory()) {
printChildFiles(new File(o), stringBuilder, lastItem);
}
}
}

stringBuilder.append("\n\n\n")
stringBuilder.append(taskList.toString())

printData4Charts(timeDataList, stringBuilder);

printData4Markdown(timeDataList, stringBuilder)

printTimeline4Text(new ArrayList<TimeData>(timeDataList), stringBuilder)

File outPutDir = new File(buildDir, "/outputs/");
if (!outPutDir.exists()) {
outPutDir.mkdirs()
}
long currentTime = System.currentTimeMillis();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date(currentTime);
String fileName = formatter.format(date) + "_taskio.txt";
File resultFile = new File(outPutDir, fileName);
boolean success = FileUtils.toFile(resultFile, stringBuilder)
if (success) {
println("task io file finished. file path = " + resultFile.getAbsolutePath())
} else {
println("task io file failed.")
}
}

/**
* 递归打印
* @param dir
* @param stringBuilder
* @param lastItem
*/
private void printChildFiles(File dir, StringBuilder stringBuilder, boolean lastItem) {
int childCount = 0;
boolean nextLastItem = false;
dir.listFiles().each { child ->
childCount++
if (lastItem) {
if (childCount == dir.listFiles().size()) {
stringBuilder.append(" └──" + child.getAbsolutePath()).append("\n")
nextLastItem = true
} else {
stringBuilder.append(" ├──" + child.getAbsolutePath()).append("\n")
nextLastItem = false
}
} else {
if (childCount == dir.listFiles().size()) {
stringBuilder.append(" | └──" + child.getAbsolutePath()).append("\n")
} else {
stringBuilder.append(" | ├──" + child.getAbsolutePath()).append("\n")
}
}
if (child.isDirectory()) {
printChildFiles(child, stringBuilder, nextLastItem);
}
}
}

/**
* data: [
*{value: 1048, name: '搜索引擎'},
*{value: 735, name: '直接访问'},
*{value: 580, name: '邮件营销'},
*{value: 484, name: '联盟广告'},
*{value: 300, name: '视频广告'}* ],
* @param timeDataList
* @param stringBuilder
*/
private void printData4Charts(List<TimeData> timeDataList, StringBuilder stringBuilder) {
stringBuilder.append("\n\n\n")
stringBuilder.append("option = {\n" +
" title: {\n" +
" text: 'Task耗时分布',\n" +
" subtext: '耗时详情',\n" +
" left: 'center'\n" +
" },\n" +
" tooltip: {\n" +
" trigger: 'item'\n" +
" },\n" +
" series: [\n" +
" {\n" +
" name: '耗时详情',\n" +
" type: 'pie',\n" +
" radius: '50%',\n" +
" data: [").append("\n");
for (TimeData timeData : timeDataList) {
stringBuilder.append("{value:")
.append(timeData.time)
.append(",name:'")
.append(timeData.taskName)
.append("'},").append("\n")
}
stringBuilder.append("],\n" +
" emphasis: {\n" +
" itemStyle: {\n" +
" shadowBlur: 10,\n" +
" shadowOffsetX: 0,\n" +
" shadowColor: 'rgba(0, 0, 0, 0.5)'\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
"};");
}


/**
* 打印markdown
* @param timeDataList
* @param stringBuilder
*/
private void printData4Markdown(List<TimeData> timeDataList, StringBuilder stringBuilder) {
timeDataList.sort(new Comparator<TimeData>() {
@Override
int compare(TimeData timeData, TimeData t1) {
return (int) (t1.time - timeData.time);
}
});
stringBuilder.append("\n\n\n")
stringBuilder.append("|task名称|耗时(ms)|备注|").append("\n")
stringBuilder.append("|:---|:---|:---|").append("\n")
for (TimeData timeData : timeDataList) {
stringBuilder.append(String.format("|%s|%s||", timeData.taskName, timeData.time)).append("\n")
}
stringBuilder.append("\n\n\n")
}


/**
* 打印任务执行的时间线
* @param timeDataList
* @param stringBuilder
*/
private void printTimeline4Text(List<TimeData> timeDataList, StringBuilder stringBuilder) {
timeDataList.sort(new Comparator<TimeData>() {
@Override
int compare(TimeData timeData, TimeData t1) {
return (int) (timeData.startTime4Charts - t1.startTime4Charts);
}
});
stringBuilder.append("\n\n\n")
stringBuilder.append("task执行的timeline").append("\n")
TimeData lastItem = timeDataList.get(timeDataList.size() - 1);
int step = (int) (lastItem.startTime4Charts + lastItem.time) / 120 + 1;
println('[taskio]:step = '+step)
char eChar = ' ';
char sChar = '▇';
for (TimeData timeData : timeDataList) {
int eCount = (int) (timeData.startTime4Charts / step) + 1;
int sCount = (int) (timeData.time / step) + 1;
for (int i = 0; i < eCount; i++) {
stringBuilder.append(eChar);
}
for (int i = 0; i < sCount; i++) {
stringBuilder.append(sChar);
}
stringBuilder.append("(").append(timeData.time).append("ms)").append(timeData.taskName).append("\n");
}
stringBuilder.append("\n\n\n")
}

}

6.对Jar文件进行处理

    /**
* 从inputJarPath删除特定的class,将剩余的class,写入到outPutJarPath
* @param inputJarPath
* @param outPutJarPath
* @param clazzNameSet
*/
private void removeClassInJar(String inputJarPath, String outPutJarPath, HashSet<String> clazzNameSet) {
try {
JarFile inputJarFile = new JarFile(inputJarPath);
Enumeration<JarEntry> inputJarEntries = inputJarFile.entries();
//将oldJarEntries中的oldJar替换掉,并将对应的新Jar生成
File outFile = new File(outPutJarPath);
if (outFile.exists()) {
outFile.delete();
}
outFile.createNewFile();
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
CheckedOutputStream checksum = new CheckedOutputStream(fileOutputStream, new Adler32());
ZipOutputStream out = new ZipOutputStream(checksum);
while (inputJarEntries.hasMoreElements()) {
ZipEntry element = inputJarEntries.nextElement();
String currentClassName = element.getName();
if (!clazzNameSet.contains(currentClassName)) {
try {
out.putNextEntry(element);
InputStream entryIn = inputJarFile.getInputStream(element);
int read;
byte[] buf = new byte[4096];
while ((read = entryIn.read(buf, 0, buf.length)) != -1) {
out.write(buf, 0, read);
}
out.flush();
out.closeEntry();
entryIn.close();
} catch (Exception e) {
throw e;
}
}
}
out.finish();
out.close();
fileOutputStream.close();
inputJarFile.close();
} catch (IOException e) {
e.printStackTrace();
}

}

7.修改AndroidManifest

//@version mochuan.zhb on 2019/4/15.
//@Author Zheng Haibo
//@Blog github.com/nuptboyzhb
//@Company Alibaba Group
//@Description 适配mateX + targetSdkVersion升级适配:透明背景的Activity,删除其screenOrientation属性


import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.TransformerFactory

project.afterEvaluate {
def LOG_TAG = 'ManifestFlodHandler: '
Task taskProcessReleaseManifest = project.tasks.findByName('splitsDiscoveryTaskRelease')
if (taskProcessReleaseManifest) {
taskProcessReleaseManifest.doFirst {
println(LOG_TAG + 'splitsDiscoveryTaskRelease doFirst files: ' + it.outputs.files.files)
def manifestFileName = './launcher/build/intermediates/manifests/full/release/AndroidManifest.xml'
def document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(manifestFileName)
def activities = document.getElementsByTagName('activity')
println(LOG_TAG + 'Activites size: ' + activities.getLength())

def buffReader = new BufferedReader(new FileReader('./launcher/recreate_activites'))
def recreateActivities = []
def line = null
while (null != (line = buffReader.readLine())) {
if (line.startsWith('com.')) {
recreateActivities.add(line)
}
}
buffReader.close()
def attNameName = 'android:name'
def attConfigChangesName = 'android:configChanges'
def attResizeableActivityName = 'android:resizeableActivity'
//android:screenOrientation="portrait"
def attScreenOrientationName = 'android:screenOrientation'
def attThemeName = 'android:theme'
for (int i = 0; i < activities.getLength(); i++) {
def attributes = activities.item(i).getAttributes()

def attName = attributes.getNamedItem(attNameName)
def attConfigChanges = attributes.getNamedItem(attConfigChangesName)
def attResizeableActivity = attributes.getNamedItem(attResizeableActivityName)
def attScreenOrientation = attributes.getNamedItem(attScreenOrientationName)
def attTheme = attributes.getNamedItem(attThemeName)

// add multi-window support
if (attResizeableActivity == null) {
def attResizeNew = document.createAttribute(attResizeableActivityName)
attResizeNew.setNodeValue('true')
attributes.setNamedItem(attResizeNew)
} else if (attResizeableActivity.getNodeValue() == 'false') {
println(LOG_TAG + "不支持多窗口 ==> " + attName.getNodeValue())
}

if (attName.getNodeValue() in recreateActivities) {
println(LOG_TAG + ' ' + attName.getNodeValue() + ' In WhiteList, Use Recreate Plan.')
continue
}

// add config change support
if (attConfigChanges != null) {
//screenSize|smallestScreenSize|screenLayout
def value = attConfigChanges.getNodeValue()
if (!value.contains('screenSize')) {
value += '|screenSize'
}
if (!value.contains('smallestScreenSize')) {
value += '|smallestScreenSize'
}
if (!value.contains('screenLayout')) {
value += '|screenLayout'
}
attConfigChanges.setNodeValue(value)
} else {
def attConfigNew = document.createAttribute(attConfigChangesName)
attConfigNew.setNodeValue('screenSize|smallestScreenSize|screenLayout')
attributes.setNamedItem(attConfigNew)
}

if (attTheme != null && attScreenOrientation != null) {
if (attTheme.getNodeValue() != null && (attTheme.getNodeValue().toLowerCase().contains("translucent")
|| attTheme.getNodeValue().toLowerCase().contains("transparent")
|| attTheme.getNodeValue().toLowerCase().contains("vrtheme")
|| attTheme.getNodeValue().toLowerCase().contains("alipaylogintheme")
|| attTheme.getNodeValue().toLowerCase().contains("mspapppaytheme")
|| attTheme.getNodeValue().toLowerCase().contains("mspapptheme"))) {
attributes.removeNamedItem(attScreenOrientationName)
println 'remove screenOrientation:' + attName
}
}

}

//save change to AndroidManifest.xml
def transformer = TransformerFactory.newInstance().newTransformer()
transformer.transform(new DOMSource(document), new StreamResult(manifestFileName))

}
}
Task taskProcessDebugManifest = project.tasks.findByName('splitsDiscoveryTaskDebug')
if (taskProcessDebugManifest) {
taskProcessDebugManifest.doFirst {
println(LOG_TAG + 'splitsDiscoveryTaskDebug doFirst files: ' + it.outputs.files.files)
def manifestFileName = './launcher/build/intermediates/manifests/full/debug/AndroidManifest.xml'
def document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(manifestFileName)
def activities = document.getElementsByTagName('activity')
println(LOG_TAG + 'Activites size: ' + activities.getLength())

def buffReader = new BufferedReader(new FileReader('./launcher/recreate_activites'))
def recreateActivities = []
def line = null
while (null != (line = buffReader.readLine())) {
if (line.startsWith('com.')) {
recreateActivities.add(line)
}
}
buffReader.close()
def attNameName = 'android:name'
def attConfigChangesName = 'android:configChanges'
def attResizeableActivityName = 'android:resizeableActivity'
//android:screenOrientation="portrait"
def attScreenOrientationName = 'android:screenOrientation'
def attThemeName = 'android:theme'
for (int i = 0; i < activities.getLength(); i++) {
def attributes = activities.item(i).getAttributes()

def attName = attributes.getNamedItem(attNameName)
def attConfigChanges = attributes.getNamedItem(attConfigChangesName)
def attResizeableActivity = attributes.getNamedItem(attResizeableActivityName)
def attScreenOrientation = attributes.getNamedItem(attScreenOrientationName)
def attTheme = attributes.getNamedItem(attThemeName)

// add multi-window support
if (attResizeableActivity == null) {
def attResizeNew = document.createAttribute(attResizeableActivityName)
attResizeNew.setNodeValue('true')
attributes.setNamedItem(attResizeNew)
} else if (attResizeableActivity.getNodeValue() == 'false') {
println(LOG_TAG + "不支持多窗口 ==> " + attName.getNodeValue())
}

if (attName.getNodeValue() in recreateActivities) {
println(LOG_TAG + ' ' + attName.getNodeValue() + ' In WhiteList, Use Recreate Plan.')
continue
}

// add config change support
if (attConfigChanges != null) {
//screenSize|smallestScreenSize|screenLayout
def value = attConfigChanges.getNodeValue()
if (!value.contains('screenSize')) {
value += '|screenSize'
}
if (!value.contains('smallestScreenSize')) {
value += '|smallestScreenSize'
}
if (!value.contains('screenLayout')) {
value += '|screenLayout'
}
attConfigChanges.setNodeValue(value)
} else {
def attConfigNew = document.createAttribute(attConfigChangesName)
attConfigNew.setNodeValue('screenSize|smallestScreenSize|screenLayout')
attributes.setNamedItem(attConfigNew)
}

if (attTheme != null && attScreenOrientation != null) {
if (attTheme.getNodeValue() != null && (attTheme.getNodeValue().toLowerCase().contains("translucent")
|| attTheme.getNodeValue().toLowerCase().contains("transparent")
|| attTheme.getNodeValue().toLowerCase().contains("vrtheme")
|| attTheme.getNodeValue().toLowerCase().contains("alipaylogintheme")
|| attTheme.getNodeValue().toLowerCase().contains("mspapppaytheme")
|| attTheme.getNodeValue().toLowerCase().contains("mspapptheme"))) {
attributes.removeNamedItem(attScreenOrientationName)
println 'remove screenOrientation:' + attName
}
}

}

//save change to AndroidManifest.xml
def transformer = TransformerFactory.newInstance().newTransformer()
transformer.transform(new DOMSource(document), new StreamResult(manifestFileName))

}
}

}

排查编译问题的思路

  • 分析每个Task的构建产物
  • 反编译jar
  • 反编译apk