Gradleを使ったWin/Mac対応のapkインストールツール
旧ブログ(blog.ogaclejapan.com)から移行してきた古い記事です。 移行に伴い、一部のレイアウトが崩れている可能性もありますmm
Androidアプリ開発de継続的インテグレーション をプロジェクトに導入してみたら、社内無線LANに繋げるテスト端末よりも個人端末など直接繋げない端末のほうが多くて非常に困った(´・ω・`)
「AndroidSDKぐらい全員入れろよ!」…とは言えないので、 セットアップ不要のPC経由でapkを半自動でインストールするツールをGradleで作成してみた。 一応、開発者以外にもチームに関わる色々な人が導入できるよう最低限の機能と操作にしたつもり。
このツールで可能なことは以下の3つ
-
showタスク 特定のWebDAVディレクトリに存在するapk一覧を表示する →不要なファイルが見えても困るので、apkのみを表示するようにした
-
downloadタスク 特定のWebDAVディレクトリに存在するapkをダウンロードする →デフォルト(apkファイル指定なし)だと一番使うであろう開発最新版がダウンロードされるようにした
-
installタスク 特定のWebDAVディレクトリに存在するapkをPCに接続された端末にadb経由で直接インストールする →デフォルト(apkファイル指定なし)だと一番使うであろう開発最新版がインストールされるようにした →ローカルにapkが存在しない場合は自動でダウンロードされるようにした
記事上、WebDAVは以下の仕様であることを前提とする
- apkが格納されたWebDAVのURLは
http://192.168.0.1/apks/
とする - apkのファイル形式は
sample-(env)-(version).apk
でUPされているものとする - プロジェクト環境はdebug,staging,releaseの3つあるものとする
- 最新開発中のものは
SNAPSHOT
というversionを意味するものとする
最終的なツールの構成はこんな感じ↓
.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── tool
├── forMac
│ └── adb
└── forWindows
├── AdbWinApi.dll
├── AdbWinUsbApi.dll
└── adb.exe
tool配下はMac/WinのAndroidSDKが入った端末からadbに必要なファイルを事前に抜き出したものをセットし、 GradleWrapperは以前書いた「 Gradle環境要らずのGradleチームビルド 」のやり方で生成する
最終的なbuild.gradleはこんな感じ↓
buildscript {
repositories {
mavenCentral()
maven {
url 'http://sardine.googlecode.com/svn/maven/'
}
}
dependencies {
classpath 'com.googlecode.sardine:sardine:314'
}
}
project.ext {
baseUrl = 'http://192.168.0.1/apks/'
sardine = SardineFactory.begin();
defaultApk = 'sample-debug-SNAPSHOT.apk'
}
import com.googlecode.sardine.SardineFactory
import org.apache.tools.ant.taskdefs.condition.Os
task wrapper(type: Wrapper) {
gradleVersion = '1.2'
}
//特定のWebDAVディレクトリに存在するapk一覧を表示する
task show(description: 'gradle[w] show') << {
showApks()
}
//特定のWebDAVディレクトリに存在するapkをダウンロードする
task download(description: 'gradle[w] download -[Ptarget=(filename)]') << {
def apk = project.defaultApk
if (project.hasProperty('target')) {
apk = target
}
downloadApk(apk)
}
//特定のWebDAVディレクトリに存在するapkをPCに接続された端末にadb経由で直接インストールする
task install(description: 'gradle[w] install [-Ptarget=(filename)] -Pforce=true') << {
def apk = project.defaultApk
if (project.hasProperty('target')) {
apk = target
}
def isForce = false
if (project.hasProperty('force')) {
isForce = force
}
installApk(apk, isForce)
}
boolean isApk(mime) {
mime.equals('application/vnd.android.package-archive')
}
void showApks() {
def dav = project.sardine
dav.list(project.baseUrl).collect {
if (isApk(it.contentType)) {
println "[${it.modified}] ${it.name}"
}
}
}
void downloadApk(name) {
downloadApk(name, true)
}
void downloadApk(name, overwrite) {
def out = new File(projectDir, name)
if (out.exists()) {
if (!overwrite) return
out.delete()
}
def apk = "${project.baseUrl}${name}"
def dav = project.sardine
if (!dav.exists(apk)) {
logger.error("file not found. ${apk}")
throw new StopActionException()
}
logger.lifecycle("download.. ${apk}")
out.withOutputStream { stream ->
dav.get(apk).eachByte { b ->
stream.write(b as int)
}
}
}
void installApk(name, isForce) {
downloadApk(name, isForce)
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
installApkForWindows(name, isForce)
return
}
if (Os.isFamily(Os.FAMILY_MAC)) {
installApkForMac(name, isForce)
return
}
logger.error("not supported os. must be windows or mac")
throw new StopActionException()
}
void installApkForWindows(name, isForce) {
adbInstall("${projectDir}\\tool\\forWindows\\adb", name, isForce)
}
void installApkForMac(name, isForce) {
adbInstall("${projectDir}/tool/forMac/adb", name, isForce)
}
void adbInstall(adb, apk, isForce) {
logger.lifecycle("adb install ${apk}")
def stdout = new StringBuffer()
def stderr = new StringBuffer()
def cmd = "${adb} install"
if (isForce) {
cmd = "${cmd} -r" //reinstall option
}
def proc = "${cmd} ${apk}".execute()
proc.consumeProcessOutput(stdout, stderr)
proc.waitForOrKill(1000 * 60) //wait for 1min
if (stdout.length() > 0) {
logger.lifecycle(stdout.toString())
}
if (stderr.length() > 0) {
logger.error(stderr.toString())
}
}
ちなみに今回はWebDAVクライアントにserdineを使ってみた。 Antタスクとしても提供しているので、簡潔で良い感じ。