コマンドラインからのビルドは assenbleDebug
とか installDebug
で行うことができる。これらはAndroid Pluginの標準タスクである。ただ、インストールまではできるがapk起動はしないし、接続端末の選択などはできないなど実用とまではいかない。
コマンドラインからビルド・対象端末選択・インストール・アプリ起動ができるタスクを追加してみた。
準備
install peco
端末を選択するために peco
を使うのでインストール。peco は標準出力をインクリメンタルサーチでき、選択することができる便利なツールだ。
$ brew install peco
※ pecoを使った非常に便利なadbツールがあります。 (adb-pect
)
実装
対象モジュールの build.gradle
にタスクを追加する。 (android studioで作ったプロジェクトなら app/build.gradle
)
SDKパスを取得する
adb
や aapt
など使うのでAndroid SDKのパスを取得するメソッドを用意する
def getSdkPath = {
def sdkDir
def localProperties = new File(rootDir, "local.properties")
if (localProperties.exists()) {
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
sdkDir = properties.getProperty('sdk.dir')
} else {
sdkDir = System.env.ANDROID_HOME;
}
return sdkDir;
}
接続デバイス名を取得
接続デバイスを取得するメソッド。peco
がなくてもデバイスは自動で選択されるようにする。
def getConnectedDevice = {
def sdkDir = getSdkPath();
def adb = sdkDir + "/platform-tools/adb"
Process peco = 'peco'.execute()
Process devices = "$adb devices".execute()
Process sed = ["sed", "-e", "1,1d"].execute()
def device = null;
// pecoがある場合は選択できるようにする
if ("which peco".execute().text.isEmpty()) {
def list = devices.pipeTo(sed).text;
list.eachLine { line, no ->
if (!line.isEmpty()) {
device = line.split()[0] // <device name> device
return true;
}
}
} else {
def select = devices.pipeTo(sed).pipeTo(peco).text
if (!select.isEmpty()) {
device = select.split()[0] // <device name> device
}
}
return device;
}
デバイスにapkをインストールするタスク
デバッグビルドの assembleDebug
を依存タスクとしておけば、このタスクの実行前にビルドが行われる。 選択された device
と 今回のビルドで生成されたapk場所 apkPath
は次のタスクでも使えるように拡張プロパティに設定しておく。
task installToDevice(dependsOn: 'assembleDebug') << {
android.applicationVariants.all { variant ->
if (!variant.install) { return }
// set command
def sdkDir = getSdkPath();
def adb = sdkDir + "/platform-tools/adb"
def apkPath;
variant.outputs.each { output ->
if (output.zipAlign.outputFile.path.endsWith('.apk')) {
apkPath = output.zipAlign.outputFile.path;
return true;
}
}
if (apkPath == null || apkPath.isEmpty()) { return; }
// set property
ext.device = getConnectedDevice();
ext.apkPath = apkPath;
if (device != null && !device.isEmpty()) {
println "$adb -s $device install -r $apkPath".execute().text
}
}
}
インストールしたapkを起動するタスク
apkを起動するにはパッケージ名と起動アクティビティが必要で、そのためにビルドapkをダンプして各情報を取得する。なお、LAUNCHER
を <activity-alias>
に設定しているなどapkによっては launchable-activity:
が取得できない場合がある。そういった場合を考慮して、下記コードの activityName
に初期値を設定しておく。
task startDebug(dependsOn: installToDevice) << {
android.applicationVariants.all { variant ->
if (!variant.install) { return }
if (ext.device == null || ext.device.isEmpty()) { return; }
if (ext.apkPath == null || ext.apkPath.isEmpty()) { return; }
def device = ext.device;
def apkPath = ext.apkPath;
// set command
def sdkDir = getSdkPath();
def aapt = sdkDir + "/build-tools/$project.android.buildToolsRevision/aapt"
def adb = sdkDir + "/platform-tools/adb"
// get apk info
def result = "$aapt dump badging $apkPath".execute().text
// package name
def packageLine = result.readLines().find { it.startsWith "package:" }
def packageName
packageLine.eachMatch(".*name='(.*?)'.*") { all, match ->
packageName = match
}
// launcher activity
def launchableActivityLine = result.readLines().find {
it.startsWith "launchable-activity:"
}
def activityName = "com.hoge.example.MainActivity" // TODO: 取れなかった時のために初期化
if (launchableActivityLine != null) {
launchableActivityLine.eachMatch(".*name='(.*?)'.*") { all, match ->
activityName = match
}
}
def intentAction = "android.intent.action.MAIN"
def intentCategory = "android.intent.category.LAUNCHER"
def activity = packageName + "/" + activityName
println "$adb -s $device shell am start -a $intentAction -c $intentCategory -n $activity".execute().text
}
}
使ってみる
./gradlew startDebug
で今回追加したタスクを実行。複数端末接続でも peco
により選択することが可能。
$ ./gradlew startDebug
:
:main:assembleDebug
:main:installToDevice
[ 0%] /data/local/tmp/main-debug.apk
[100%] /data/local/tmp/main-debug.apk
pkg: /data/local/tmp/main-debug.apk
Success
:main:startDebug
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] ....
BUILD SUCCESSFUL
おしまい
CIで自動スクリプトで全部やりたいとか、コマンドラインから操作したいといった時に使えると思います。個人的にはコマンドラインから色々出来たほうが好きなので、こういった対応は非常に好きです。
コードはgistにあります。コピペでも多分動くはずです。
Gradleを使って業務を楽にする の記事を参考させてもらいました。