又掌握了一项新技能 - 断点调试 Gradle 插件
前言
最初开发Android应用程序的时候,肯定是在打log调试,然后慢慢地觉得打log效率太低下了,不能快速定位问题,于是走上了断点调试之路。Gradle插件也一样,从会写插件那一刻起到现在,一直用的是打log调试功能,但是同样的这种方式效率也太低下了,这之前,我也尝试过寻找断点调试的方式,但是一直没有成功,昨天偶然之间调通了,于是记录一发。
之前失败的方式
之前测试断点调试的功能的时候,一直在build.gradle中直接测代码,测试的代码也是在project.afterEvaluate之后,然后遍历android..applicationVariants,获取各个信息,就像这样子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
dependencies {
compile gradleApi()
compile localGroovy()
}
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import com.android.build.gradle.internal.variant.ApplicationVariantData
import com.android.build.gradle.internal.api.ApplicationVariantImpl
project.afterEvaluate {
if (project.plugins.hasPlugin("com.android.application")) {
def android = project.extensions.getByName("android")
android.applicationVariants.all {ApplicationVariantImpl variant ->
project.logger.error "DebuggerPlugin:${variant}"
ApplicationVariantData apkVariantData = variant.getApkVariantData()
ApplicationVariantData variantData = variant.getVariantData()
TestVariant testVariant = variant.getTestVariant()
UnitTestVariant unitTestVariant = variant.getUnitTestVariant()
}
}
}
|
于是这个断点我打了一年硬是没打住,恩,没错,这个东西我断断续续实验了一年也没有成功过,也有点无语,原因也不知道。昨日发现,必须得在外面包一层plugin才能打住断点,就像这样子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
dependencies {
compile gradleApi()
compile localGroovy()
}
apply plugin: DebuggerPlugin
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import com.android.build.gradle.internal.variant.ApplicationVariantData
import com.android.build.gradle.internal.api.ApplicationVariantImpl
class DebuggerPlugin implements Plugin<Project> {
void apply(Project project) {
project.afterEvaluate {
if (project.plugins.hasPlugin("com.android.application")) {
def android = project.extensions.getByName("android")
android.applicationVariants.all {ApplicationVariantImpl variant ->
project.logger.error "DebuggerPlugin:${variant}"
ApplicationVariantData apkVariantData = variant.getApkVariantData()
ApplicationVariantData variantData = variant.getVariantData()
TestVariant testVariant = variant.getTestVariant()
UnitTestVariant unitTestVariant = variant.getUnitTestVariant()
}
}
}
}
}
|
具体原因也找不到,理论上来讲,两者没有什么大的区别,除非不包plugin的代码编译后代码位置发生了变化,导致打不到断点。
其实这事也怪自己,如果一开始直接用插件项目来测,将其发布到本地maven,然后执行去打断点,估计老早就成功了,硬是在build.gradle中写零碎的代码来测试,往事不提也罢。
一个坑
以上代码执行过程会先出现一个错,如下:
1
|
Error:The closure '[email protected]' is not valid as an action for argument 'com.and[email protected]1103b69d'. It should accept no parameters, or one compatible with type 'com.android.build.gradle.internal.api.ApplicationVariantImpl_Decorated'. It accepts (com.android.build.gradle.internal.api.ApplicationVariantImpl).
|
这个错出现的时候,只需要将ApplicationVariantImpl variant改成def variant即可,然后继续运行,会出现另一个问题。
即出现了一个cannot cast object with class A to A的问题,如下
1
|
Error:Cannot cast object 'ApplicationVariantData{debug}' with class 'com.android.build.gradle.internal.variant.ApplicationVariantData' to class 'com.android.build.gradle.internal.variant.ApplicationVariantData'
|
可以看到虽然两个对象obj1和obj2的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被虚拟机认为是相同的,所以抛出了ClassCastException异常。
那么怎么解决呢,将项目中所有buildscript中的dependencies下引用的android gradle plugin 版本都改成同一个,即
1
2
3
4
5
6
7
8
|
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:${global_gradle_plugin_version}"
}
}
|
之后将~/.gradle/daemon/目录下内容全部删除,然后看看是否解决了,如果没有解决,则继续删除~/.gradle/daemon/,然后重启电脑。TM的如果还没好,那么请确定项目中所有引用的插件中的compile的android gradle plugin版本是否都一致,如果不一致,请保持一致,不然也有问题。
这问题就算完事了,大概可能和Gradle的守护进程、插件引用的android gradle plugin版本不一致有那么一点关系。
断点调试方式1
说完了以上坑,正式进入断点调试的环节,方式一很简单,直接利用gradle的参数让其等待我们的调试进程attach上去。比如我要执行gradle clean这个task,则加上两个额外参数即可
- 一个是开启debug
-
一个是不使用守护进程
具体例子如下:
1gradle :app:clean -Dorg.gradle.debug=true --no-daemon之后这个进程就会一直等待,直到我们attach我们的调试进程。如下图所示:
然后参考这篇文章Intellij-IDEA远程调试,利用Android Studio或者Intellij IDEA的remote debug进行调试,端口号填5005.
如图
然后运行remote
之后就会attach上去我们的进程
然后看看效果
从此可以愉快的断点调试了。
断点调试方式2
和方式一差不多,只不过不是用gradle的参数来开启debug,而是用环境变量
1
|
export GRADLE_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
|
之后就跟正常执行任务一样
1
|
gradle clean
|
剩下的操作和方式1一样。