我們在插樁的過程中,需要知道我們的包名和過濾出我們的Activity來,所以我就想到了讀取清單文件AndroidManifest
那么我們就繼續(xù)回到我們的Transform類的transform方法中來。
首先我們要做的就是拿到清單文件
File rootLocation = null
try {
rootLocation = outputProvider.rootLocation
} catch (Throwable e) {
//android gradle plugin 3.0.0+ 修改了私有變量,將其移動(dòng)到了IntermediateFolderUtils中去
rootLocation = outputProvider.folderUtils.getRootFolder()
}
if (rootLocation == null) {
throw new GradleException("can't get transform root location")
}
println ">>> rootLocation: ${rootLocation}"
// 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}"
我們首先要得到一個(gè)rootLocation,也就是build項(xiàng)目時(shí)候存放文件的目錄,大致的結(jié)構(gòu)就是
D:\......\項(xiàng)目名稱\build\intermediates\transforms\xxxTask\debug\
然后我們從該目錄里截取最后的那個(gè)文件夾名稱也就是debug,賦值給變量variantDir
然后我們有一個(gè)方法來讀取我們的清單文件的路徑
/**
* 獲取 AndroidManifest.xml 路徑
*/
def static manifestPath(Project project, String variantDir) {
// Compatible with path separators for window and Linux, and fit split param based on 'Pattern.quote'
def variantDirArray = variantDir.split(Pattern.quote(File.separator))
String variantName = ""
variantDirArray.each {
//首字母大寫進(jìn)行拼接
variantName += it.capitalize()
}
println ">>> variantName:${variantName}"
//獲取processManifestTask
def processManifestTask = project.tasks.getByName("process${variantName}Manifest")
//如果processManifestTask存在的話
//transform的task目前能保證在processManifestTask之后執(zhí)行
if (processManifestTask) {
File result = null
//正常的manifest
File manifestOutputFile = null
//instant run的manifest
File instantRunManifestOutputFile = null
try {
manifestOutputFile = processManifestTask.getManifestOutputFile()
instantRunManifestOutputFile = processManifestTask.getInstantRunManifestOutputFile()
} catch (Exception e) {
manifestOutputFile = new File(processManifestTask.getManifestOutputDirectory(), "AndroidManifest.xml")
instantRunManifestOutputFile = new File(processManifestTask.getInstantRunManifestOutputDirectory(), "AndroidManifest.xml")
}
if (manifestOutputFile == null && instantRunManifestOutputFile == null) {
throw new GradleException("can't get manifest file")
}
//打印
println " manifestOutputFile:${manifestOutputFile} ${manifestOutputFile.exists()}"
println " instantRunManifestOutputFile:${instantRunManifestOutputFile} ${instantRunManifestOutputFile.exists()}"
//先設(shè)置為正常的manifest
result = manifestOutputFile
try {
//獲取instant run 的Task
def instantRunTask = project.tasks.getByName("transformClassesWithInstantRunFor${variantName}")
//查找instant run是否存在且文件存在
if (instantRunTask && instantRunManifestOutputFile.exists()) {
println ' Instant run is enabled and the manifest is exist.'
if (!manifestOutputFile.exists()) {
//因?yàn)檫@里只是為了讀取activity,所以無論用哪個(gè)manifest差別不大
//正常情況下不建議用instant run的manifest,除非正常的manifest不存在
//只有當(dāng)正常的manifest不存在時(shí),才會(huì)去使用instant run產(chǎn)生的manifest
result = instantRunManifestOutputFile
}
}
} catch (ignored) {
// transformClassesWithInstantRunForXXX may not exists
}
//最后檢測文件是否存在,打印
if (!result.exists()) {
println ' AndroidManifest.xml not exist'
}
//輸出路徑
println " AndroidManifest.xml 路徑:$result"
return result.absolutePath
}
return ""
}
接著我們對清單文件進(jìn)行解析
def manifest = new XmlSlurper().parse(filePath)
這里的filePath就是我們上面拿到的清單文件的路徑
然后我們就可以從manifest里取到我們想要的東西。
比如取到包名:
manifest.@package
比如取到所有的activity:
def activities = []
String pkg = manifest.@package
manifest.application.activity.each {
String name = it.'@android:name'
if (name.substring(0, 1) == '.') {
name = pkg + name
}
activities << name
}
再比如取到Application:
manifest.application.@name;
這樣我們就通過解析清單文件拿到了所有的Activity 以及包名。那么就可以在我們的transform里進(jìn)行過濾了。
事實(shí)上一開始我想到的是遞歸一個(gè)類,然后拿到最上面的非Object的父類,來判斷該類是否是Activity,所以下了下面的代碼。
private boolean isActivity(CtClass c){
CtClass sourceSuperClass = getSourceSuperClass(c)
if(sourceSuperClass != null && sourceSuperClass.getName().equals("android.app.Activity")){
return true
}
return false
}
/***
* 得到最根部的父類
*/
private CtClass getSourceSuperClass(CtClass c){
if(c != null) {
CtClass superClass = c.getSuperclass();
while (superClass != null) {
if (superClass.getName().equals("java.lang.Object")) {
break;
}
superClass = getSourceSuperClass(c);
}
return superClass;
}
return null;
}
但后來在一個(gè)開源項(xiàng)目里(replugin) 看到了讀取清單文件路徑的代碼,非常感謝replugin。