作者別: Gen

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

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が便利でいいかもしれない

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

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

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

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

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

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

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

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

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

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

こう書いたらこう動くよねを確認するためにユニットテストを活用する

私はわりとカジュアルにユニットテストを使っている(と思う)。クラスやメソッドの振る舞いをテストするさらに前段階の状態で使っている。

「こういうコードを書いたらこうなると思ってるんだけど、合ってるよね?」というのを確認するためのテストコードをユニットテストに書いてしまうのである。ノイズにしかならないので、普通はこんな使い方しないと思うが、Androidの文脈においていちいち端末で実行して動作を確認するよりは早いと思っているので、個人的には便利に使っている。

実例

上記のリポジトリでは、ThreeTenAbpの使い方を確認するテスト用のコードなんかが散らばっているが、こんな感じでライブラリの動作を確認したり、最近だとRxJavaでOperatorを連結した処理の確認をしたりするのにユニットテストを利用している。

testディレクトリに適当なクラスを作って、そこでJUnitスタイルで動きを確認したいコードを書くだけである。

私はKotlinでコードを書くことが多くなってきているので、Kotlinの拡張関数の動きを確認するためにユニットテストを活用していたりする。例えば空文字をsplit()したときにサイズがどうなるのか、なんてのを確認したりするわけだ。

class StringSplitTest {
  @Test
  fun split_empty() {
    val test = "".split("\t")
    test.size.should.equal(1)
  }
}

このコードではexpektというライブラリを使ってアサーションを行っている。

実装を追えよという話ではあるのだけど、ふとした疑問をさっと確認するのには便利だと思うのだけどどうだろうか。わざわざ実機なりエミュレータ上で実行して、デバッガで確認して・・・なんてするよりは絶対に早い。

たぶんチームで開発しているとかいう状況だと、こんなテストコードが残されると邪魔でしかないと思うが、個人的に確認する分には弊害はないだろう。消すのを忘れてコミットしちゃうおそれがあるとか、そんなレベルの話。

私の場合はぼっちで開発しているので、こういうレベルでもテストコードを書いていれば、ユニットテストのやり方の勉強にもなっていいかなと思っている。

KotlinでUnitテストでassertionに何を使うか

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

KotlinでUnitテストを書く際に、assertionに何を使うかという話。

私の場合特にこだわりがあるわけではないのだが、基本的には多数派に従いたいという気持ちが強い。しかしながら、Kotlinのテストのassertionに使うならこれ一択、みたいなものがない(と思っている)ので、いつも困る。

Javaなら特に何もせずともhamcrestのassertThat(sut.getHoge()).is("fuga")みたいなassertionを使っていればいいのだが、Kotlinの場合はisが予約語であるという問題がある。いちいちisをバッククォートでくくらなければならず美しくない。

knit

https://github.com/ntaro/knit

日本ではいちばん有名なやつだと思われる(DroidKaigiのアプリでも使われていたはずなので)。

sut.getHoge().should be "fuga"みたいな感じで使う。

ただrepositoryを追加しないと組み込めないので私はあまり使っていない。build.gradleに一行追加するだけの話なんだけどね・・・。

expekt

https://github.com/winterbe/expekt

knitに似たsut.getHoge().should.equals("fuga")みたいな感じのAPI。リポジトリの追加が必要ないので、私はこっちをよく使っている。

ただメンテされてないのが玉に瑕。

kotlintest

https://github.com/kotlintest/kotlintest

assertionライブラリではなくて、Spec風のテストを書くためのライブラリ。

ただsut.getHoge() shouldBe "fuga"みたいなassertionが含まれている。

SpekというSpec風のテストを書くためのライブラリがあるが、こちらはJava8でなければ使えないという制約があって、Androidのプロジェクトに適用するのはなんだか難しそうで手を出していない。

kotlintestはネストしたテストを書きやすくて、これいいかもとか思ったこともあったのだけれど、androidTestに組み込むとメソッドカウントがオーバーしてしまうので使えない。

assertk

https://github.com/willowtreeapps/assertk

Android Weeklyで流れてきて知った。AssertJチックなassertionライブラリで、Kotlinで使うならコレのが便利なのだろうか。

assert(sut.getHoge()).isEqualTo("fuga")みたいな感じで使う。

しかし個々最近はshould系ばかり使ってきているので、最初にassertを書かないといけない形式はめんどくさいと感じてしまっている。

AssertJ

http://joel-costigliola.github.io/assertj/

Javaのコードも考慮に入れるならJavaで使えるライブラリを使うのがいいのだろう。

私はJavaだとhamcrest使っとけばいいやな人だったので、Javaでassersionライブラリを何使うかなんて特に気にしたことはなかった。

みんなが使っているものが知りたい

みんなはどれを使っているのだろうか。他にこれが使いやすいみたいなのがあったら教えて欲しい。

私はexpektを使う傾向が強いが、揺らいでいる。

正直なところassertionさえできればなんでもいいのだろうからして、自分が使いやすいものを使えばいいってだけの話なのだろうけれども。

https://discuss.kotlinlang.org/t/what-assertions-library-do-you-use/1809

ここをみると、kotlintestが多いっぽい。

Instant-appを試してみた

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

Instant-Appを試してみた。FlexibleTimerというアプリを作って、Instant-Appに対応させてみたのである。

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

Instant-Appというのは、アプリをインストールすることなく使えるようにする仕組みで、今回作ったアプリはhttps://app.gcreate.jp/flexibletimer/にアクセスすれば試すことができる。(Androidからアクセスすれば実行できるはず)

Instant-Appを実行できる環境

今のところAndroid6.0以上の端末であれば動くらしい。

Android6.0(API23)以上の端末であれば、おそらく設定 > Googleの中にInstant Appsという項目があると思う。それを有効にすればInstant-Appが実行できる。将来的にはAPI21以上もサポートされるらしいが、今は23以上が要件。

1つ注意点があって、Instant-Appはどうもどれか1つのアカウントでしか有効にできないみたい。私は2つのアカウントを1つの端末で使っていて、片方のアカウントでInstant-Appを有効にすると、もう片方は無効になってしまう。

このことで何を注意しなければならないかというと、Chromeにログインしているアカウントと、Instant-Appを有効にするアカウントは揃えておけということである。揃えていないとChromeからInstant-Appを提供しているURLにアクセスしても、Instant-Appが実行されないからである。揃えていなくても、例えばSlackで共有されたURLを開けばInstant-Appが実行されるし、Google Playからサイトに移動しても起動するので、Instant-App自体が使えないわけではない。

用意したURL(これはアプリを作成する際にAndroidManifest.xmlに定義する)にアクセスすれば、Instant-App用のAPKをGoogle Playからロードしてきて、インストールすることなくアプリが実行される。

URLは実際にアクセス可能でなければならない。つまり、Webサイトを所有していない場合には使えないということになる。所有していない場合はFirebase Hostingを利用してねというQAがある。(Stackoverflow

Instant-Appへの対応の仕方

今のところAndroid Studio3.0を使わなければInstant-App対応は不可能である。理由としては、3.0でなければInstant-Appを作成するために使うcom.android.featureプラグインが認識できないからだ。

まだ3.0は正式バージョンではないので、急いで対応しないとならないというものではないと思う。

既存のアプリをInstant-Appへ対応させるのは、そんなに難しい手順が必要なわけではない。たぶん、3.0でビルドできなくなったとか、gradleプラグインが対応してなくて動かないとか、そういった種類のトラブルに対応するほうがはるかに大変なだけだと思う。

細かいやり方はドキュメントを見てもらえばそんなに難しくはないと思う。

ちなみにcodelabで既存のアプリをInstant-App対応させる方法が一通り学べる。

はまったポイント

foreground serviceが動かない

Instant-AppではLong-running background serviceはサポートされていないが、foreground serviceは使うことができる。ことになっているが、現時点ではまだ対応されていないらしい。

Stackoverflow

Architecture Componentが動かない

私の場合LiveDataを使おうとしていたのだが、Instant-Appでは使えなかった。エラーは起きないが、LiveDataの変更が受け取れなかった。

https://issuetracker.google.com/issues/38493434

Content Providerの仕組みで初期化してるから、Content Providerが使えないInstant-Appでは動かないのではないのかということらしい。

しょうがないので、RxJava2のBehaviorSubjectに置き換えて対応した。

App Linksの設定

私はApp Linksにこれまで対応したことがなかったので、Android StudioのApp Links Assistant任せでやっていたのだが、App Linksについて、仕組みとか対応の仕方とかをちゃんと調べてからやったほうがハマりが少ないのではないかと思う。

特にAndroidManifest.xmlの記述に関してはちゃんとドキュメントに書いてあるので、App Links Assistantに任せるのではなく、ドキュメントを見ながら設定するように。ビルドすること自体は問題なくできるが、Google PlayにAPKをアップロードしたときにはじめて弾かれるので、設定ミスに気づくのが遅くなってしまった。

https://developer.android.com/topic/instant-apps/prepare.html#default-url

私の場合、1つのintent-filterにhttpsとhttpの両方を設定すること、<meta-data>タグでdefault-urlの設定をすることが漏れていて、無駄な時間を過ごしてしまった。

サイト側の設定

私の場合はhttps://app.gcreate.jp/flexibletimer/というURLの、Webサイト側の設定をどうするかという話である。

必須なのはhttps対応することと、有効なドメインを持つこと、後は該当サイトにアクセスできるようにWebサーバを用意することだ。

Instant-AppのためにはApp Linksの対応が必要なので、https://developer.android.com/training/app-links/index.htmlを確認しながら設定すると良い。

Instant-AppのAPKはGoogle Playに置くだけであり、サイト側ではドメイン直下に.well-known/assetlinks.jsonファイルを配置するだけである。

ただこのApp Linksの設定は、Instant-Appの実行に関してはあまり意味は無いのだと思う。これはあくまでApp Linksの設定だから、インストールしたアプリがある状態で、指定したURLにアクセスしたときにアプリが起動するようにするための設定だと思うので。

しかしそうすると、なぜ指定したURLにアクセスするとInstant-App用のAPKがGoogle Playから読み込まれるのかがよくわからない。

仕組みがよくわからないので、どう設定したらどう動くのかが知りたくて、今回試してみたわけである。

とりあえず、用意したURLにアクセスすればGoogle PlayにアップロードしたInstant-AppのAPKが読み込まれてアプリが実行されるという仕組みのようで、Webサイト側は特別な設定は必要なかった(App Links用のjsonファイルを用意するくらい)。

現状ではcanaryバージョンのAndroid Studio3.0を使うので、Instant-Appがどうのというより、Android Studioのバグに振り回されることのほうが多かった気がする。

Instant-Appは利用者同士がコンテンツを共有するようなタイプのアプリの場合、特に効果的だと思う。むしろ今回私が作ったような、アプリ単体で完結してしまうようなアプリの場合、Instant-Appである意味はあんまりないと思った。アプリ内でやり取りされるコンテンツを、アプリをインストールしていないユーザに対して簡単にアクセスできるようにするのがInstant-Appの強みだと思う。

Architecture Componentを触ってみた

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

とりあえず軽く触ってみた。

codelab

Google IOの動画はとりあえず2つ見てみた。

最初のやつは「こんなん作ったでー」という話で、2つ目がその中身を説明っていう感じなので、時間がないなら2つ目だけ見ればいいんじゃないかなと思う。全部英語なので、私の英語力では雰囲気しかわからず時間対効果はあまり良くなかった気がしている。

3つ目もあるのだけど、これはまだ見れていない。タイトルから見るとRoomについての説明なのだろうか。

個人的にArchitecture Componentで気になっていたのは、いかにActivityのライフサイクルに振り回されなくてすむようにできるかという部分だ。つまり、LifecycleOwnerLiveDataViewModelについてである。とりあえずその観点で言うと、最初にあげたcodelabを触れば、雰囲気はわかった。2つ目の動画でだいたいスタンスがわかったような気がしている。

たぶん2つ目の動画で話していたと思うのだけど、このArchitecture Componentはすでに個々の開発者がそれぞれの工夫でActivityなどのライフサイクルによる呪縛を回避している手段を置き換えるためのものではないという話が、個人的にはしっくり来た。すでにライフサイクルとの付き合い方がうまくできている人は、別にそれでいいと。

ただ、Androidをこれから学ぼうとする人にとっては、Android特有のライフサイクルにまつわるあれこれは、学習していく上でつまづきやすいポイントで、さらにそれを回避するためのライブラリの使い方を学ぼうとすると余計にややこしくなってしまう。そこで、これからAndroidを学んでいく人にとって、とっつきやすいシンプルな仕組みを用意したよ、というのがArchitecture Componentということらしい。

私は最近ではDaggerやRxJavaを使って、Activityにはデータを持たせない、単に表示するだけのものとして扱うようにしてアプリを作るようにしている。そういった方法ですでにうまいこと回せているなら、無理して移行する必要はないのだろう。すでにうまいことやっている人にとっては、ちょっと触れば雰囲気がつかめるだろうから、とりあえずcodelabだけ触って雰囲気を掴んでおいて、1.0が出るのを待つくらいのスタンスでいいんじゃないかなと思う。

以下は触ってみた感想。

LifecycleOwnerとは

ざっくりした理解で言うと、ActivityとかFragmentとかServiceとか、Android特有のライフサイクルをもってるオブジェクトのことという認識でいる。

LiveDataの購読を行う際に引数に指定してやったり、ViewModelの生成・取得を行う際に引数に渡したりするのに出てくる。

LiveDataの購読に関しては、LifecycleOwner(Activityとか)のライフサイクルにあわせて自動的に購読解除してくれるらしい。つまり、いちいち自分でunsubscribe/disposeとかしたりしなくていいっていうこと。

またActivity等のライフサイクルにあわせた処理を行うのに利用したりできるっぽい。(codelabではLocationManagerから位置情報を受け取るクラスを作って、Activityのライフサイクルにあわせてセンサーの登録と解除を行うのに使っていた)

LiveData

UIに表示したりする実際のデータ。DataBindingでいうObservable<Hoge>みたいなものだし、RxJavaでいうObservable<Hoge>みたいなもの。

その実態は最新の値を保持してよしなに通知してくれるデータホルダー。RxJavaみたいなストリームではないよということだ。またスレッドの概念も持ってないので、バックグラウンドで処理してメインスレッドで通知みたいなことはしない。

LiveDataの更新はメインスレッドでないとできないみたいで、別スレッドからLiveData.setValue()したら落ちた。別スレッドから値を更新したい場合は、postValue()を使うらしい。

ActivityなどのViewは、このLiveDataを購読して、変更を受け取ったらUIを更新することだけ考えるような作りにするのが良いのだろう。

RxJavaでいうBehaviorSubjectみたいな動きをするなぁという印象を持った。

ViewModel

画面回転してもライフサイクルが継続してくれるもの。これが最初からあればAndroidアプリ開発はもっと楽になっていただろうと思う。

今までActivityにもたせていた状態やロジックを、全部こっちに持ってくればうまいことできると思う。

Activity.finish()を呼び出したらViewModelのライフサイクルも終了する(ViewModel::onCleard()が呼び出される)。

画面を回転させた場合はそのまま以前の状態を引き継いだものが、再生成されたActivityに渡ってくる。ただ、あくまでonConfigurationChangeで破棄されないだけなので、ホーム画面に一度移動した後にOSによって終了されたりした場合(Activityを保持しないが有効になってたりした場合)はViewModelのライフサイクルも終了してしまう。

データの永続化は別途考えないといけない。

軽く触ってみて

RxJavaなどの知識を持っているからか、割りとすんなり使えそうな気がしている。

それを抜きにしてもそんなにややこしくないと思うので、Androidをこれから学ぼうという人はとりあえずArchitecture Componentの使い方を学ぶと、シュッと入門できていいんじゃないだろうか。

触る前は「なんかいろいろあってややこしそうだな」とか思っていたのだが、触ってみると思いの外それぞれ独立していて、使いたいコンポーネントだけ利用すればいいという意味がよくわかった。

私個人としては、ViewModelはすぐにでも使いたい。Daggerを使って似たようなことをやっていたけれども、ViewModelを使うほうが楽だ。LiveDataはもうちょっと調べて、DataBindingとの組み合わせ方を掴んだら置き換えるかもしれない。

スワイプで削除できるRecyclerViewを実装するときの悩み

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

RecyclerViewを使うときに必ず実装するであろうRecyclerAdapter。List<Hoge>をRecyclerViewに表示するのに使う。

単にリストを表示するだけならあまり迷わないのだが、プラスアルファの処理を行う必要が出てきたときに私はよく悩む。例えば、現在進行形でもにょっているのが、リストのアイテムををスワイプしたらそのアイテムを削除したいというケース。とりあえず実装して動いてはいるのだが、削除に関するコントロール処理をどこに書くのが適切なのだろうかという疑問に対する明快な解を持ち合わせていない。

最近触っていないけど、FilteredHatebuというアプリでは削除に関する処理をPresenterに担わせた。Adapterは単にList<Hoge>とRecyclerViewの橋渡しをするだけというシンプルな作りだ。

一方で、現在作っているアプリではAdapterで削除に関する処理を行っている。この2つの違いがなぜ生まれたかというと、AdapterがList<Hoge>を持っているかどうかという問題に行き着く気がする。

RecyclerViewやListViewを使うとき、ネットで見かけるコードではAdapterにList<Hoge>を持たせるものをよく見かける。コンストラクタを使って渡すなり、セッターを使うなりして、AdapterにList<Hoge>をセットしてやる手法だ。単に表示するだけならこれで問題はないのだが、削除に関する処理を行おうとすると混乱し始める。

削除処理はList<Hoge>のアイテムを削除する処理を内包する。RecyclerViewの2番めのアイテムがスワイプされたら、List<Hoge>の2番めのHogeを削除しないといけない。ではその削除を実行するのは、AdapterなのかそれともActivityなのか、それとももっと他のもの(例えばPresenter)なのかがよくわからない。

List<Hoge>の操作が必要なのだから、List<Hoge>を管理しているものが削除すれば良い。となったときに、AdapterがList<Hoge>を持っていることが多いので、そのままAdapterに削除処理を実装することが多いのである。

削除可能なRecyclerViewの実装について、ベストプラクティスが知りたい。そして知りたいと思ったときに、ふと「そもそもAdapterにList<Hoge>を持たせるのはどうなんだろうか」と疑問に感じたのである。

私の理解では、AdapterはList<Hoge>とRecyclerViewの橋渡しをするもの、つまりHogeクラスを表示するためのViewに変換するのがその責務という認識だ。その認識からすると、AdapterにList<Hoge>をもたせて削除に関する処理が加わっている今作っているAdapterは、AdapterではなくてControllerになってる気がする。

そんなことを考えていると、そもそもRecyclerViewでアイテムをスワイプして削除させるのが間違っているのではないかという気分にもなってくる。別にAdapterにどれだけの責務をもたせるかは、開発者のさじ加減であって、個人の好きなようにしたらいいのかもしれない。

そんな堂々巡りのはて、まあ動けばいいかという結論に落ち着く。削除可能なRecyclerViewの実装、みんなはどうやっているのだろう。