visible true

技術的なメモを書く

Gradleで相対パスでライブラリプロジェクトを参照する

パッと出てこなかったのでメモっとく

こんな構成の時、appからlibraryを参照したいとする。

myProject
  - app
targetLibrary
  - library

myProject/settings.gradle

include 'app', ':..:targetLibrary:library'

myProject/app/build.gradle

dependencies {
    compile project(':..:targetLibrary:library')

Gradle Plugin 1.1.0-rc1でJVMでテストを実行出来るようになったらしい

New Build System - Android Tools Project Siteの1.1.0-rc1のRelease Noteに以下の文が

Unit testing support. Unit testing code is run on the local JVM, against a special version of android.jar that is compatible with popular mocking frameworks (e.g. Mockito).

という事で早速試してみました。

セットアップ

Android Studioを1.1 beta 4にする

まずはAndroid Studioを1.1 Beta 4以上にします。Android Studioを1.1 Beta 4はcanary channelで配布されてます。

f:id:sys1yagi:20150206152430p:plain

gradle pluginを1.1.0-rc1にする

プロジェクト直下のbuild.gradle

dependencies {
  classpath 'com.android.tools.build:gradle:1.1.0-rc1'
}

dependenciesにtestCompileでjunit4を追加する

gradle plugin 1.1.0-rc1からdependenciesにtestCompileというキーワードが増えました。localのUnit Testで使うライブラリを定義できます。

dependencies {
  testCompile 'junit:junit:4.11'
  testCompile "org.mockito:mockito-core:1.9.5"
}

Enable Unit Testing Supportを有効にする

Android Studioの[Preference]-[Gradle]-[Experimental]でEnable Unit Testing supportを有効にします。この設定の後gradle syncを行うかAndroid Studioを再起動して下さい。

f:id:sys1yagi:20150206152501p:plain

テストを書く

テストはsrc/test/java配下に置きます。ディレクトリを作ってもそのままではコードを認識してくれません。

f:id:sys1yagi:20150206153657p:plain

Build VariantsTest ArtifactUnit Testsに変更すると各種dependenciesのパスが通った状態でコードを記述出来るようになります。

f:id:sys1yagi:20150206153714p:plain

テストコードを書きます。

ListUtils.java

public class ListUtils {
  public static <T> T head(List<T> list) {
    if (list == null || list.isEmpty()) {
      return null;
    }
    return list.get(0);
  }
}

普通のJUnit4です。

import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

public class ListUtilsTest {
  @Test
  public void head() {
    List<String> strings = new ArrayList<>();

    assertThat(ListUtils.head(strings), nullValue());

    strings.add("a");
    assertThat(ListUtils.head(strings), is("a"));

    strings.add("b");
    assertThat(ListUtils.head(strings), is("a"));
  }
}

Gradleで実行する

まずはGradleで実行してみます。gradle pluginを1.1.0-rc1からtest, testDebug, testReleaseなどのタスクが増えました。

./gradlew testDebug

module_name/build/reports/tests/index.htmlにレポートが吐かれます。

f:id:sys1yagi:20150206153733p:plain

Android Studioで実行する

Android StudioではEdit ConfigurationsでJUnitのConfigurationを定義します。デフォルトとの変更点は下部のBefore launchです。Makeになっているのでそれを削除しGradle-awareを選択します。そこでtestDebug等のタスクを指定します。

f:id:sys1yagi:20150206153751p:plain

あとは実行するだけ。エラーになるとreportsのindex.htmlを見に行かないとダメです。まだこの辺はこなれてない感じですね。

f:id:sys1yagi:20150206153815p:plain

まとめ

とりあえずテストをlocalで実行する所まで書きました。今回追加されたUnit Testの機能はAndroidTestCompile共存するのでdeviceが必要なテストと不要なテストを分けて管理出来るようになりました。

Unit Testの方はmockable-android.jarというjarを生成していました。これをテストで参照している様です。中身をザッとみたら実装はなくどれもRuntimeExceptionをthrowしていました。mockableという事でUnit TestではAndroid Frameworkのクラスをmockitoなどでmockして差し替えましょうという事みたいです。

TextUtilsとかもmock差し替えが必要みたいなのでRobolectricとの置き換えはまだ先になりそうです(そもそも置き換えられるかもちょっと不明)。Shadow的な仕組みが今後追加されるんでしょうか?気になる所です。

参考

BintrayにAndroidのライブラリをアップする

Maven Centralは拝承しないといけなくて面倒だ、Bintrayは何か適当にアップ出来るらしいという事でBintrayに飛びついたのですがリポジトリにライブラリをアップする手順が難解で挫折し続けていました。途方に暮れていた所社内で以下のBintrayのページとリポジトリを教えてもらいました。

gradle-aar-example

bintray/bintray-examples · GitHub

中の人が色んなパターンのファイルをBintrayにアップする方法を書いたサンプル集です。サイコー。gradle-aar-exampleってのもあってホントにサイコーです。

AndroidのライブラリをBintrayにアップする

bintray-examples/gradle-aar-example at master · bintray/bintray-examples · GitHubを読めば分かるんですが一応手順をメモっときます。

Bintrayのアカウントを作る

とりあえずBintrayのアカウントを作りましょう。フリーでは500MBのスペースを得られます。

f:id:sys1yagi:20150205234949p:plain https://bintray.com/

API Keyを確認する

アカウントを取ったらBintrayのmaven repositoryにファイルをアップロードする時に必要なAPI Keyを確認します。

自分のプロフィール画面を開き、Editから編集画面へ行きます。

f:id:sys1yagi:20150205235241p:plain

編集画面にAPI Keyという項目があります。

f:id:sys1yagi:20150205235250p:plain

gradle.propertiesに設定を書く

あとはGradle用の設定を書くだけです。Bintrayにファイルをアップロードする為に必要なuser名とAPI Keyをgradle.propertiesに記述します。この時gradle.propertiesは.gitignore等に含めてVCSにはcommitしないようにしましょう。(local.propertiesだとbuild.gradleから見えなかったので今回はgradle.propertiesに書きました。何か良い方法あれば教えて下さい。)

gradle.properties

bintray_user=USER_NAME
bintray_api_key=API_KEY

build.gradleに設定を書く

長いですがそんなに複雑でもないです。groupIdやversion、siteUrl等固有の値は変更しないといけないですが他はテンプレです。

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:1.0.0'
    classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
    classpath 'com.github.dcendents:android-maven-plugin:1.2'
  }
}
apply plugin: 'com.android.library'
android {
  //省略
}

apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'

group = 'com.sys1yagi.android'
version = '1.0.0'
def siteUrl = 'https://github.com/sys1yagi/toaster'
def gitUrl = 'https://github.com/sys1yagi/toaster.git'

bintray {
  user = bintray_user
  key = bintray_api_key

  configurations = ['archives'] //When uploading configuration files
  pkg {
    repo = 'maven'
    name = 'toaster'
    desc = 'Android Toaster! You can use toast anywhere.'
    websiteUrl = siteUrl
    issueTrackerUrl = 'https://github.com/sys1yagi/toaster/issues'
    vcsUrl = gitUrl
    licenses = ['Apache-2.0']
    labels = ['android']
    publicDownloadNumbers = true
  }
}

install {
  repositories.mavenInstaller {
    pom {
      project {
        packaging 'aar'
        name 'toaster'
        url siteUrl
        licenses {
          license {
            name 'The Apache Software License, Version 2.0'
            url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
          }
        }
        developers {
          developer {
            id 'sys1yagi'
            name 'Toshihiro Yagi'
            email 'sylc.yagi@gmail.com'
          }
        }
        scm {
          connection 'https://github.com/sys1yagi/toaster.git'
          developerConnection 'https://github.com/sys1yagi/toaster.git'
          url siteUrl
        }
      }
    }
  }
}

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
}

task sourcesJar(type: Jar) {
  from android.sourceSets.main.java.srcDirs
  classifier = 'sources'
}

task javadoc(type: Javadoc) {
  source = android.sourceSets.main.java.srcDirs
  classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}

task javadocJar(type: Jar, dependsOn: javadoc) {
  classifier = 'javadoc'
  from javadoc.destinationDir
}
artifacts {
  archives javadocJar
  archives sourcesJar
}

task findConventions << {
  println project.getConvention()
}

アップロードする

taskにbintrayUploadが増えます。実行するとaarがBintrayのmaven repositoryにアップロードされます。

f:id:sys1yagi:20150206000737p:plain

jcenterに登録する

↑のページの右下からjcenterへの登録を申請するフォームへ飛べます。申請が通ればbuildscriptの所でjcenter()って書いてるプロジェクトならフツーにdependenciesでライブラリを追加出来るようになります。

f:id:sys1yagi:20150206000857p:plain

試しにアップしたもの

toasterというどうしようもないライブラリをアップしてみました。jcenterへのpublishは依頼中なのでまだbintrayのmaven repositoryを参照しないと使えません。今後はもっと有益なライブラリをアップできるよう善処します。

toaster

gradle-tab-completion.bashをMacに導入する際に必要な修正と、初回実行時の留意点

Android開発を爆速にする10のコマンドラインスクリプト - クックパッド開発者ブログ

われらが@tomorrowkeyが書いてくれた↑で紹介されている、Gradleのタスクをcompletionしてくれるgradle-tab-completion.bashなんですが2014-12/21現在でMac(10.9.5 Mavericks)に導入するにあたってちょっと修正が必要なのでその点について書きます。あと初回実行時に留意するべきポイントがあるので解説します。

Macに導入する場合に必要な修正

gradle-tab-completion.bashは以下のgistで公開されてます。

Gradle tab completion for Bash. Works on both Mac and Linux.

導入方法とかは↑に書いてあるので省きます。現在公開されているgradle-tab-completion.bashには誤りがあってMacでは正常に動きません。すでにgistに誤りについてのコメントがあるのでいずれ直ってるかもしれません。

必要な修正は21行目です。余計なスペースがあるんですね。

before

21:   if [[ -x `which md5 2 > /dev/null` ]]; then # mac

after

21:   if [[ -x `which md5 2> /dev/null` ]]; then # mac

この修正をすれば正常にMacで動作します。

初回実行時の留意点

gradle-tab-completion.bashの中身を読んでみると、33行目で一度gradleのtasksを実行してそれらをgrepしつつawkなどをかまして対象のbuild.gradleのタスク一覧を取り出しています。

33: commands=$($gradle_cmd --no-color --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')

その後この一覧をキャッシュして使いまわします。その為初回のcompletionは./gradlew tasksの実行が完了するまで待たされます。 プロジェクトによっては./gradlew tasksがすごく時間がかかったりする場合もあるのでcompletionがフリーズした印象を受けたりします。というか僕はそれではまりました(./gradlew tasksが1分かかった。待ちきれず中断しまくった)。

また、build.gradleのmd5ハッシュ値を見て更新されたかどうかを判定しているので、build.gradleを更新したあとの最初のcompletionも時間がかかると思います。

初回、更新後は気長に待ちましょう。

まとめ

gradle-tab-completion.bashベンリサイコー

f:id:sys1yagi:20141221184805p:plain

ビルド時にjava.lang.OutOfMemoryError: GC overhead limit exceededで死ぬときはdexOptionsにjavaMaxHeapSizeをセットすると良い

タイトルそのまんま。gradleでビルドしていて以下のエラーに出会ったら、

java.lang.OutOfMemoryError: GC overhead limit exceeded

build.gradleのdexOptionsjavaMaxHeapSizeを指定すれば大体直る。

android {
  dexOptions {
    javaMaxHeapSize "2g"
  }
}

この問題が出る兆候に「ビルドがめちゃくちゃ長い」というのがある。スワップしまくってるんだろうか。とりあえず2gくらいに設定しておけば大体いけると思う。