症状
Android Studio 3.xに対応するため、com.android.tools.build:gradle:3.0.1
に変更したところ、RunConfigurationのApplicationから実行できていたpure Javaなアプリケーションの依存が解決できなくなった。
つまりNoClassDefFoundError
が出る。
要はあるModuleにこんなJava Application存在していた。
対処 (ベストプラクティスはこちら)
注意: これだとブレークポイントで止まれない。止まることができる方法は後述
このように、gradle run
で実行できるように設定する。
画像ではlibというモジュール名になってる。
:yourModuleName:run
そして、対象のmoduleのbuild.gradleに以下を追記。
apply plugin: 'application'
mainClassName = "your.main.class.myClass"
要はresourceが取得できなかった問題に似ている?
Android StudioでJava Libraryなモジュールからリソースを含んだ実行可能なJarを生成するまで
ブレークポイントでも止まる実行方法
注意: ブレークしてしばらくしたらgradleのtaskが死んでしまうのでダメか...
lib
というJavaなモジュールがあって、myClass.java
があって、Gsonを参照しているとする。
package com.example.lib;
import com.google.gson.GsonBuilder;
public class myClass {
public myClass() {
System.out.println(GsonBuilder.class);
System.out.println("hoge");
}
public static void main(String[] args) throws Exception {
new myClass();
}
}
build.gradle
にJavaExecなtaskを適当な名前で追加する。
ここでexecuteHote
とかexecutePiyo
とか名前を変えて、それぞれのmainを設定しておけば、mainを複数作りたいmoduleでも対応できると思う。
apply plugin: 'application'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.code.gson:gson:2.8.2'
}
mainClassName = "" //意味ないように見えるが、これがないとプロジェクト全体のbuildが通らない
task executeMyClass(type: JavaExec) {
main = "com.example.lib.myClass"
classpath = sourceSets.main.runtimeClasspath
}
Add New ConfigurationからRemoteを追加。Nameは適当に。
Search sources using module's classpath:
は<whole project>
でのままでもいいかもしれない
Before launchのところからRun Gradle task
を追加する。
Grade projectは実行したいprojectであり、今回はlib。
Talksには:lib:executeMyClass --debug-jvm
とする。(--debug-jvm
によってデバッガのアタッチを待ってくれる。)
まとめるとこんな感じになる。
実行
Run debugをするとBefore launch
に従ってexecuteMyClass
が実行される。
が、これはデバッガからのアタッチを待ってるので終わらない。
なのでStopする。要は画像の一番右のやつをクリック。
そしたら、ブレークポイントで止まる。
JavaExec
Gradle to execute Java class (without modifying build.gradle)
Stopしたのになんで実行されてんだ感がすごいし、Android Studio 2.xの時より面倒になったけど、とりあえず動くので良しとする。動かん。
そしてベストプラクティス
前章にあるように、build.gradle
にtask executeMyClass
を同じように作る。
Add New ConfigurationからRemoteを追加するが、Nameは適当にして、他は全てデフォルトでそのままOKする。
おもむろにshを実行する。
./gradlew :lib:executeMyClass --debug-jvm
すると、EXECUTINGで止まったままになる。
Android Studioでさっき追加したRemoteをdebugで実行すると...なんとアタッチできる。めでたし。
エラーはターミナルに表示されるので、落ちたクラスにクリックでジャンプはできない。
Android Studio 2.x時代と同じように実行できる方法を発見したので真のベストプラクティスはこちら
余談
Android Studio 2.xの時と比べるとすごい手間であるが、時代に取り残されないようにするためにはこうするしかないのか...?
そもそもAndroid StudioでJavaアプリケーションを動かすのが間違ってるのかもしれない。(と思ったけど、Androidなモジュールがないプロジェクトであれば、ApplicationのConfigurationから実行は可能なのが余計に謎)
Gradle plugin 3.xでimplementationとかapiとか増えたせいの仕様変更が原因な気がするけど実際問題よく分からん...