前言
最初开发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
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
'DebuggerPlugin$_apply_closure1$_closure2@20825b3e' is not valid as an action
for argument
'com.android.build.gradle.internal.api.ApplicationVariantImpl_Decorated@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一样。