カテゴリー: 未分類

shields.ioを使ってバッジを表示する

http://shields.io/を使ってバッジを表示する。

今回はDart Packagesで公開されているパッケージのバージョンを表示するバッジを出したかった。
Dart Packagesはshields.ioでサポートされており、https://img.shields.io/pub/v/box2d.svgというような感じでパッケージのバージョンを表示することができる。
ちなみにbox2dの部分がパッケージ名なので、ここを自分が表示したいパッケージ名に書き換えればよい。

この方法で表示されるパッケージのバージョンは、安定版の最新バージョンが表示される。(安定版というのは1.0.0-rc1みたいにセマンティックバージョンの後ろに何もつかないやつのことを指している)

https://img.shields.io/pub/v/box2d.svg box2d

https://img.shields.io/pub/v/pointycastle.svg pointycastle

本題

ところでDartはバージョン2への移行時期であり、Flutterで利用する場合には開発版を利用する必要があったりする。
例えばredux_persistpointycastleなどがそう。
ではこれら開発版のバージョンのバッジを表示するためにはどうしたら良いのだろうというのが今回の本題。

今回はPointy Castleというライブラリの開発版バージョンを表示する。
これを書いている時点での安定版は0.11.1で開発版は1.0.0-rc1となっている。

Dart Packagesでは各パッケージのバージョン情報をjsonで取得することができる。
https://github.com/PointyCastle/pointycastleの最後に.jsonとすればよい。
shields.ioではJSONからクエリを使って直接バッジを作成することができる。https://shields.io/#dynamic-badge
クエリはjsonpathを使ったクエリが使える。
今回の場合使うクエリは$.versions[?(@=="1.0.0-rc1")]になる。

https://img.shields.io/badge/dynamic/json.svg?label=pub&url=https%3A%2F%2Fpub.dartlang.org%2Fpackages%2Fpointycastle.json&query=%24.versions%5B%3F(%40%3D%3D%221.0.0-rc1%22)%5D pub

ちなみにこの方法では動的に最新バージョンを取得することはできない。
(クエリをうまいこと扱えばできるのかもしれないが、私にはわからなかった)
例えばクエリを$.versions[?(@.includes("rc"))]とするとバージョンにrcを含むものだけが取得できる。
しかしrcを含むバージョンが複数ある場合、それら全てが列挙されてしまう。

$.versions[?(@.includes("+"))]をクエリに使った場合: +versions

Android Studio2.x時代のプロジェクトを3.0環境へ更新

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

ストアに公開しているアプリは2018年8月までにtargetSdkVersionを26以上にする必要がある。その関係で以前にリリースしたアプリを更新しているのだが、その際に最初のハードルとなるのが古いAndroid Studioで作ったプロジェクトを現在のバージョンに更新する作業である。

最近何回も同じことをやっているので、備忘録的に書いておく。

基本的には全部Migrate to Android Plugin for Gradle 3.0.0に書いてあるので、そのとおりにやるだけである。

書いている途中でAndroid Studio 3.1が正式版になった。が、3.0から3.1への更新はそんなに大きく変更しなくても済むので、基本は3.0へのマイグレーションガイドに書いてあることに対応すればよいはず。

Gradleのバージョン更新

まず始めにGradle(Gradle wrapper)のバージョン更新を行う。Android Studio3.0は最低でもGradle4.1以上が必要。(3.1はGradle4.4以上)

gradle wrapperの更新はgradle-wrapper.propertiesファイルを書き換えてgradle syncを行うだけ。

私は./gradlew wrapper --gradle-version=<Gradleのバージョン>コマンドで更新している。これで更新したらシェルスクリプトの更新もかかる場合があるので、こっちのほうがいいのかなという雰囲気で選んでいるだけだったりする。

コマンドで更新した場合、distributionUrlで指定されるgradle wrapperが4.x-bin.zipといった感じでバイナリのみのものになる。Android Studioはallにしとけと変更を促してきて、結局手書きでdistributionUrlを書き換えることになるので、普通にマイグレーションドキュメントにあるようにgradle-wrapper.propertiesを書き換えるだけでいいと思う。

ちなみに利用するgradleのバージョンだが、私の場合Twitterで見かけたGradleのリリースツイートを見て覚えてるバージョンを利用している。今だと4.6。

使えるバージョンはhttps://gradle.org/releases/で確認できる。基本的には最新使っとけばいいんじゃないだろうか。

build.gradleの更新

  • android gradle pluginのバージョンを更新
  • repositoryにgoogle()を追加する(先にGradle wrapperのバージョンを上げておく必要がある)
  • 利用しているgradle pluginのバージョン更新

プロジェクトで利用しているgradle pluginのバージョンが古い場合、原因がよくわからないエラーが多発する。例えばNo signature of method: com.android.build.gradle.internal.scope.VariantScopeImpl.getGenerateRClassTask() is applicable for argument types: () values: []とか。こういうのは利用しているgradle pluginを最新のバージョンに更新すると解消されることが多かった。

基本的にAndroid Studioが利用しているプラグインの新しいバージョンがあれば教えてくれるはず(網掛けになって新しいバージョンがあることを示唆してくれる)ので、それに従ってgradle pluginのバージョンを上げてみると解決する場合が多いだろう。

エラーメッセージで検索してもこれといった解決策が見つからないという場合には、利用しているプラグインのバージョンを上げることを試してみよう。

ライブラリプロジェクトを利用している場合

昔はライブラリプロジェクトを利用している場合、publishNonDefault trueを設定して、アプリケーションモジュールをdebugビルドするときは、ライブラリプロジェクトもdebugビルドにするなんて指定をしている人もいただろう。

この設定をしている場合、Android Studio3.xにアップデートするにあたってちょっとした手直しが必要になる。

Android Studio3.0からはライブラリモジュールのビルド設定は、利用するアプリケーションモジュールのものと同じものが利用されるようになった。appモジュールでdebugビルドを選んだら、自動的に依存しているライブラリモジュールもdebugビルドでビルドされる。

そのため以下の手直しが必要。

ライブラリモジュール

publishNonDefault trueの記述を削除する。

アプリケーションモジュール

例えば以下のように記述していたとする。

debugCompile project(path: ":library", configuration: "debug")
releaseCompile project(path: ":library", configuration: "release")

この場合、この2行は不要になるので削除。その後あらためてimplementation project(":library")とライブラリモジュールへの依存を記述する。

buildTypeを増やしている場合

追加でbuildTypeに手を加えている場合(デフォルトのdebugrelease以外に定義している場合)、増やしたbuildTypeの定義にmatchingFallbacksという行を追加してやる必要がある。

これはそのbuildTypeが使われるときに、それが存在しないライブラリプロジェクトなどがどのbuildTypeを使えばいいかを指定するものだ。

Android Wearアプリを利用しているプロジェクト

ライブラリモジュールと同様の問題と変更が行われている。publishNonDefault trueが要らなくなって、debugWearApp ...という記述群がwearApp(":wear")の1行で済むようになった。

プロダクトフレーバーを利用している場合

新しくflavorDimensionsを最低1つは定義しないといけなくなった。

productFlavorは何らかの基準で使い分けているはずなので、その基準を定義すれば良い。例えばhttps://github.com/gen0083/FilteredHatebuこのプロジェクトは、ネットワークのレスポンスという観点からmockprodというプロダクトフレーバーを定義している。

ネットワークの観点で切り分けているので、flavorDimensions "network"という感じで定義してやる。

後はproductFlavorの定義の部分で、どのdimensionに属する定義なのかを指定してやればいい。flavorDimensionsが1つしかないのであれば省略可能である。

私は最初勘違いしていたのだが、これは決して各フレーバーごとに異なるdimensionを割り当てなければならないということではない。このプロジェクトでは最初mocktestproddefaultというdimensionを割り当てたのだが、そうするとプロダクトフレーバーはmockProdDebugという形になってしまい、mockprodを切り替えられなくなってしまった。切り替えて使うものについては、同じdimensionを割り当てないといけない。

apt → annotationProcessorへの置き換え

これは確かmustだったような気がする。(気がするというのは、最近更新したプロジェクトの中にはaptを使っているものがなかったので)

古いプロジェクトだとgradle pluginを使ってアノテーションによるコード生成ライブラリを使っていたと思うが、その機能は公式に取り込まれているので、gradle pluginの削除とaptannotationProcessorに置き換えてやる。

やらなくても問題ない(けどやっといたほうがいいこと)

compile → implementationへの置き換え

dependenciesのcompile "com.android.support:appcompat-v7:xxx"のcompileの部分をimplementationに置き換える。

これは直さなくてもビルド自体は通るものの、Android Studio3.1からはwarningとして警告されるので、もうさっさと書き換えてしまったほうが良いだろう。

アプリケーションモジュールしかないプロジェクトであれば、全部implementationにしておけばいいはずである。基本的にimplementationにしておいて、それではうまくいかない場合にapiに変更するばよいと思う。apiが必要になってくるのは、ライブラリモジュールを使っている場合だろう。

ライブラリモジュールでのみcompileで依存性を組み込んでいて、利用側のアプリケーションモジュールではdependenciesに記述していないという状態があるのであればapiで指定しないとうまくいかないことがあるだろう。

Droidkaigi2018アプリにコントリビュートする

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

去年に引き続き、今年もDroidkaigiアプリにコントリビュートすることができた。

去年は1行書き換える程度の細かいPR中心だったが、今年は機能の追加なんかもできて個人的に成長が感じられてうれしい。

Droidkaigiアプリは後から他の人が出したPRを見て「こういうやり方があるのか」と知見を得たり、知らないやり方を知れたりとそれだけで勉強になる。しかし私にとっては、リアルタイムで参加することこそ意義がある。

なぜなら、普段ソロで勉強・開発をしているので、コードに対するフィードバックが得られる機会がないからだ。PR送ればそんな貴重なフィードバックがいただける可能性があるのである。このチャンスを逃す手はない。万年ソロのせいでGit力すらないのだが、PRを送ることがGit力を鍛えるチャンスにもなってよい。

今回は、私のように独学でAndroid勉強しているというような人に向けて、この貴重なチャンスを最大限活用しようぜというスタンスで記事を書いてみようと思う。

準備

コントリビュートする前段階、準備に関しては公式情報が詳しいので参考にされたし。

DroidKaigi 2018 アプリをGitHubで公開しました

Issueを立てる

コードを書いてPRを送るのはハードルが高い、という人はまずはissueを立てることで貢献するのがいい。アプリを手元で実行してみて、ここの挙動がおかしくないだろうかなど、気づいたことを書けば良い。

日本語OKと言われても、みんな英語でやりとりしている中自分だけ日本語で書くのもなぁなんて思うかもしれないが、そこで躊躇するのはもったいないと思う。

例えばIssueを立てるのも勉強になるのである。こういう挙動がおかしいという問題提起に対して、誰かがそれを解決するPRを送ってくる。そうすると、この問題の原因はここにあって、こう対処すれば良かったのかと学びになる。

他の人が立てたIssueでも同じように学びになるが、やはり自分で立てたIssueの方が身につくと思う。

Issueを立てるときやPRの説明などに、動画をつけるのが親切でいいと思う。

私はAndroid Studioを使って端末の動画を撮影(Logcatのタブのところに動画撮影ボタンがある)、撮影したMP4のファイルをこのサイトを使ってアニメーションGIFに変換し、それをGithubに添付している。

Issueの原因を探る

すでに立っているIssueに対して、なぜそのような問題が起こっているのかの原因を探る。

今年は探る前に「このIssueに取り組みます」と手を挙げてassignしてもらうといい。やってみて無理そうならその旨を伝えればいいということなので、気軽に挑戦するといいと思う。

とは言え手を挙げてやっぱり無理でしたというのは恥ずかしいというのも真理。まずは立っているIssueの原因探求をやってみるところから始めてもいいかもしれない。

おそらくではあるが、すでに立っているIssueに対して追加情報を書き加えるのも立派なコントリビュートであると思う(コントリビューターには名前が載らないだろうが)。

原因がわかれば、対策を自分でできそうならassignしてもらえばよいし、原因は分かったが対処法が思いつかないというのであれば、他の人のPRを待って学びを得る機会が手に入ったと思えば良い。どちらにしろよい勉強になる。

PRを送る

ちなみに私はIssueを立てることなくいくつかPRを送っている。私の場合、Issueを一つ立てるのも時間がかかるので(主に英語力のせい)、コード書いてPR送ったほうが早い場合が多いからである。

Issueに取り組もうとして別の問題に気づいてそこに対処していたらPRの形になった、というパターンが多いだけであるが。

正直コードを書いているより、動作を確認したりIssueやPRの説明を英語で書くのに四苦八苦している時間のほうが長い。

コードに関しては、こんなコードで大丈夫だろうかとか、微妙なコードでレビュアーに負担を与えはしまいかとか、明確にだめとは言えないけど良くもなくて中途半端だなとか思われないだろうかとか、いろんな不安でいっぱいである。

しかしだからといって、せっかく書いたコードを送らないのは成長のチャンスを棒に振るうようなもの。エイヤとPR送るようにしている。先にも書いたとおり、私はずっとボッチ開発をしてきているので、誰かと一緒に作業していくという機会自体がめちゃめちゃ貴重なのである。

正直なところ、コードを書いてPR送ることこそ何よりも勉強になるので、特に私と同じような一人で開発しているとか勉強しているという人ほど、こういう機会を利用しない手はない。

PR送る上で気をつけていること

大したことではないのだけど、コミットする前にReformat codeを実行するようにするのは気をつけている。Macだとcmd + option + Lで実行されるやつ。

DroidkaigiアプリではcodeStyleが共有されているので、これを実行しておけばPRを送った後でスペースが足りないとかの機械的な指摘がぐっと減るので、PR画面が見やすくなると思う。

手作業でやると絶対忘れるので(去年の私はそうだった)、Android Studioでコミットする画面のBefore commitの部分のチェック項目にあるReformat Codeとかにチェックを入れておくのをおすすめする。

before commit

これにチェックを入れておけば、コミットする前に自動的にReformat codeが行われるので、PR送信後に機械的な指摘が減るはずである。

また、Save Actionsというプラグインを使うのもおすすめである。アプリをローカルで実行した際にReformat codeが走るようにできる便利プラグインである。Android Studioから検索してインストールできるので、気になる人は使ってみてほしい。

後はコミットの履歴をきれいにしたりというのも気をつけているつもりであるが、あんまり神経質になる必要はないと思う。

成長のチャンス

チームで開発する機会がない私にとっては、Droidkaigiアプリの開発はとてもよい成長チャンスである。PRを送っている時点ですでに非日常なのだ。

PRでもIssueでも、これ大丈夫かな、クソコードって思われないかな、迷惑になってないかなと不安が渦巻いてしょうがない。しかしそんな心配しても、ダメなところはダメって言ってくれるだろうし、言われないとしても誰かが直すだろうし、あれこれ悩む前に送ってしまえでいいと思う。

やらかして恥ずかしい思いをしても、それは独学では得ることのできない貴重な体験である。そもそも参加しやすい雰囲気作りしてくださっているので、恥をかくなんてことを心配する必要はないのだけど。本当にスタッフの皆さんやレビュアーの方々には頭が上がらない。

せっかくの成長機会が目の前に転がっているのである。これを利用しない手はない。尻込みするくらいなら、どんどん飛び込んでいけばいいと思う。私はそう考えて突撃している。

幸いなことに、ちょうど今急ぎの用件が他にない。そのおかげでこのイベントにがっつり時間をとることができている。今後も最新技術のキャッチアップとあわせて、レビュアー・スタッフの方々に感謝しつつ、私にとっては大変貴重なこの機会を最大限利用しようと思う。

Androidプログラミングを学ぶ上で大切だと思うこと

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

こっちのブログで書くか、別のブログで書くか迷ったのだが、プログラミングの話だしこっちに書こうかなと思う。

書こうと思ったきっかけは、Androidプログラミングを教える仕事を受けたこと。まあ個人的に、私などが人様に何かを教えるなどおこがましいという思いはあったものの、まあ何事もやってみなければわからないということでやってみた。

Android特有の、ライフサイクル周りの話だとかは、まあ知っていないと辛いよねっていうことではあるんだけど、それよりもっと基礎的な、プログラミングを学ぶ姿勢とでも言おうか、今回書きたいのはそこについてだ。Androidプログラミングとタイトルにしているのは、私がAndroidしか知らないから限定しているだけで、たぶん他の言語でも同じなんじゃないかなと思う。

私はずっと一人でAndroidのプログラミングをやってきた。ほぼ独学である。書籍とネットの情報を頼りに黙々とやってきた1。そんなやつが人様にプログラミングを教えるのもおこがましい気がするものの、3ヶ月間教えてみて大事だなと思ったことが3つある。

1つ目は言ってしまえばコミュニケーション能力である。いきなりプログラミング関係ないじゃないかと思われるかもしれないが、これはとても大事である。別に話を弾ませたり、相手の気持ちを慮ったりする能力が必要だというわけではなく2、自分の考えを相手に伝えようと努力できることって、プログラミング関係なしに大事だなと思ったのである。具体的なシチュエーションとしてはエラーを伝える場面でよくそう思った。なんだかよくわからないけれどもこういうエラーが出たんですと、わからないなりに説明しようとする人とそうでない人がいる。エラーの確認の仕方は知っているかどうかの問題なので、初心者だから見込みがないとかそういう話ではない。しかし、わからないなりに説明をしようとする人に、私は伸び代を感じたのである。

2つ目はチャレンジ精神である。とりあえずやってみることが大事だということ。こう書いたら動いた、だったらこう書けば違う動きになるのではないかと仮説を立てて、挑戦してみる姿勢が大事だ。知らなければどうしようもない部分もあるので、教えてもらうことも大事ではあるが、教えてもらうのを待つだけではなく、自ら挑戦する姿勢もまた大事なのであると言いたい。

3つ目は自分で調べる力である。Androidは特にソースコードがすべて公開されているので、なぜそう動くのかはコードを読めば分かる3。ソースコードを読むのでなくても、公式のドキュメントを読んで調べることなら誰でもできるはず4。Androidのプログラムを書いていると、便利なライブラリのお世話になることがとても多いが、そういったライブラリの使い方を、ライブラリのドキュメントやソースコードから調べることは重要だ。

以上3つ。プログラミングを教えているのに、プログラミングの知識、変数がどうのとかJavaがどうのとかとかよりも、もっと基礎的なことの方が大事に思えたことが意外に感じられた。

最後に、これからプログラミングを勉強しようと思っている人は、できるだけ効率的に学びたいなと思っていることだろう。きっと、いい師匠(先生)を見つけるのが効率的なんだろうけれど、どうやって探すのかは私が知りたい。だからそんな効率的に学ぶ方法を探す前に、さっさと何かプログラミングしてみる方が早いと私は思っている。

Androidプログラミングで言えば、とりあえず市販の本でもネットの情報でもなんでいいので、とりあえず書いてあるとおりにやってみるのがよいと思う。書いてある通りにやってうまくいかなければ、誰かに聞くのがいいだろう。

実は、そういう「書いてあるとおりにやってみたけど動かない」という状況は、スキルアップにちょうどいいシチュエーション5なのだけど、初心者はただ辛いだけなので、誰かに聞くのが手っ取り早い。Twitterでつぶやいてみるとか、teratailとかStackoverflowとかで質問してみるとか6。学校の先生がいるなら、先生に質問するのがいいだろう。聞ける人がいるということはとても恵まれたことなので7、そんな環境にいる人は今のうちに有効活用するべきだと思う。

大事だと思った3つのこと。コミュニケーション能力、チャレンジ精神、自分で調べる力。何にでも言えることじゃないかと思うけど、何にでも言えるからこそ大事なんじゃなかろうか。実際、教えていてこの人はプログラムに向いている・向いていないと感じることが何回かあった。それはなぜそう思うのかと考えてみたら、プログラミングの知識よりも本人の姿勢によるところが大きいと思ったのだ。


  1. ネットで公開される知見に頼るところが大きいのでとてもありがたく拝見している 
  2. あるにこしたことはないが 
  3. 理解できるかはともかく 
  4. 理解できるかはともかく 
  5. 原因を調べて解決することは、プログラミングをしていく上で何度も通る道であり、そういうハマりを経て人は成長していくのである 
  6. 質問する敷居の高さは、teratailの方が低いとは思うが、思い出してほしい。コミュニケーション能力が大切だということを。インターネット上の人々はあなたの状況を理解しているエスパーではないので、聞き方が悪いと答えは返ってこないどころか、逆に傷つくことになるかもしれない。 
  7. 学生という立場は学生という立場を失ってはじめてそのありがたみが分かるのだ 

CIを導入してみた

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

FilteredHatebuにCIを導入してみました。

CI上での設定も必要なので、これだけで導入できるわけではありませんが、CI導入のための変更はこんな感じです。エミュレータが必要なEspressoでのテストがこけないよう、テストコードを修正した変更も含まれています。

そもそもこのアプリをpublicなリポジトリでやっているのは、実はCIを導入してみたかったからという理由がありました。

最初はとりあえずCIを導入してみようというだけで作業を始めたのですが、結局よく目にするTravisCI、CircleCI、Werckerの3つを試してみました。

CIを使うにあたっては、publicなリポジトリで運用する、無料でできる範囲でやる、あまりにややこしいことはやらない(できればそのCI単体で完結させる、ただしSlackへの通知は除く)という3点を念頭にやってみました。

各CIサービスを使ってみた感想

  • TravisCIは秘匿情報を比較的安全に運用しやすい
  • CircleCIはテストレポートを確認しやすい
  • Werckerは圧倒的にビルドが早い

publicなリポジトリでの運用という観点で、秘匿情報(署名ファイルなど)の取扱、テストレポートの確認手段、ビルド時間の長さ、導入難易度について私の主観でまとめてみるとこんな感じです。

CIサービス 秘匿情報 テストレポート ビルド時間 導入難易度
TravisCI × ×
CircleCI × ×
Wercker ×

秘匿情報については後述。

テストレポートは、./gradlew connectedAndroidTestなどを実行した後に生成されるHTML形式のレポートを確認できるかということです。CircleCIはCIサービス上でHTMLファイルにアクセスすることが可能です。Werckerは直接見れませんがファイルをダウンロードできます。Travisはそもそも見れません。どこか外部のストレージサービスでも使って、ファイルをアップロードするしかないようです。

ビルド時間はWerckerの圧勝です。TravisCIもCircleCIもSDKのアップデートに時間を食われるため、一度のビルドに18分くらいかかります。Werckerビルド環境をDockerで構築してしまえるので、SDKのアップデートが発生しません。初回を除けばだいたい6分くらいで済んでいます。圧倒的な早さです。

導入難易度については、TravisCIとCircleCIはどちらもあまり大差はないと思います。ドキュメントも日本語情報も充実しています。設定もYAMLで記述するだけですから、CIサービスごとの方言はあるもののそうハマるものでもありません(CircleCIは微妙にドキュメントが現状に追いついていない部分があって惑わされたりもしましたけど)。

一方でWerckerはドキュメントが他2つと比較して充実しているわけではありません(他2つと比較すると分かりにくいと感じました)。さらにDockerの知識が必要にもなるので、導入までにかかった時間は一番長かったです。(逆にDockerの知識があって、Androidの環境を構築するのが簡単にできてしまう人であれば、Wercker使うのが一番ラクかもしれません)

3つのサービスを使ってみましたが、それぞれ一長一短で、このサービスが最強といえないもどかしさがありました。ただprivateリポジトリで使うなら、Werckerが一番いい気がします(早さと設定の自由度が魅力)。

秘匿情報の取扱

困ったのは、署名ファイル(release.keystore)をCIでどう扱うのかという問題です。

どのCIサービスでも同じですが、release.keystoreなどの秘匿情報をCIでも扱えるようにするためには、そのファイルをリポジトリに含めるか、もしくはインターネット経由でアクセスできるどこかに別途公開しておくかしかありません。

前者の方法はpublicなリポジトリでは使えません。privateリポジトリであれば問題ないのでしょうが、私のケースでは採用できませんでした。

かといって後者のどこか別の場所に置くという方法も、適切な場所が思いつきませんでした。誰でもアクセスできるような場所に置くことはできませんし、適切なアクセス制限がかけられる置き場所となると、選択肢はそう多くはないと思います。

どのCIサービスでも、privateな情報についてはCI側で環境変数を利用することができます。publicなリポジトリで運用しても、パスワードなどが見えないように配慮することができます。TravisとWerckerは環境変数にセキュアな項目にする設定があるので、その点いくらか安心です。ただしCircleCIは、publicなプロジェクトではパスワードとか漏れたら困る情報を環境変数に入れるなと注意書きがありました。

しかし環境変数で扱えるのは文字列です。署名に使うrelease.keystoreというバイナリファイルを環境変数で扱うことはできません。

私が試した3つのサービスで、唯一秘匿情報ファイルの取扱がCIサービス単体で解決できるのはTravisだけです。解決できると言っても、暗号化してリポジトリに含めるという方法ですけれど。

他のサービスでは別途ストレージサービスを利用するなどして、そこからファイルをとってくるという手法を使わなければなりません。

エミュレータを使うテストは鬼門

今回CIを導入したプロジェクトでは、Espressoを使ったテストを行っています。手元の実機では安定していても、CIで実行すると失敗ばかりで非常に困りました。

Espressoテストレコーダーは万能ではない

ローカルの実機だと問題ないのにCI上だとエラーが起こりました。例えばこんなコード。

-        ViewInteraction appCompatTextView = onView(
-                allOf(withId(android.R.id.text1), withText("test.com/"),
-                      childAtPosition(
-                              allOf(withClassName(
-                                      is("com.android.internal.app.AlertController$RecycleListView")),
-                                    withParent(withClassName(is("android.widget.FrameLayout")))),
-                              0),
-                      isDisplayed()));
-        appCompatTextView.perform(click());
+        onData(anything())
+                .atPosition(0)
+                .perform(click());

onData~にコードを書き換えるとCI上でも問題なく動くようになりました。Espressoテストレコーダーは便利ですが、こればかりに頼る訳にはいかないという教訓です。

DataBindingを使っていると起こるエラー

java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation at android.databinding.DataBindingUtil.<clinit>(DataBindingUtil.java:31)という謎のエラーが発生しました。

最初に導入していたTravisでぶち当たった問題で、Android Studioでテストを実行する分には問題ないのに、TravisとWerckerではコケました。Werckerは利用するDockerイメージによるものだとは思います。CircleCIは特に問題になりませんでした。

https://code.google.com/p/android/issues/detail?id=182715

どういうエラーなんだかよく把握していないのですが、DataBindingを使っていて、かつテスト対象のActivityでDataBindingを使っていると発生するようです。

#31に回避策が書いてあります。

エミュレータの起動を待つ処理

CIでエミュレータを使ったテストを行うためには、エミュレータが起動するのを待たなければなりません。TravisCIにもCircleCIにも、エミュレータの起動を待つためのスクリプトが用意されています。Werckerを使う場合はそんな便利なコマンドは用意されていないので、自分でシェルスクリプトを書く必要があります。

しかしこれがまあ安定しない。

試行錯誤の結果、最終的にはkeyeventを送る前に10秒スリープ処理を挟むことで安定しました。(ネットで探し回っていたらsleep 30としている情報にあたり、それで安定するようになった。30秒は長すぎる気がして5秒にしたら時折コケるので10秒にした、という経緯があります)

CIを導入してみて

最初はデプロイまでやってやろうとはじめましたが、途中で方針転換しました。初めてやるなら簡単なところからやるべきだなと思います。

とりあえず最初はprivateなリポジトリ、publicでやるならせめてdebugビルドのみでの運用がおすすめです。publicかつリリースビルドもやって、デプロイまでやろうなんていうのは大変だと思います。

そしてやるにしてもユニットテストまでにしておいたほうが無難ではないかなと思います。UIテスト(Espressoなどエミュレータを使ってやるテスト)までやろうとすると、これまた大変です。(大変でした)

最終的に3つのCIサービスを設定してみましたが、それぞれのサービスごとの特色が見えて面白かったです。

DataBindingを試す

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

DataBindingがアツいらしいと聞いて試してみました。簡単な使い方をするなら想像以上に簡単でした。

今までActivityなどでfindViewByIdを書きたくないから、ButterKnifeをどのプロジェクトでも使っていたのですが、DataBindingを使えば同じようなことができます。

両者を使ってみて感じたのは、ButterKnifeがレイアウトXMLをJavaコードに持ってくるイメージであるとすれば、DataBindingはJavaコードをレイアウトXMLに持っていくイメージであるということです。

DataBindingを使うことで、Javaで作成したコードを、レイアウトXMLに埋め込むことができるようになります。レイアウトXMLでどのデータを使うか指定しておけば、Activityで「このクラス(のインスタンス)を使ってくれ」と指定するだけでその内容を表示できたりします。

具体的な使い方はData Binding Guide – Android Developersを参照してください。

DataBindingを使う設定

Android Studio 1.3以上であることが必須です。

Android Gradle Plugin 1.5.0-alpha1以上を使っていることが必須、でした。

Android Studio 2.0 betaになると、コード補完のサポートがより強力になってます。

build.gradleでDataBindingの設定を有効にすることで利用できます。

android {
....
dataBinding {
enabled = true
}
}

表示するデータを保持するクラスの作成

“`public class Character{
public String name;
public int age;
public String skill;

<pre><code>public Character(String name, int age, String skill){
this.name = name;
this.age = age;
this.skill = skill;
}
</code></pre>

}“`

DataBindingを使ってアクセスするには、publicなフィールドであるか、privateなフィールドである場合publicなgetterがあることが必須です。

レイアウトXML

“`
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>

<pre><code><data>
<variable
name="chara"
type="jp.gcreate.sample.databinding.Character"
/>
</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="jp.gcreate.sample.databinding.MainActivity"
>

<TextView
android:id="@+id/chara_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{chara.name}"
/>

<TextView
android:id="@+id/chara_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(chara.age)}"
/>

<TextView
android:id="@+id/chara_skill"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{chara.skill}"
/>

</LinearLayout>
</code></pre>

</layout>“`

ポイントはこんな感じ。

  • レイアウトXMLを<layout>タグで囲む
  • レイアウトXML内で利用するクラスを<data>タグ内で宣言する
  • <data>タグ内で宣言するクラス名は完全修飾ドメイン名
  • <variables>タグ内で宣言したnameを使ってアクセスする
  • Javaのコードを埋め込める(Integer.toString()とか)
  • コードは@{}で囲む

ちなみにandroid:textのところに埋め込むデータは、TextView.setText()を使って設定されるので、ここにint型のデータを埋め込むときは注意が必要です。何も考えずにint型のデータを表示しようとすると、そのint値がリソースIDとして認識されてしまいエラーになるからです。その際のエラー内容も、「そんなリソースIDないぞ」という内容で軽くハマりました。

Activityの処理

ちなみにDataBindingによって生成されるクラス名は、レイアウトXMLのファイル名をスネークケースからキャメルケースに変換したものに、Bindingを付け加えたものになります。

activity_main.xmlならActivityMainBindingに、hoge_hoge.xmlならHogeHogeBindingになります。

“`public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
Character chara;

<pre><code>@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
chara = new Character("桃太郎", 18, "きびだんご");
binding.setChara(chara);
}
</code></pre>

}“`

レイアウトXMLに紐付けるクラスを、setChara()で渡すことで、渡したCharacterクラスのデータが表示されます。ちなみにレイアウトXMLで<variables name="chara" .../>と設定しているからsetChara()になっています。nameをhogeにしていたらsetHoge()になります。

データの変更を反映させたい

単に表示するだけでは「何が便利なのか」という感じるかもしれません。

例えば桃太郎のスキルを「鬼退治」に変更したいとします。findViewByIdを使わずとも、DataBindingを使えばレイアウトXMLでidを割り振ったViewにアクセスすることができます。

@Override
public boolean onTouchEvent(MotionEvent event) {
binding.charaSkill.setText("鬼退治");
return super.onTouchEvent(event);
}

これを追加すると、タッチイベントが発生したらスキルの内容が「鬼退治」に変わります。

インスタンスの内容が変わったら自動的に反映されるようにする

レイアウトXMLと紐付けるクラスを以下のように書き換えると、インスタンスの内容が変更されるだけでUIに表示されるデータも変わります。

“`public class Character extends BaseObservable {
public String name;
@Bindable
public int age;
public String skill;

<pre><code>public Character(String name, int age, String skill) {
this.name = name;
this.age = age;
this.skill = skill;
}

public void countUp(){
age++;
notifyPropertyChanged(jp.gcreate.sample.databinding.BR.age);
}
</code></pre>

}“`

  • BaseObservableのサブクラスにする
  • 自動的に変更させたいpublicなフィールド、もしくはgetterに@Bindableアノテーションを付ける
  • データの変更が生じるメソッドでnotifyPropertyChanged()を呼ぶ

Activityのコードを以下のように書き換えてみます。

@Override
public boolean onTouchEvent(MotionEvent event) {
chara.countUp();
return super.onTouchEvent(event);
}

こうするとタッチイベントが生じる度に桃太郎が年をとるようになります。いちいち自分でTextViewに変更済みのデータを設定する必要がなくなります。

今のところ私はこの程度しか使っていないのですが、これだけでも充分便利だなぁと思います。とりあえずは簡単なところから試してみてはいかがでしょうか。

アプリを公開してコメントもらえて嬉しかった話

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

Google Playで公開しているカスタムフォトウォッチで初めてコメントをもらいました。

英語でのコメントだったのですが、「12時間表示のオプションが欲しい」っていう内容でした。

おそらく私は、単にそれだけのコメントでも嬉しかったと思います。自分で作ったものに対して反応がもらえるっていうのは、それだけでうれしいのです。

しかし今回のコメントはさらに嬉しい要素がありました。「12時間表示が必要だからアンインストールしたけど、時刻や日付の色やサイズを変えれるのはとてもいい」と褒めてくれていました。

英語でのコメントでしたけど、褒めてくれてるっぽくてテンションが上りました。

つたない英語力とGoogle Translateの力を借りつつ、コメントくれた人とメッセージのやり取りしました。思えば生まれて初めて海外の人とコミュニケーションした気がします。

12時間表示は実は奥が深い

今回の話は「単に嬉しかった」だけに終わりません。実はいい勉強にもなりました。それは12時間表示についてです。

今回もらったコメントに対して、さっそく12時間表示への対応を行いました。スマホの時刻設定で12時間表示にしている場合、wear側での表示を12時間表示にするようにしたのです。12時になったら0時と表示するようにしたわけです。

しかしコメントをくれた方とのやりとりの中で、「うちの国では0時は深夜の0時(24時)を意味するんだ」ということを言われました。

カスタムフォトウォッチでは日付の表示方法についてはいろいろなパターンを提供するようにしていました。海外では月/日ではなく、日/月と表示する場合もあることは知っていたからです。

しかし時刻の表示についても似たような問題があることを、ここにきて初めて知りました。

アプリの海外対応は、文字面だけを英語にするだけではないのだなと、とてもいい勉強になりました。

とりあえず公開してしまえも必要なのかも

アプリを公開するにあたって、「こんな機能も要るかな」とかいろいろ迷ったりもしました。色の選択肢はもっと増やしたほうがいいのかとか、デザインはこれでいいのかとか。

それでも最終的には「ぐだぐだ考えるの面倒くさくなったからもういいや」でアプリを公開しました。いつまでたっても公開できる気がしないし、何のリアクションももらえない状態で開発するのもモチベーションが保てなかったのです。

全てのユーザが今回の方のように丁寧なレビューをくれるとは限りません。それどころか「何やねんこの糞アプリ」みたいなコメントが寄せられるかもしれません。が、それでも公開しなければ何の反応ももらえません。

作るところまで作ったら、後は利用者の反応を見ながら開発するっていうのもいいのかもしれません。

コメント付きレビューをもらえたのが初めてだったので、なんか無性に嬉しかったっていう話でした。

DroidKaigiに参加してきました

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

DroidKaigiに行ってきました。飛行機使って前泊での参加です。

満員でしたね。熱気がすごい。

今日ほど分身の術が使えたらと思うことはなかったでしょう。それくらい、全部のセッション聞きたかったです。

今後のアプリ開発に役立つ情報がいっぱいでした。現状動いてはいるけど、ちゃんとできてなかったところとか、「そうなんだ」っていう気付きもあって有意義でした。

とりあえず面倒臭がらずに少しずつテストを書くことから始めようかなと思います。

テストを動かす環境づくりがよくわからないとか、テストの書き方がよく分からないとか、個人でやってると仕様の変更で対応してしまってテストまで書き換える必要が出てきて面倒くさいとかで敬遠してたんですけど、つべこべ言わずにテスト書こうと思いました。

一方で、周りの空気に飲まれて受け身になりすぎたのが反省点です。なんかもうちょっと攻めの姿勢で聞けたら良かったのにと、振り返って思います。

質問とかできたら良かったんですけど、頭働かなくてそれどころではありませんでした。

言い訳ですけど、参加してる間はそれどころではなかったんですよね・・・。周りスゲーし、人はいっぱいだし、席の確保も大変だし、スケジュールは過密だしで話を聞くのが精一杯でした。

幸い皆さんスライドを公開してくださっている上に、スライド見るだけでも話の内容がある程度分かるようになってるので、後でじっくり復習したいと思います。

個人でアプリ作ってると、こういった周りの開発者さんたちの空気感とか全くわからないので、参加してよかったなと思います。わざわざ岡山から出張った甲斐はあったかな・・・。

運営の皆さん、発表者の皆さん、参加者の皆さん、お疲れ様でした。

Master of Fragmentの更新を首を長くして待ってます。

Android Wearでwearとスマホ間でデータをやりとりする話

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

データのやりとりはWearable.DataApiを使うことでやりとりできます。Wearable.MessageApiを使うことでもできます。

両者の違いはこんな感じ。

DataApi

  • 接続が切れていても送信できる
  • データはonDataChangedで受け取る
  • 送れるデータは100KBまでだが、Assetを使うことで大きいデータも送れる
  • データを送信するというより、DataItemを更新して、その更新を通知するイメージ

MessageApi

  • 現在接続中のノードに対してデータを送信することができる
  • データはonMessageReceivedで受け取る
  • データを送る際にはノードを指定する必要がある

ノードIDについて

当たり前ですが、ノードIDはAndroid Wear端末とスマホで異なります。

WatchFaceを作成して、その設定画面を用意している場合、WearのノードIDはスマホ側では簡単に取得できます。

mobile側の設定画面となるActivityでgetIntent().getStringExtra(WatchFaceCompanion.EXTRA_PEER_ID)とするとWearのノードIDが取得できます。これは設定画面の起動がAndroid Wearアプリ経由で行われるためです。

一方Wearから、もしくはmobileからでもAndroid Wearアプリを経由しない起動の仕方をするアプリの場合はこの方法では取得できません。

その場合はNodeApi.getConnectedNodesを使うことで、接続されている端末のノードリストを取得することができます。現状スマホとAndroid Wearは1:1でペアリングされるはずなので、これで相手方のノードIDを取得できるでしょう。(将来的に複数ペアリングできるようになったらどうやればいいんでしょうね?)

DataApiを使ったデータ送信の注意点

[DataApi – Android Developers]のConcurrencyにありますが、DataApiを使ったからといってmobileとwearでDataItemが全く同じになるわけではありません。

onDataChanged()内であれば正しいデータが参照できます。これは変更があったDataItemが通知されてきているからです。

しかしAndroid Wear端末を再起動した後に、DataApiを使って送信された設定データを読み込もうとした時に問題が生じます。

wear側からDataItemを識別するUriでデータを取りに行っても、mobile側の設定と齟齬が生じている可能性があります。

その理由はDataItemが以下のように識別されているからです。

wear://ノードID/パス

mobileで作ったDataItemはmobileのノードIDで識別されます。同じパス文字列で識別しているからといって、勝手にwearのノードIDのものが変更されるわけではありません。

これはWatchFaceのサンプルコード(com.example.android.wearable.watchface)を見ると何となく分かると思います。サンプルコードでは、mobileからのDataItemの変更を受け取ると、wear側で同じデータを上書きする処理を行っています。

サンプルのようにmobileからもwearからもデータを送り合うようなアプリの場合、どちらのノードのDataItemも常に同じ状態にするように配慮しないと齟齬が生じて困ることになると思います。

片側からしか送らないというのであれば、ノードIDを指定してDataItemを取りに行くのもありかもしれません。

MacをYosemiteにバージョンアップしたらAndroid Studioが起動しなくなった

この記事は最終更新から3ヶ月以上が経過しています。情報が古い可能性があります。

MacをYosemiteにバージョンアップしたらAndroid Studioが起動しなくなりました。

原因はYosemiteへのアップデートでJDK 1.6が消えてしまったことが原因のようでした。(消えたのか参照できなくなったのかはよく分かりませんが・・・)

ターミナルで/usr/libexec/java_home -v 1.6と打つと、Unable to find any JVMs matching version “1.6”.と表示されました。JDK 1.7と1.8はバージョンアップ前と同じように存在してくれてましたが、1.6だけ参照できなくなってました。

対策はJDK 1.6(Java 6)を再インストールすればOKです。JDK1.6(Java 6)はこちらからダウンロードできます