新版遷移過程中修復(fù)Classpath問題
Freeline踩坑日記
追查表象的蛛絲馬跡
結(jié)果前一部分的修改
基本上做到了編譯的通過
然后在增量的時(shí)候 報(bào)出了錯(cuò)誤
類似這樣子的錯(cuò)誤
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java:3: error: package android.annotation does not exist
import android.annotation.SuppressLint;
^
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java:4: error: package android.content does not exist
import android.content.Context;
^
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java:5: error: package android.content does not exist
import android.content.Intent;
^
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java:6: error: package android.os does not exist
import android.os.Build;
^
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java:7: error: package android.support.annotation does not exist
import android.support.annotation.RequiresApi;
^
...
很顯然是classpath的缺失
于是查閱Freeline的log , 也分析了Freeline的增量構(gòu)建流程
這里幾段Python代碼是運(yùn)行了增量javac
javacargs = self._generate_java_compile_args(extra_javac_args_enabled=extra_javac_args_enabled)
self.debug('javac exec: ' + ' '.join(javacargs))
output, err, code = cexec(javacargs, callback=None)
于是追查javac的命令 查看它的傳入的classpath
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/javac
-encoding UTF-8 -g -target 1.7 -source 1.7
-cp
/Users/jichenyang/AndroidStudioProjects/ONE/app/build/freeline/app/classes:
/Users/jichenyang/AndroidStudioProjects/ONE/app/build/intermediates/classes/debug:
/Users/jichenyang/android-sdk-mac_x86/platforms/android-25/android.jar
/Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java
/Users/jichenyang/AndroidStudioProjects/ONE/app/build/freeline/app/backup/com/liuzh/one/R.java -d /Users/jichenyang/AndroidStudioProjects/ONE/app/build/freeline/app/classes
通過觀察這幾個(gè)目錄可以發(fā)現(xiàn)的是 這些classpath并不能滿足需要
缺少了一些第三方庫(kù)的依賴
根據(jù):“控制變量法” 我切換到了穩(wěn)定版的Gradle Plugin跑了一遍 查看它的log
PS:大家看長(zhǎng)度就可以~
javac exec: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/javac
-encoding UTF-8 -g -target 1.7 -source 1.7 -cp
/Users/jichenyang/AndroidStudioProjects/ONE/app/build/freeline/app/classes:
/Users/jichenyang/AndroidStudioProjects/ONE/app/build/intermediates/classes/debug:
/Users/jichenyang/android-sdk-mac_x86/platforms/android-25/android.jar:
/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.6.1/b9d63507329a7178e026fc334f87587ee5070ac5/gson-2.6.1.jar:
/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.2.0/41e67dba73c3347e4503761642c39d0e06ca1f2/retrofit-2.2.0.jar:
/Users/jichenyang/.android/build-cache/edb8a7ac2177cd2946b58128ce36f08bc9b38f89/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/1533df315d42d7f4ff00b9f8b7de5c201256284e/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/6aefc3619c39e6e4ce3118e695f213d148de053e/output/jars/classes.jar:/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.picasso/picasso/2.5.2/7446d06ec8d4f7ffcc53f1da37c95f200dcb9387/picasso-2.5.2.jar:/Users/jichenyang/.android/build-cache/dffbaa523b9008a1896eec5b373120bb1c6de17c/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/4aaff6c08351f15c642176ed10e6377844d7046c/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/b20f53c3c1cdc7f0f1b4d0dbb35408b94ce38903/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/ba30abeac3972b144fefa6124554f582fa60df5c/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/d4cdfb05df67f451bb9aba52161bc413bc43c9ea/output/jars/classes.jar:/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.11.0/840897fcd7223a8143f1d9b6f69714e7be34fd50/okio-1.11.0.jar:/Users/jichenyang/.android/build-cache/3936d4bae4a86548d80c6de52bbd92ee7a284855/output/jars/classes.jar:/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.6.0/69edde9fc4b01c9fd51d25b83428837478c27254/okhttp-3.6.0.jar:/Users/jichenyang/.android/build-cache/f159250e4dfb4e3d4150d31319640f34f7ff3388/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/64331bf8b484ce0281624b5b84622ed7a08fbba2/output/jars/classes.jar:/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.0.2/f8d87f15b94b8d74e7ccf61d7eedb558811cdb30/converter-gson-2.0.2.jar:/Users/jichenyang/.android/build-cache/11cf9470b93863a1118d758231db813ac8dbc99c/output/jars/classes.jar:/Users/jichenyang/.android/build-cache/28277db1bdc4921dded9cbd9bc2991b9198a9093/output/jars/classes.jar:/Users/jichenyang/android-sdk-mac_x86/extras/android/m2repository/com/android/support/support-annotations/25.3.1/support-annotations-25.3.1.jar:/Users/jichenyang/.android/build-cache/837dc24219f090432e4c44c4b5da23f93591bb19/output/jars/classes.jar: /Users/jichenyang/AndroidStudioProjects/ONE/app/src/main/java/com/liuzh/one/activity/MovieActivity.java -d /Users/jichenyang/AndroidStudioProjects/ONE/app/build/freeline/app/classes
相比之下大概是什么呢:
正常情況下 應(yīng)該還囊括到Gradle編譯過去中釋放aar產(chǎn)生的jar包依賴 加入classpath
然而在AS3.0中 這部分依賴并沒有被加入進(jìn)來
Freeline的源碼中有一段注釋可以很清楚的表面Classpath的來源
def fill_classpaths(self):
# classpaths:
# 1. patch classes
# 2. dependent modules' patch classes
# 3. android.jar
# 4. third party jars
# 5. generated classes in build directory
...
發(fā)現(xiàn)表象查源頭
問:為什么這部分Classpath丟失了
查看Freeline增量編譯Javac部分Classpath添加的方法
def fill_classpaths(self):
# classpaths:
# 1. patch classes
# 2. dependent modules' patch classes
# 3. android.jar
# 4. third party jars
# 5. generated classes in build directory
patch_classes_cache_dir = self._finder.get_patch_classes_cache_dir()
self._classpaths.append(patch_classes_cache_dir)
self._classpaths.append(self._finder.get_dst_classes_dir())
for module in self._module_info['local_module_dep']:
finder = GradleDirectoryFinder(module, self._module_dir_map[module], self._cache_dir)
self._classpaths.append(finder.get_patch_classes_cache_dir())
# add main module classes dir to classpath to generate databinding files
main_module_name = self._config['main_project_name']
if self._name != main_module_name and self._is_databinding_enabled:
finder = GradleDirectoryFinder(main_module_name, self._module_dir_map[main_module_name], self._cache_dir,
config=self._config)
self._classpaths.append(finder.get_dst_classes_dir())
self._classpaths.append(os.path.join(self._config['compile_sdk_directory'], 'android.jar'))
#重點(diǎn)來了 (下面)
self._classpaths.extend(self._module_info['dep_jar_path'])
# remove existing same-name class in build directory
srcdirs = self._config['project_source_sets'][self._name]['main_src_directory']
...
注釋寫的很清晰 顧名思義
可以用Debug的方法 一句一句的跑 然后在Debugger里面看Classpath的變量值
self._classpaths.extend(self._module_info['dep_jar_path'])
這句話就是添加第三方依賴的操作
然后這條線就到了下一個(gè)工序 —> self._module_info['dep_jar_path']
是哪里來的?
通過調(diào)試+全局搜索的騷操作
定位在了
def get_project_info(config):
Logger.debug("collecting project info, please wait a while...")
project_info = {}
if 'modules' in config:
modules = config['modules']
else:
modules = get_all_modules(os.getcwd())
#從這個(gè)json文件中取出來了依賴路徑
jar_dependencies_path = os.path.join(config['build_cache_dir'], 'jar_dependencies.json')
jar_dependencies = []
if os.path.exists(jar_dependencies_path):
jar_dependencies = load_json_cache(jar_dependencies_path)
for module in modules:
if module['name'] in config['project_source_sets']:
module_info = {}
module_info['name'] = module['name']
module_info['path'] = module['path']
module_info['relative_dir'] = module['path']
#把他們放進(jìn)去
module_info['dep_jar_path'] = jar_dependencies
module_info['packagename'] = get_package_name(
config['project_source_sets'][module['name']]['main_manifest_path'])
看上面代碼的注釋部分 就可以知道 這些依賴是從一個(gè)json里面取出來的
于是我們?nèi)シ@個(gè)json文件
[
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.6.1/b9d63507329a7178e026fc334f87587ee5070ac5/gson-2.6.1.jar",
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.2.0/41e67dba73c3347e4503761642c39d0e06ca1f2/retrofit-2.2.0.jar",
"/Users/jichenyang/.android/build-cache/edb8a7ac2177cd2946b58128ce36f08bc9b38f89/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/1533df315d42d7f4ff00b9f8b7de5c201256284e/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/6aefc3619c39e6e4ce3118e695f213d148de053e/output/jars/classes.jar",
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.picasso/picasso/2.5.2/7446d06ec8d4f7ffcc53f1da37c95f200dcb9387/picasso-2.5.2.jar",
"/Users/jichenyang/.android/build-cache/dffbaa523b9008a1896eec5b373120bb1c6de17c/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/4aaff6c08351f15c642176ed10e6377844d7046c/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/b20f53c3c1cdc7f0f1b4d0dbb35408b94ce38903/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/ba30abeac3972b144fefa6124554f582fa60df5c/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/d4cdfb05df67f451bb9aba52161bc413bc43c9ea/output/jars/classes.jar",
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.11.0/840897fcd7223a8143f1d9b6f69714e7be34fd50/okio-1.11.0.jar",
"/Users/jichenyang/.android/build-cache/3936d4bae4a86548d80c6de52bbd92ee7a284855/output/jars/classes.jar",
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.6.0/69edde9fc4b01c9fd51d25b83428837478c27254/okhttp-3.6.0.jar",
"/Users/jichenyang/.android/build-cache/f159250e4dfb4e3d4150d31319640f34f7ff3388/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/64331bf8b484ce0281624b5b84622ed7a08fbba2/output/jars/classes.jar",
"/Users/jichenyang/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.0.2/f8d87f15b94b8d74e7ccf61d7eedb558811cdb30/converter-gson-2.0.2.jar",
"/Users/jichenyang/.android/build-cache/11cf9470b93863a1118d758231db813ac8dbc99c/output/jars/classes.jar",
"/Users/jichenyang/.android/build-cache/28277db1bdc4921dded9cbd9bc2991b9198a9093/output/jars/classes.jar",
"/Users/jichenyang/android-sdk-mac_x86/extras/android/m2repository/com/android/support/support-annotations/25.3.1/support-annotations-25.3.1.jar",
"/Users/jichenyang/.android/build-cache/837dc24219f090432e4c44c4b5da23f93591bb19/output/jars/classes.jar",
""
]
很顯然了已經(jīng) (對(duì)比之下 3.0的環(huán)境下 這個(gè)json的數(shù)組是空的)
測(cè)試的時(shí)候一個(gè)比較hack的操作是 我把2.3.3環(huán)境下跑出的json文件內(nèi)容 復(fù)制到3.0環(huán)境下
然后增量javac成功
這也就證明了javac環(huán)節(jié)的問題 同時(shí)消去了我的一個(gè)擔(dān)憂:是不是Dexmerge的時(shí)候會(huì)爛
窮追不舍 修復(fù)問題
那么
為什么這個(gè)json文件會(huì)是空的瓮床?
為什么2.3的時(shí)候這個(gè)json文件可以被賦值炒事?
這個(gè)json文件的數(shù)據(jù)是在什么時(shí)候被存入的宵凌?
帶著問題 我們繼續(xù)追查源碼
一招騷操作 我在Freeline源碼里面全局搜索
Command + shift + F
就搜它: "jar_dependencies.json"
果然 它還在Freeline的Gradle Plugin里面出現(xiàn)了
Freeline的取出jar的邏輯是:
把處理jar的Task取出來
然后遍歷他的inputs棒妨,取出那些jar包的地址 放在json里面
String manifest_path = project.android.sourceSets.main.manifest.srcFile.path
if (getMinSdkVersion(variant.mergedFlavor, manifest_path) < 21 && multiDexEnabled) {
classesProcessTask = project.tasks.findByName("transformClassesWithJarMergingFor${variant.name.capitalize()}")
multiDexListTask = project.tasks.findByName("transformClassesWithMultidexlistFor${variant.name.capitalize()}")
} else {
classesProcessTask = project.tasks.findByName("transformClassesWithDexFor${variant.name.capitalize()}")
}
project.task(hackClassesBeforeDex) << {
def jarDependencies = []
//就是這里偷出jar依賴地址
classesProcessTask.inputs.files.files.each { f ->
if (f.isDirectory()) {
f.eachFileRecurse(FileType.FILES) { file ->
backUpClass(backupMap, file as File, backUpDirPath as String, modules.values())
FreelineInjector.inject(excludeHackClasses, file as File, modules.values())
if (file.path.endsWith(".jar")) {
jarDependencies.add(file.path)
}
}
} else {
backUpClass(backupMap, f as File, backUpDirPath as String, modules.values())
FreelineInjector.inject(excludeHackClasses, f as File, modules.values())
if (f.path.endsWith(".jar")) {
jarDependencies.add(f.path)
}
}
}
if (preDexTask == null) {
def providedConf = project.configurations.findByName("provided")
// providedConf.setCanBeResolved(true) //適配3.0 但是這里不行
if (providedConf) {
def providedJars = providedConf.asPath.split(File.pathSeparator)
jarDependencies.addAll(providedJars)
}
jarDependencies.addAll(addtionalJars)
// add all additional jars to final jar dependencies
def json = new JsonBuilder(jarDependencies).toPrettyString()
project.logger.info(json)
FreelineUtils.saveJson(json, FreelineUtils.joinPath(FreelineUtils.getBuildCacheDir(project.buildDir.absolutePath), "jar_dependencies.json"), true);
}
}
這是它之前的代碼
那為什么無法兼容新版3.0呢宴偿?
因?yàn)?code>"transformClassesWithJarMergingFor${variant.name.capitalize()}"這個(gè)task在3.0上...
不存在了...
不存在了...

顯然Android studio改了編譯流程的task
不過它也不能跳過這一步 肯定有替代品 ...
果然
classesProcessTask = project.tasks.findByName("transformClassesWithDexBuilderFor${variant.name.capitalize()}")
在Plugin中增加對(duì)3.0的判斷 然后替換成這個(gè)task就可以了
很幸運(yùn)的是...這個(gè)task的相關(guān)從inputs里面取jar相關(guān)的api都沒有變...
沒有給我太大的折磨
改完相關(guān)代碼 install到MavenLocal
跑一下增量 OK昭齐!
如果有問題的話....那估計(jì)是3.0自帶的問題
坑還是要慢慢踩