作者別: Gen

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

ストアに公開しているアプリは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で指定しないとうまくいかないことがあるだろう。

flutterやるならぜひ登録しておきたいLive Template

Flutterを触り始めたのだが、とにかくWidgetのレイアウト変更がツライと感じていた。

AndroidでいうところのViewのレイアウトがコードでゴリゴリ書いていく感じになっているので、インデントが深くなってツライ。Widgetの考え方もAndroidのViewとはちょっと違う(レイアウトに関する設定はレイアウト情報を持つWidgetでくるむとか)

ゼロから作る分にはまだいいのだが、一度作ったレイアウトに対して、「このTextにmargin付け加えたい」となったときがツライ。シンプルなレイアウトならまだいいが、Widgetが何個も登場したり、何回層もネストされてたりすると正直触りたくない。

例えばこんな感じ。

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("FriendlyChat"),
      ),
      body: new Column(
        children: <Widget>[
          new Flexible(
              child: new ListView.builder(
                padding: const EdgeInsets.all(8.0),
                reverse: true,
                itemBuilder: (_, index) => _messages[index],
                itemCount: _messages.length,
              ),
          ),
          new Divider(height: 1.0,),
          new Container(
            decoration: new BoxDecoration(
              color: Theme.of(context).cardColor,
            ),
            child: new Text("hoge"),
          ),
        ],
      ),
    );
  }

これはまだかわいいものだが、それでもめんどうくさい。

そこでLive Templateを追加して、IntelliJのSurrond With機能を使って簡単にWidgetを囲えるようにしてみた。Flutterのチュートリアルを試したりするのに活躍するのでかなり捗ると思う。

Live Templateを定義

Preference > Editor > Live Templateが定義場所。

定義する場所は別にどこでもいいのだろうけれど、Flutterに関することなのでFlutterのところに追加した。

new Column(
  children: <Widget>[
    $SELECTION$
  ]
),

Dartファイルに適用されるように指定して、Reformat according to styleにチェックも入れておく。

Sample

new Container(
  child: $SELECTION$
),

childを1つしか取らないWidgetでラップする場合に使うやつもついでに追加。使っているのがColumnContainerであることに特に意味はない。まあそこは必要に応じてクラス名を書き換えればいいだろうから気にしない。

Live Templateを定義したら、後は囲いたいWidgetを選択した状態で、Surround with機能を呼び出し(私の環境だとcmd+opt+t)、定義したLive Templateを適用すればよい。楽ちん。

test movie

Live Templateは初めて活用したので、もっとこうした方がいいよというのがあったら教えて欲しい。

追記:

記事を書き終わった後で、そもそも標準でWidgetをラップする機能が存在していることを知った。ラップしたいWidgetのクラス名にカーソルをあわせた状態で、Intention Actions(Macならopt+enterで出るやつ)を使うとWidgetを別のWidgetでラップすることができる。わざわざLive Templateを追加する必要はなかった・・・。

カスタムフォトウォッチVer2の紹介

Android搭載のスマートウォッチ向け、自分の好きな画像を文字盤に設定することのできるアプリがカスタムフォトウォッチです。

Google Play で手に入れよう

アプリ実行に必要なもの

  • Android搭載のスマートウォッチ
  • スマートウォッチとペアリングしたAndroidのスマホ(今のところiPhone対応の予定はありません)

スマートウォッチのウォッチフェイスをカスタマイズするアプリなので、スマートウォッチがないと意味をなしません。スマホだけでは単なる画像編集アプリのような何かにしかならないのでご注意を。

Android搭載スマートウォッチはiPhoneともペアリングして使えますが、今のところこのアプリはiOSには対応していません。

カスタムフォトウォッチはスマホとスマートウォッチの両方にインストールする必要があります。

以前のバージョンであればスマホからインストールすれば、ペアリングしているスマートウォッチにもアプリが自動的にインストールされていましたが、Ver2からはそれぞれ個別にインストールが必要になっていますのでご注意ください。

実行に必要なソフトウェア

  • GooglePlay開発者サービス
  • Wear OS by Google(スマホとスマートウォッチのペアリングを行うアプリ)

これらはスマートウォッチとスマホとの間の通信処理を行うために必要となっています。インストールされていてもバージョンが古い場合にアプリがうまく動作しないおそれがあります。バージョンが古い場合はアプリから更新を促すメッセージが表示されると思うので、各アプリの更新をお願いします。

メッセージを無視しても使うことはできると思いますが、スマホから設定した画像がスマートウォッチに表示されないなど、データの同期がうまくいかないおそれがありますのでご注意ください。

使い方

スマホ側

まずは画像を追加しましょう。右下のプラスボタンを押せば画像を追加できます。

Add new image

スマホ側からスマートウォッチに設定したい画像を取り込み、文字盤に表示する時刻や日付などの情報を設定します。スマホに保存されている画像データや、カメラからその場で撮影した画像などを取り込むことができます。

Import image

中央に表示されている枠がスマートウォッチの文字盤に設定される範囲になります。拡大・縮小などで位置を調整してみてください。拡大・縮小の他回転(90度単位ですが)や反転もサポートしています。位置が決まったら切り取りをおこなってください。

Cut image

このアプリでは時刻の表示位置などを細かくカスタマイズできるので、画像に合わせて最適な位置に調整していただけます。

Customize

以上の手順で画像データを用意すれば自動的にスマートウォッチに同期されます。

スマートウォッチ側

まずはウォッチフェイスにカスタムフォトウォッチを設定してください。スマートウォッチの文字盤で左右にスワイプするとウォッチフェイスの変更ができると思います。そこからカスタムフォトウォッチを選択してください。

Select watchface

Select customphotowatch

スマホから設定した画像に切り替えるには、設定ページを開いてください。カスタムフォトウォッチをウォッチフェイスに設定した状態で、スマートウォッチの画面を長押しすれば開くと思います。

もしくはウォッチフェイス選択画面でカスタムフォトウォッチの下に歯車アイコンが表示されるので、それを押すと設定画面が開きます。

Open setting page

設定画面を開くとスマホで設定した画像が一覧で表示されます。切り替えたい画像を選んでもらえばスマートウォッチの文字盤が設定した画像に切り替わります。

Select image

以前までのバージョンでは、スマホで画像を設定したら自動的にその画像に切り替わっていましたが、Ver2からはスマートウォッチで切り替えるようになりました。

FAQ

スマホで作成したデータがスマートウォッチにない

同期メニューを実行していただくことで改善する可能性があります。同期メニューはスマホ側のアプリの右上メニューからアクセスできます。

Sync menu is here

同期メニューを実行しても反映されない場合、以下の可能性が考えられます。

  • Google Play開発者サービスのバージョンが古い・インストールされていない
  • スマートウォッチ・もしくはスマホ側のアプリが古い

スマホおよびスマートウォッチそれぞれでGoogle Play Storeにアクセスして、アプリの更新がないか確認をお願いします。

削除したはずのデータがスマートウォッチ側に残っている

同じく上記の同期メニューの実行で改善されると思います。

スマートウォッチの画像が真っ暗になる・時刻しか表示されない

以前のバージョンからアップデートを行った場合に発生する場合があります。

基本的にはスマホから同期のメニューを実行していただいた後に、スマートウォッチ側の設定画面(画面長押し、もしくはウォッチフェイス選択画面の歯車アイコンをタップ)を開いて画像を選択していただければ改善されると思います。

設定画面すら開けない場合、一度スマートウォッチ側のカスタムフォトウォッチをアンインストールしていただき、再度インストールし直していただくと改善するかと思います。

サポート窓口

ストアページに記載しているメールアドレスまでご連絡ください。

https://play.google.com/store/apps/details?id=jp.gcreate.product.customphotowatch

Google Play で手に入れよう

RoomでLiveDataを扱うときに注意

Architecture Componentで追加されたRoomを実際に使ってみた。

なんかややこしそうと思って敬遠していたのだけど、やってみると意外とそうでもないなという感じで「あれ、これだけ?」っていう感じで実装できた。実装できたと言っても、適当なサンプルだから簡単だっただけなんだけども。

https://github.com/gen0083/SampleArchitectureComponent

RoomではDatabaseDaoEntityの3種類を用意してやると、AnnotationProcessorによるコード生成が行われてSQLiteを簡単に利用できるというライブラリ。

LiveDataを返すようにDAOで定義することができ、この場合Databaseが更新されるとその変更がLiveDataを通じて受け取れる。いわゆるいいねボタン問題を解決する一つの解決策と言える。

LiveDataを返すようにDAOで定義を行うと、その処理はバックグラウンドで実行されるようなコードが生成される。逆にそれ以外の処理は自分で実行スレッドを考慮しないといけない。デフォルトだとRoomで生成されたコードをメインスレッドで実行すると例外が吐かれる。

LiveDataを返すようにする場合は注意しなければいけないことがある。

https://github.com/gen0083/SampleArchitectureComponent/blob/master/app/src/androidTest/java/jp/gcreate/sample/samplearchitecturecomponent/data/repository/TestRepositoryByRoomTest.kt#L66

このコードのようにsut.watch().valueとLiveDataのvalueを取得しても、その時点ではvalueはnullである。該当のテーブルのデータ全件取得と変更通知を兼ねてLiveDataを使おうとすると想定通りに動かない。実際にデータが取れるまでは時間がかかる。

LiveData<List<TestData>>としておけば、変更通知も受け取れる、データ全件取得もできる、便利じゃんと思ったけど、そうそううまくはいかなかった。といっても、RecyclerViewに表示する目的であれば、LiveDataを使うとすごい楽ちんであった。同じくSupport LibraryのListAdapterと組み合わせるとすごい楽。

ちなみにこのLiveDataを返すメソッドのユニットテストを行う場合、android.arch.core:core-testing:$architecture_version"を追加して、@get:Rule var executorRule = InstantTaskExecutorRule()とJunitTestRuleを適用してやるとうまいことテストができる。このルールを適用しないとバックグラウンドでの処理を待たずにテストメソッドが終了してしまい、LiveDataからデータを受け取ることなくテストが終了してしまう。

versionCodeの最大値

Google Playにアプリをアップロードする際、versionCodeを常に増やしていく必要がある。

Gradleを使ってこのversionCodeを自動的に生成する(versionNameと一緒に)ときに、versionCodeには最大値が存在するということは覚えておかないとならない。

https://developer.android.com/studio/publish/versioning.html

Google Playでは2.1Mが最大値となっているらしい。

versionCodeを生成するスクリプトを書く際は、APKを作成してそのAPKのversionCodeを確認するところまで気を配ろう。スクリプトでversionCodeが生成できていることだけ確認して、Google PlayにAPKをアップロードしたら最大値にひっかかって更新できなくなった、なんてことになったら悲しすぎる。

ちなみにこの問題にでくわしたのは、versionCodeをメジャーバージョン3桁、マイナーバージョン3桁、パッチバージョン3桁、ビルド番号3桁、MultiAPK識別用の符号1桁でversionCodeを生成したときである。

Google PlayにAPKをアップロードする前に気づけて良かった。

Androidアプリ設計パターン入門を読んだ

Androidアプリ設計パターン入門を読んだ。製本版を買ったとばかり思っていたが、電子版で出資していた。思い込みとは恐ろしいものである。

せっかく出資して購入したのだから、もっと早く読んで(アーリーアクセス版で)疑問点なり質問すればよかったなと今更後悔している。

購入した動機は、設計に関する考え方を知りたかったからだ。

考え方というと勘違いされてしまうかもしれないが、○○という設計パターンはこういうところで優れている、なんてことを知りたかったわけではない。他の人(というか日本Android界を引っ張っていくようなエンジニアの方々)が、どういうことに困ってどういう思いで設計パターンを選んでいるのだろう、というところが知りたかったのである。

その観点から言うと、やっぱりみんな困るところは一緒なんだなぁということが分かってよかった。個人的には特に4章が興味深かった。

そもそも私自身はこれまでずっとソロで開発してきているので、チーム開発の話自体が新鮮である。みんなこういう感じで開発してるのかと、そういう雰囲気が感じられる事自体が何よりありがたい。そういう意味で買って(出資して)良かったなと思っている。

正直な話、ソロ開発する上ではどの設計パターンで作るかについて神経質になる必要はないと思う。最近の私は、とりあえずActivityのライフサイクルに振り回されないように作ればいいかな程度のゆるい考え方で、設計パターンの遵守より作りやすさを優先して作っている。

本書を読んでいて思ったのは、やはり設計パターンはチーム開発において重要になってくるものだということだろう。

どの章も、チームでの開発の視点で描かれている。設計パターンの導入とチームでの開発は切っても切り離せない問題なのだ。

本書は、Androidにおいてこの設計パターンで作れば間違いがない、このパターンこそ最強であるからこれをやっておけ、という内容にはなっていない。だから、結局どのパターンを学んでおけば安泰なのか知りたい、という観点で読むと肩透かしを食らうと思う。

そもそもそんな銀の弾丸があるなら、みんながそのパターンで作っているはずだ。

私は本書を読んでいて、Fluxパターンはなんか良さそうだなと思ったのだけど(データの流れを一方通行にするというところと、状態を保持するStoreが独立してるってところが良さそうに感じた)、じゃあこのパターンでリストのスクロール位置を保存しようと思ったらちょっと大仰すぎやしないかと感じる。スクロールの度にActionを発行して、Storeの状態を更新していくのだろうか。onSavedInstanceSateで位置を保存するだけでいいんちゃうのと思ったりする。

そんなわけで銀の弾丸はない。どの設計パターンを学べばいいですかっていう人にはもしかしたら合わないかもしれない。

Android開発においては、どんな設計パターンがどういう問題を解決するために使えるのか、どういう作り方をするのかという考え方を学んでおくしかない。自分が他のチームにジョインした時に困らないように。

これからチーム開発を行っていけるようにするためには、どういう問題を解決するためにどんな設計パターンが存在していて、どんな感じにAndroidに適用してくのかの考え方を知っておくことが大事だと思う。その考え方について、本書はMVP、MVVM、Fluxといった設計パターンを、著者の考え方とあわせて書かれているので、独学で勉強していますとかチーム開発の経験がない人ほど有用であるように思う。少なくとも、ずっと独学で勉強してきた私にとってはとても有用な内容だった。

具体的にどうやって実装していくかなんて言うのは自分で手を動かしてみないとわからない。本書で大事なのは著者の頭の中身が覗ける(考え方が赤裸々に書かれている)ということである。

ああ、私も誰かと一緒にアプリ開発したい(切実)。

Android アプリ設計パターン入門

Android アプリ設計パターン入門

  • 著者:日高 正博,小西裕介,藤原聖,吉岡 毅,今井 智章,
  • 製本版,電子版
  • PEAKSで購入する

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

去年に引き続き、今年も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でも、これ大丈夫かな、クソコードって思われないかな、迷惑になってないかなと不安が渦巻いてしょうがない。しかしそんな心配しても、ダメなところはダメって言ってくれるだろうし、言われないとしても誰かが直すだろうし、あれこれ悩む前に送ってしまえでいいと思う。

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

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

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

com.google.android.gms.tasks.Taskのユニットテスト

com.google.android.gms.tasks.Task<T>にまつわり処理でユニットテストを書きたいということがある。例えばFirebaseを使うときにそんな思いに駆られることがあるだろう。

私はFirebase Realtime Databaseの処理をラップするクラスを作成したときに、ユニットテストを書きたいと思った。

何らかの処理を要求した後、返ってくるのがこのTask<T>というクラスである。リクエストはすべて非同期で処理されるので、処理の結果はaddOnSuccessListenerなどを使ってリスナーをセットして、そのリスナーの中でリクエストの結果を受け取るようなコードになる。

ユニットテスト書くときに困るポイントは2つ。

  • どうやってTask<T>を生成するか
  • 処理結果をリスナーで受け取るにはどうするか

Taskの生成

Task<T>を生成するにはTasks.forResult()などのメソッドを利用するとよい。

com.google.android.gms.tasks.Tasksがファクトリメソッドみたいなものなので、こいつ経由で目当てのTask<T>が作れる。こうすることで起点部分の問題はクリアできる。

https://developers.google.com/android/reference/com/google/android/gms/tasks/Tasks

リスナーの注意点

処理結果をリスナーで受け取る際は注意が必要である。

利用する際にはaddOnSuccessListener()に渡す引数は1つ、この場合は成功時に行う処理だけ指定していると思う。

この場合、OnSuccessListeneronSuccessメソッドがメインスレッドで呼び出されることに注意が必要である。

実はTask<T>に対して設定するOnSuccessListenerOnFailureListenerOnCompliteListenerはそれぞれ、特に指定を行わない限り必ずAndroidのメインスレッド上で呼び出される。このメインスレッドで呼び出されるというのは、Looper.getMainLopper()で取得されるメインスレッドである。

つまり、JVMユニットテスト(testディレクトリに配置するユニットテスト)ではリスナーの処理が呼び出されないことに注意が必要である。

この問題に対応するためには、Instrumentation testとして実行する(androidTestディレクトリに配置する)か、addOnSuccessListenerでリスナーを設定する際に、スレッドを指定(Executorを指定)してやればよい。

val executor: Executor = Executors.newSingleThreadExecutor()
Tasks.forResult("test")
  .addOnSuccessListener(executor, OnSuccessListener { println(it) })

ちなみにそのままでは処理タイミングの問題で、リスナーの処理が呼び出される前にテストメソッドが終了してしまう場合がある。そのため、CountDownLatchなどを利用するなどして、リスナーの処理が呼び出されるのを待ち合わせたりする必要がある。

待ち合わせが必要になるのは、Instrumentation Testでも同じである。たとえTasks.forResult()を使って即座に処理が完了するTask<T>にリスナーを設定しようとも、タイミングによって呼ばれないことが起こりうるので、リスナーの呼び出しのテストを行う場合には必ず待ち合わせの処理を挟むことが必要。

Adaptive Icon

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

Adaptive IconをFigmaを使って作ってみた。

Adaptive icon

Adaptive Iconは、開発者側はアプリのアイコンとなる前景画像と背景画像の2種類だけを用意して、後のアイコンの形はOS(デバイス側)にまかせてしまうという仕組み(という理解を私はしている)。

今まではランチャーアイコンとして四角いやつと丸いやつの2種類を別途用意していたけども、これからは2種類の画像(前景と背景)さえ用意しとけば後はOS側(正確にはホームアプリ?)がアイコンの形をよしなに表示してくれる。

といってもAPI26からの機能なので、現実的には依然として普通のランチャーアイコンは用意しなければならない。

とはいえ今回Figmaを使ってアイコンを作ってみたが、25以下のための画像データを用意するのはそこまで手間だとは感じなかった。108dpでアイコンデータを作っているので、その画像をAndroid Asset Studioに持っていってランチャーアイコンを生成するだけだったので。

Figmaを使ってアイコンデータ作成

今回はFigmaを使ってアイコンデータを作成した。ちなみにベクターデータである。

作成したforeground画像とbackground画像を、Adaptive Iconとして表示した場合にどうなるかは、このツールを使ってシュミレーションしながら確認した。

Adaptive Icon非対応の端末用の既存のランチャーアイコンは、Figmaから書き出した画像データをAndroid Asset Studioを使って生成した。

こんな感じで作成。

Icons

これは重ね合わせた状態だが、前景と背景別々に作ってある。

Fore back

どちらの画像も108dp四方になるように、108dpの矩形を別途用意した上で、その上にアイコン要素を描画するようにしている。そうしないとエクスポートした際に108dpの画像として出力できないからである。

作成した画像はforegroundとbackgroundそれぞれをSVGでエクスポートして、そのSVGファイルをVector Drawableに変換してAndroid Studioへ持っていった。

Vector Drawableへの変換はこのツールを利用した。

複雑な形状だとうまく変換できないこともあるので、適宜調整が必要だろう。Vector Drawableとしてうまく変換できたとしても、パスデータが複雑すぎるという警告が出ることもある。あまり複雑すぎるのも考えものである。

ちなみにFigma用のAdaptive Iconのテンプレートがあったので、こちら
から利用させていただいた。

テンプレートのサイズはFigma上では432になっている。Adaptive Iconは108dpで作成するものだ。FigmaとかSVGの仕様とかに疎いのでよくわからないのだが、432のサイズでアイコンを作成してSVGでエクスポートしても問題ないのだろうか?

私はよくわからなかったので、アイコンサイズを108の大きさで作成した。

ちなみに432とか単位をつけずに書いているのは、Figma上での単位が分からなかったからである。432のままSVGエクスポートしてVector Drawableに変換すると432dpになってしまい、そこからXMLの数値を108dpに書き換えるだけで問題ないのかよくわからなかったので、最初からFigmaの段階で108のサイズで作成したというわけ。

ちなみにテンプレートは一旦フラット化してベクター情報に変換し、サイズを108に縮小してアイコン画像の位置調整のガイドとして利用させてもらった。

Adaptive Iconの対応状況

こうして作成したAdaptive Iconだが、これが表示できるかどうか使っているランチャー次第のようだ。

私の場合、実機Nexus6P(Android 8.1.0)に作ったAdaptive Iconのアプリをインストールしても、固定されたアイコンとしては表示されるものの、アニメーション(ぷるぷる動くような視覚効果)はしなかった。

Pixel Launcherだとぷるぷるするらしい。

私の使っているランチャーはGoogle Nowランチャーなので、Adaptive Iconにきちんと対応しているわけではないようだ。アイコンとして表示することはできるが、アイコンのシェイプを変えたり、アニメーションしたりしない。

試しに他のランチャーをインストールして試してみたところ、Adaptive Iconへの対応を謳っているランチャーであればAdaptive Iconがぷるぷるすることを確認した(私が試したのはNova Launcher)。

参考

3分で分かる?Android OのAdaptive Iconに対応しよう

Adaptive Iconのシミュレーションツール(Web)

Designing Adaptive Icons(各ツールにおけるテンプレートへのリンクあり)

SvgToVectorDrawableConverter.Web(SVGをVector Drawableに変換するツール)

アプリアイコンづくりにFigmaが便利でいいかもしれない

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

Sketch はもういらない? デザインツール Figma の紹介という記事を見て知って使ってみた。

ググラビリティが異様に低い(検索するとフィギュア関連のサイトのほうが多く引っかかる)ので、探すのにちょっと苦労する。

UIが英語オンリーなんだけど、それを言ったらAndroid Studioだって同じなので障害にはならないと思う。

私はSketchを持っていないし、チームでデザインなんてこともやったことがないので、そういった観点からこのツールを評価することはできないし、今回はそんなことを言うために記事を書いているのではない。

使ってみたらベクターツールが結構面白いのである。そんなに機能があるわけでもなく、シンプルなことしかできないのだけど、直感的にやりたいことができるというか。ちょっとしたアイコンを作ろうとするときなんかは、扱いやすくて便利なんじゃないかなと思う。

四角形を描画して、その四角形を元にアンカーポイント追加して形を微調整して・・・なんていうのがシンプルにできて、個人的に直感的な操作ができて便利だなぁと思ったのである。

アプリのアイコンを作成するくらいの用途であれば充分使える気がする。

私の場合、Windowsでタブレットを使って手書きで下書き、その画像をMacのAffinity Designerを使ってベクタデータにするなんて作業をしていた。FigmaだとWin-Macを行き来するのも楽でいい。

ちょっと気になるのは、Figmaはタブレットのペン操作に対応していないっぽいということ。タブレットのペンで操作すると、どんなツールを選んでいようともスクロール操作にしかならなくて困った。

ただ、アプリアイコン作るくらいの用途であれば、ベクタツールがシンプルでFigmaは便利なんじゃないかと思う。