もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

DroidKaigi 2016に行ってきた 2日目

一日目

rei19.hatenablog.com

ということで2日目も行ってきました。2日目も知見にあふれていて最高でしたね!各セッションの資料はこちらのブログであらかたまとめられていたので、今回は参加したセッションと感想を少しだけ残しておこうかなっと思います。

unsolublesugar.com

unsolublesugar.com

参加セッション

第1部「Support Libraryノススメ」 + 第2部「Support Library Internal」 @yuichi_araki さん

  • Googleのエンジニア @yuichi_araki さんによるSupport Libraryの解説でした。Androidに触り始めのころに思った疑問がかなりすっきり。基本的にSupportLibraryを使えば良いということがわかったのは収穫でした。

生まれ変わったUI Automatorを使いこなす @sumio_tym さん

  • UIまわりのテストの知見が少なかったのでこちらを選びました。UIのテストは基本はEspressoを使ってそれでカバーできないところはUI Automatorを使うとよいとのこと。自分のアプリでもテスト書かないとなあ。

Advanced Kotlin for Android @ngsw_taro さん

  • 最近、個人的にホットなKotlinのセッション。Kotlinでアプリを一本書いたけど、後半のExtensionを使ったハックはそんな書き方あるんだ!という感じで参考になりました。Kotlinは書いてて気持ちいいんですよね。

  • ぽろっとTwitterで聞きたかったこと流したら速攻で反応あってびっくりしたw

Android実プロダクトへのKotlinの導入事例 @boohbah さん

  • 連続でKotlinのセッション。こちらは実際にJavaからKotlinに書きなおした事例ということでした。移行の中でハマったことなどが紹介されていて、前のセッションとは違った知見が得られましたね。個人的にはすでにJavaで最適化されているアプリはあえて書きなおさなくてもいいかなーと思っている派ですけど、でもKotlinで書きたい。。。

Androidエンジニアになって2年の学び @ryugoo_ さん

  • 僕もAndroidさわって1年ちょいで割と同じような環境だったので、わかるわ~と思いながら聞いてました。一人プルリクからのセルフレビュー、マージをえんえんと繰り返す感じですね。わかります。規約類は充実させる前にそのプロジェクトは抜けてしまったけど、きっと同じようなことをしてたんだろうな。

Fireside chat @公式アプリのリリースにかかわったみなさん

  • 今回のDroidKaigiの公式アプリに関わった方々でディスカッション。とりあえずIssueたてたらPRが速攻来たみたいなエピソードを聞いてただただOSSすごいって感じました。公式アプリ自体、腕の立つエンジニアたちの知見が集まっていて超勉強になりました。来年は自分もPR送りたいなあ。あとKotlinで書きなおしてみようとも思ったりしました。

まとめ

  • 控えめにいって最高でした。去年は開発が佳境だったこともあり参加できなかったのですが、今年は参加できて本当に良かったと思いました。セッションを聞いて、へー、なるほどすごい!で終わらせるのではなく、実際の開発に活かしたり自分の中の知識として使えるようになるまでがDroidKaigiかなという認識なので、インプットからのアウトプットを心がけるようにします。

  • とりあえず帰ってから書いたもの

qiita.com

  • こっちはKotlinのセッションで聞いた内容をちょっとだけ反映

github.com

DroidKaigi 2016に行ってきた 1日目

Androidの開発者向けのお祭り DroidKaigi に参加してきたレポ。去年は行けなかったけど、今年は参加できた!写真撮ろうと思ったけどスマホの充電器を忘れてしまってご臨終な感じだったのでほとんどテキスト。。。

参加セッション

基調講演 : OSSの動向を捉えた実装方針 @wasabeef さん

  • CyberAgentさんで作っているAndroidアプリで使っているライブラリの話を中心に、昨今よく使われるライブラリを紹介。Rxでリスト操作してるところはStreamに置き換えた方がいいかな。Androidはこの辺のライブラリを知っているか知らないかで作りが変わるので聞けてよかったです。

Master of Canvas @amyu_san さん

  • Canvasを使った面白いアニメーションの作り方の話。Androidはアニメーション苦手なイメージあったけど、いろいろ出来るんだなということがわかってよかったです。ちょうど仕事で必要そうな感じでタイムリー。

Android Lintで正しさを学ぼう @Nkzn さん

  • 割と初学者向けの内容で、あー同じことやったわwという内容でした。./gradle lint でlintかけられるの知らなかったので自分のGradle力低いな。。。と反省。仕事で作ってるやつにとりあえず流してみようかなと思います。ちょっとコワイ。

Android Dev Tools Knowledge @operandoOS さん

スライド上がってなかったので雑なメモ

# 便利なコマンド
- 簡単にadbの環境を用意する
    - brew install android-sdk (caskかも?)
- adb-peco
    - 複数のAndroid端末を同時に繋いでるときに便利
- input text
    - adb shell input text droidkaigi
    - adb shellでどのエミュに繋ぐか選べるので↑のコマンドで選択した端末にテキストを送りこめる
- dumpsys
    - system serviceをdumpできる
    - adb shell dumpsys | grep "DUMP OF SERVICEIS"
    - adb shell dumpsys activity top でViewの構造やら見える
    - adb shell dumpsys activity activities | grep apk
- settings
    - 設定にどんな値が入っているか確認できる
- screenrecord
    - 画面録画機能
- systrace
    - パフォーマンスを見るためのツール
    - HTMLでレポートを出力できる
- atrace

- Android-Command-Note
    - いろいろのっけてる
- AndroidSHell
    - もうちょっと細かく載っている
- dexcount-gradle-plugin
- gradle-version-plugin
    - 使っているライブラリの更新をチェックしてくれる
    - gradle dependencyUpdates
- build-time-tracker-plugin
- gradle-android-command-plugin

# Android Studio Plugin
- Android wifi adb
- ADB Idea
    - studio内からadbコマンド打てる
- adb commander for android
- eventbus-intellij-plugin
- android-postfix-plugin
- Google Developers color scheme

# その他のツール
- androidtool-mac
- tonkotsu
- vysor
    - 端末のミラーリングしてくれる
- Android SDK Search
- DPI Calculator
- Android Resource Navigator
- Material Terminal
    - アプリの中でshell使える
- materialdoc.com
    - 公式ページのサンプルをアプリにした
- DesignOverlay
    - 神ツールっぽい
- View Debug

# どこでこの情報を集めるか
- コード読む
- Google+を覗く
- Twitter
- GitHub
  • adb系のコマンドやgradleのコマンドをひたすら紹介する内容でしたがこの辺が勉強不足な僕的にはすごいよかったです。少しずつ使いこなしていきたいですね。

Dagger2とRealmを利用したモダンな開発 @experopero さん

  • RealmとDagger2で実際の運用での使い方を聞けたのはよかったです。Dagger2はいまいち正解がわからない。。。とりあえずSubComponent使ってみようかと思いました。

Androidの省電力について考える 中西 良明さん

スライド上がってなかったので雑なメモ

# 電力使用量削減の歴史
- Android 6.0で大変革が起こった
    - 5系までは開発者側に省電力の仕組みの実装が委ねられたが6系から標準な仕様として実装された
- 以前は通信キャリアごとで対策が取られていた
    - 端末メーカーレベルでもあった
- Job Scheduler(5.0〜)
- Fused Location Provider

# 6.0からの話
- 強制的に電力消費を抑える仕組み
    - Doze
        - 一定の条件を満たすとDozeモードに入る。たまに起きる
        - setAlermClockはDozeを突破できる。が、推奨されなさそう
        - GCMはNWが閉じるので受け取れないが受け取れるようにする指定もある
    - App Standby
        - アプリが一定期間使われていないとアプリはスタンバイ状態に置かれる
        - スタンバイになったアプリは1日1回しかネットーワークに繋がらなくなる
- 電力的に行儀が悪いとStoreから落とされる可能性がある
- 5.0以上はJobSchedulerを以下はAlarmManagerを使うとよいのでは。めんどくさかったらAlarmManagerだけでも。
- adbを使うことで強制的にDozeモードにすることができる
  • この辺、あまり消費電力を意識したことないので聞けてよかったです。とりあえずAlarmManagerのAPIから調べてみよう

実践!Android Studioプラグイン開発 @konifar さん

スライド上がってなかったので雑なメモ

# プラグインの話
- IntelijでJava6開発
- pluginのプロジェクトを作成
- Eventに応じたActionクラスを作成
- 基本的な導入はQiitaに投稿したぞい
    - 簡単なことはすぐできる

# 実践的な話
## 覚えておくべきクラスと役割
- AnActionEvent
    - プラグインのActionに関連するさまざまな情報を扱うクラス
- Project
    - プロジェクトの情報がとれる。例えばSDKのバージョンによって生成するコードを変える。。。ということなど
- Editor
    - Editorの情報を取ることができる。選択中のテキストを取得するなど
- Document
    - EditorからDocumentを取得する。リスナーなど仕込めて、入力に応じてなんかさせる、などできる
- PsFile

### COmponent
- Dialog
    - DialogWrapperを継承して使う
- Editor Hint
    - HintManager
- Notification
    - Notification.Busを使ってNotifyする。これは更新確認で使ったりするのでデファクトで入れる

## 悩んだ時の調べ方
- 公開されているコードを読むのが早い
- indellij-sdk-docs
    - 基本的にはこれを見ると大丈夫そう
- android-parcelable-intellij-plugin
    - Parcelableを自動生成するぷらぎん
- android-drawable-importer-intellij-plugin
    - 実践的な内容は全て詰まっている
- adb-idea
    - adbコマンドをAndroid Studio上から簡単に実行できる
- Android Studio Code
    - 最終兵器。GitHubには上がっていないが別の場所にある

# まとめ
- 基本的なクラスは覚えておく
- わからないときは似たコードを読んでみる
  • アプリ作るとき、Koniferさんのプラグインめっちゃお世話になってるので、自分でも何か作れるようになりたいなーと思って聞いたけど、事前知識を仕入れることができてありがたかったです。おそらく知らないで突撃したらすごい時間かかりそうな感じ。。。

  • 余談

2日目に続く。。。

  • スタッフの方々お疲れ様でした。明日もよろしくお願いいたします。

ストレングスファインダー(Now, Discover your strength)をやってみた

会社の開発チームでみんなでやってみようという話になったのでテストしてみました。

元ネタはこちら

さあ、才能(じぶん)に目覚めよう―あなたの5つの強みを見出し、活かす

さあ、才能(じぶん)に目覚めよう―あなたの5つの強みを見出し、活かす

  • 初版は2001年となっているので割と古典ぽい感じですね。簡単に紹介すると、才能知識技術が組み合わさることで強みが生まれ充実したライフを送ることが出来るよ!という話で、ストレングスファインダーはそのうちの才能を明らかにするためのツールという感じです。簡単なテストに答えていくいことで34に分類された資質の中から自分にあてはまる上位5個がわかります。なお、テストを受験するためには本の末尾についている受験コードが必要になるので中古で買っても本は読めるけどテストは受けられない可能性が高いです。よくできている。

やってみた結果

最上志向

優秀であること、平均ではなく。これがあなたの基準です。平均以下の何かを平均より少し上に引き上げるには大変な努力を要し、あなたはそこに全く意味を見出しません。平均以上の何かを最高のものに高めるのも、同じように多大な努力を必要としますが、はるかに胸躍ります。自分自身のものか他の人のものかに関わらず、強みはあなたを魅了します。真珠を追い求めるダイバーのように、あなたは強みを示す明らかな徴候を探し求めます。生まれついての優秀さ、飲み込みの速さ、一気に上達した技能――これらがわずかでも見えることは、強みがあるかもしれないことを示す手がかりになります。そして一旦強みを発見すると、あなたはそれを伸ばし、磨きをかけ、優秀さへ高めずにはいられません。あなたは真珠を光り輝くまで磨くのです。このように、この自然に長所を見分ける力は、他の人から人を区別していると見られるかもしれません。あなたはあなたの強みを高く評価してくれる人たちと一緒に過ごすことを選びます。同じように、自分の強みを発見しそれを伸ばしてきたと思われる人たちに惹かれます。あなたは、あなたを型にはめて、弱点を克服させようとする人々を避ける傾向があります。あなたは自分の弱みを嘆きながら人生を送りたくありません。それよりも、持って生まれた天賦の才能を最大限に利用したいと考えます。その方が楽しく、実りも多いのです。そして意外なことに、その方がもっと大変なのです。

学習欲

あなたは学ぶことが大好きです。あなたが最も関心を持つテーマは、あなたの他の資質や経験によって決まりますが、それが何であれ、あなたはいつも学ぶ「プロセス」に心を惹かれます。内容や結果よりもプロセスこそが、あなたにとっては刺激的なのです。あなたは何も知らない状態から能力を備えた状態に、着実で計画的なプロセスを経て移行することで活気づけられます。最初にいくつかの事実に接することでぞくぞくし、早い段階で学んだことを復誦し練習する努力をし、スキルを習得するにつれ自信が強まる――これがあなたの心を惹きつける学習プロセスです。あなたの意欲の高まりは、あなたに社会人学習――外国語、ヨガ、大学院など――への参加を促すようになります。それは、短期プロジェクトへの取組みを依頼されて、短期間で沢山の新しいことを学ぶことが求められ、そしてすぐにまた次の新しいプロジェクトへに取組んでいく必要のあるような、活気に溢れた職場環境の中で力を発揮します。この「学習欲」という資質は、必ずしもあなたがその分野の専門家になろうとしているとか、専門的あるいは学術的な資格に伴う尊敬の念を求めていることを意味するわけではありません。学習の成果は、「学習のプロセス」ほど重要ではないのです。

内省

あなたは考えることが好きです。あなたは頭脳活動を好みます。あなたは脳を刺激し、縦横無尽に頭を働かせることが好きです。あなたが頭を働かせている方向は、例えば問題を解こうとしているのかもしれないし、アイデアを考え出そうとしているのかもしれないし、あるいはほかの人の感情を理解しようとしているのかもしれません。何に集中しているかは、あなたのほかの強みによるでしょう。一方では、頭を働かせている方向は一点に定まっていない可能性もあります。「内省」の資質は、あなたが何を考えているかというところまで影響するわけではありません。単に、あなたは考えることが好きだということを意味しているだけです。あなたは独りの時間を楽しむ類の人です。なぜなら、独りでいる時間は、黙想し内省するための時間だからです。あなたは内省的です。ある意味で、あなたは自分自身の最良の伴侶です。あなたは自分自身にいろいろな質問を投げ掛け、自分でそれぞれの回答がどうであるかを検討します。この内省作業により、あなたは実際に行っていることと頭の中で考えて検討したことと比べた時、若干不満を覚えるかもしれません。あるいはこの内省作業は、その日の出来事や、予定している人との会話などといったような、より現実的な事柄に向かうかもしれません。それがどの方向にあなたを導くにしても、この頭の中でのやりとりはあなたの人生で変わらぬもののひとつです。

目標志向

「私はどこに向かっているのか?」とあなたは自問します。毎日、この質問を繰り返します。目標志向という資質のために、あなたは明確な行き先を必要とします。行き先がないと、あなたの生活や仕事はたちまち苛立たしいものになる可能性があります。ですから毎年、毎月、さらに毎週でさえ、あなたは目標を設定します。この目標はあなたの羅針盤となり、優先順位を決定したり、行き先に向かうコースに戻るために必要な修正をする上で、あなたを助けてくれます。あなたの目標志向は素晴らしい力を持っています。何故ならそれはあなたの行動をふるいにかけさせるからです。――すなわち、特定の行動が目標へ近づくために役に立つかどうかを本能的に評価し、役に立たない行動を無視します。そして最終的に、あなたの目標志向はあなたを効率的にさせるのです。当然ながらこの裏返しとして、あなたは遅れや障害や、例えそれがどんなに興味深く見えようとも本筋から外れることにいらいらするようになります。このことは、あなたを集団の一員として非常に貴重な存在にしています。他の人が脇道にそれ始めると、あなたは彼らを本筋へ連れ戻します。あなたの目標志向は、目標に向かって進むために役に立っていないものは重要ではないということを、あらゆる人に気付かせます。そしてもし重要でないなら、それは時間を割く価値がないということです。あなたは、あらゆる人を進路から外れさせません。

戦略性

戦略性という資質によって、あなたはいろいろなものが乱雑にある中から、最終の目的に合った最善の道筋を発見することができます。これは学習できるスキルではありません。これは特異な考え方であり、物事に対する特殊な見方です。他の人には単に複雑さとしか見えない時でも、あなたにはこの資質によってパターンが見えます。これらを意識して、あなたはあらゆる選択肢のシナリオの最後まで想像し、常に「こうなったらどうなる? では、こうなったらどうなる?」と自問します。このような繰り返しによって、先を読むことができるのです。そして、あなたは起こる可能性のある障害の危険性を正確に予測することができます。それぞれの道筋の先にある状況が解かることで、あなたは道筋を選び始めます。行き止まりの道をあなたは切り捨てます。まともに抵抗を受ける道を排除します。混乱に巻き込まれる道を捨て去ります。そして、選ばれた道――すなわちあなたの戦略――にたどり着くまで、あなたは選択と切り捨てを繰り返します。そしてこの戦略を武器として先へ進みます。これが、あなたの戦略性という資質の役割です:問いかけ、選抜し、行動するのです。

結果を見て

  • チーム内で発表したら、わかるわ~という評価が多かったです。自分でも上位3つは、なるほど確かになと思う点が多々あったり。家でぼっちでもくもくコード書いてたりするのは好きなので、今の仕事というか生き方はある程度は自分の資質ともマッチしてるんだなあという印象です。朝活でコード書いたり本読んだりしてますよーとか話するとストイックですねって言われることあるけど、自分ではそうなの??という感じなので、そういう生活は自分の資質とマッチしているから苦に感じていないという話なのでしょうね。人によっては自分では思っていなかった資質が結果に出たので、チームでやって公開しあうとおもしろいかもしれません。リーダークラスの人はメンバーのストレングスファインダーを見て資質にあったタスクを割り振るようにしたら効果が出たりするのかな。なかなか面白かったので興味ある方はぜひ。

2015年を振り返る

仕事

  • 5月くらいまでAndroid
  • 10月までC#
  • 11月転職
  • 転職先でまたAndroid

プライベート

  • 特に変わったことは起きず。
  • たまに逆流性食道炎っぽい症状が出るのも直ってない。
  • Prime Videoを使い始めたことで我が家のPS4さんが本気出しはじめた。
    • 今はシュタゲ専用機となっている。

勉強の話とか

  • 3年前くらいから何かしら仕事に関係ある/ないに関わらず新しい言語/技術に挑戦してアウトプットを出すということを細々と続けているのですが、今年はElixirとKotlinを触って、それぞれ何かしらの形でアウトプット出せるとこまでいけました。

github.com

HBFav Material はてブを流れるように見るアプリ - Google Play の Android アプリ

  • 今年読んだ本で覚えてるのはこんな感じ

How Google Works (ハウ・グーグル・ワークス)  ―私たちの働き方とマネジメント

How Google Works (ハウ・グーグル・ワークス) ―私たちの働き方とマネジメント

総括

  • 今年は転職したのが一番大きなトピックでした。5、6年働いた環境を離れて新しい環境に飛び込んだわけですが、考え方とか近い人が多いし自分にはないものを持っている人も多いので学びが多い。ストレスなく楽しくやれています。
  • 来年もしばらくAndroidに触れていそうなので、もっとベースのスキルを磨いていきたい。まだAndroid触り始めて1年くらいなので色々足りないところが多いですね。
  • ちょっとインプットが少なかったかなという印象なので来年はGitHubに草を生やしつつ、幅広い本を多めに読みたいかなと。
  • 来年の目標はまだ決めてないけど、とりあえずSwiftを始めようかなーとは思います。フロントに寄せながら広くスキルを広げていくという成長曲線を目指します。

Kotlinで書いたHBFav MaterialというAndroidアプリをリリースしました

タイトルのとおりですがKotlinで書いたAndroidアプリをリリースしました。ことりんかわいいよことりん。なお、Android版HBFavはすでに別の方がHBFav for Androidをリリースしていたので少し名前を変えてHBFav Materialという名前で出しています。Design Support Libraryを使ってマテリアルデザイン風味にしたHBFavという意味合い。

アプリはこちら

HBFav Material はてブを流れるように見るアプリ - Google Play の Android アプリ

経緯

rei19.hatenablog.com

  • 合宿の中では完成まで至らなかったけど、せっかくなのでリリースまで持って行きました。

感想

  • Kotlinで書くメリットはQiitaのこの記事にまとめられてるので紹介。最高でした。

qiita.com

taro.hatenablog.jp

  • プライベートで実績作って仕事で導入。。。できたらいいな。仕事でもKotlinでAndroid書きたい。
  • コードはこちら。もっとこう書いたほうがいいよ!とかあればプルリクをいただけるとうれしいです。

github.com

  • 転職してネイティブの人になりつつあるけど、Webアプリも書きたいですな。やはりElixirでなんか作るか。

追記

  • Potatotips #26 で発表したのでその資料

名言 大事なことはFateから教わった

このエントリーは 名言Advent Calendar 2015の16日目のエントリです。

www.adventar.org

名言

貴様は臣下を救うばかりで導く事をしなかった
王の欲の形を示す事もなく、道を見失った臣下を捨て置き、
ただ一人ですまし顔のまま小奇麗な理想とやらを思い焦がれていただけよ
王とは、誰よりも鮮烈に生き、もろ人を見せる姿を指す言葉
全ての勇者の羨望を束ね、その道標として立つ者こそが王!
故に、王とは孤高にあらず
  • Fate/Zeroの聖杯問答からですね。知らない人置いてきぼりな感じです。僕はFate好きすぎるw
  • このシーンは3人の王が王の在り方をそれぞれ語り合うシーンですが、内容は仕事にも通じるところがあるなーと思ってたりします。プロダクトが目指す未来、目指す世界を共有して、そこについて行きたい!と思わせるような人と仕事したいですね。
  • 観たことない人は今ならNetfrixで配信されている(らしい)ので、正月休みにでも観るとよろし。

Fate/Zero-アニメ公式サイト-

Androidのデザインパターンを考えてみたの続き。Kotlin対応版。

一番最新のやつはこっち

rei19.hatenablog.com

前回はこちら

rei19.hatenablog.com

  • 前のプロジェクトで開発していたアプリをリリースしてからはAndroidから離れていたのですが、会社変えてまた触ることになったので思い出す意味でも再考します。
  • 前回は出来るだけ純正のAPIだけで考えたのですが、今回はライブラリも使いながらもう少し手を入れてみたり。

今一度ポイント

  • 基本的にModelは死なない + 通知で変更を伝える役割を持つ
  • ViewやVCでAPIを呼ぶのはユーザーの操作に影響されやすいからやめれ

変えたところ

  • Android Best Practiceを参考にパッケージ構成を整理

github.com

  • 以下のライブラリ群をGradleに追加。AsyncTaskLoader周りはokHttpとRxを使い、Observer周りはottoのEventBusを使って通知のやり取りをする。

    • okHttp
    • otto
    • rxJava
    • rxAndroid
  • こちらも参考に。たぶん前の投稿のスライドと同じ所属の方の作。ありがたや。

github.com

  • 今、Kotlin楽しい感じなので、Kotlinで書きなおしてます。考え方は変わらないのでJavaに置き換えて読むとよいかと。

パッケージ構成

パッケージ名 役割
entities ドメインで扱うデータクラスを配置
events EventBusを通して発火するイベントクラスを配置
fragments Fragmentを配置
managers 多様なデータを扱うコントローラを配置
models ドメインモデルを配置
network 外部との通信が発生するモジュールを配置
utils ユーティリティー群
views Viewに関連するモジュールを配置

コード

  • managers/ModelLocator
  • こちらは先程のandroid-mvc-sampleをKotlinで焼き直し
package me.rei_m.kotlinmvcsample.managers

import java.util.HashMap

/**
 * 各Modelのインスタンスを管理する
 */
public class ModelLocator private constructor() {

    companion object {
        private val showcase = HashMap<Tag, Any>();

        public enum class Tag {
            ATND,
        }

        public fun register(tag: Tag, model: Any): Unit {
            showcase.put(tag, model)
        }

        public fun get(tag: Tag): Any = showcase[tag]!!
    }
}
  • アプリ起動時にAtndApiのモデルをセットしておく
        // ModelLocatorにModelの参照を登録
        ModelLocator.register(ModelLocator.Companion.Tag.ATND, AtndModel());
  • entities/AtndEntity
  • Kotlinのデータクラスを使って作成
package me.rei_m.kotlinmvcsample.entities

import java.io.Serializable

/**
 * Atndのイベント情報を保持するデータクラス.
 */
public data class AtndEventEntity(val id: Int, val title: String) : Serializable
  • network/AtndApi
  • APIとの通信部分を作成。さんぷるなのでキーワードは固定。
package me.rei_m.kotlinmvcsample.network

import com.squareup.okhttp.CacheControl
import com.squareup.okhttp.HttpUrl
import com.squareup.okhttp.OkHttpClient
import com.squareup.okhttp.Request
import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import org.json.JSONObject
import rx.Observable
import java.net.HttpURLConnection

/**
 * Atnd Apiへの接続部分を定義する.
 */
public final class AtndApi private constructor() {

    companion object {

        /**
         * Atnd APIへのリクエストを行いAtndEventのEntityを返すObservableを作成する.
         */
        public fun request(): Observable<AtndEventEntity> {

            return Observable.create({ t ->

                // リクエストのURLを作成.
                val url = HttpUrl.Builder()
                        .scheme("https")
                        .host("api.atnd.org")
                        .addPathSegment("events")
                        .addQueryParameter("keyword_or", "google,cloud")
                        .addQueryParameter("format", "json")
                        .build()

                val request = Request.Builder()
                        .url(url)
                        .cacheControl(CacheControl.FORCE_NETWORK)
                        .build()

                // リクエスト開始.
                val response = OkHttpClient().newCall(request).execute()

                if (response.code() == HttpURLConnection.HTTP_OK) {
                    // 正常に取得できたら、パースしてonNextでObserverに流す.
                    val responseJson = JSONObject(response.body().string());
                    val eventCount = responseJson.getInt("results_returned")
                    if (0 < eventCount) {
                        val events = responseJson.getJSONArray("events")
                        for (i in 0..eventCount - 1) {
                            val e = events.getJSONObject(i).getJSONObject("event");
                            t.onNext(AtndEventEntity(e.getInt("event_id"), e.getString("title")))
                        }
                    }
                } else {
                    // エラーの場合はレスポンスコードをThrowableにつめて投げる
                    t.onError(Throwable(response.code().toString()))
                }

                t.onCompleted()
            })
        }
    }
}
  • models/AtndModel
package me.rei_m.kotlinmvcsample.models

import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent
import me.rei_m.kotlinmvcsample.events.EventBusHolder
import me.rei_m.kotlinmvcsample.network.AtndApi
import rx.Observer
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import java.util.*
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent.Companion.Type as EventType

/**
 * Atndに関する情報を管理するドメインモデル.
 */
public class AtndModel {

    /** API読込中など処理中か判定するフラグ */
    public var isBusy = false
        private set

    /** アテンドのイベント情報を保持するリスト */
    public val atndEventList = ArrayList<AtndEventEntity>()

    /**
     * AtndのAPIからイベント情報を取得し、内部に保持する.
     */
    public fun fetch() {

        // ビジー状態の場合は処理終了.
        if (isBusy) {
            return
        }

        // ビジー状態にする
        isBusy = true

        // APIリクエストのオブザーバーを作成
        val observer = object : Observer<AtndEventEntity> {
            override fun onNext(t: AtndEventEntity?) {
                // リストに追加する.
                atndEventList.add(t!!)
            }

            override fun onCompleted() {
                // onCompletedを受け取ったら完了のLoadedEventを発火する
                EventBusHolder.EVENT_BUS.post(AtndLoadedEvent(EventType.COMPLETE))
            }

            override fun onError(e: Throwable?) {
                // onErrorを受け取ったらエラーのLoadedEventを発火する
                EventBusHolder.EVENT_BUS.post(AtndLoadedEvent(EventType.ERROR))
            }
        }

        // AtndのAPIにリクエストを飛ばしてModelへの配信を開始する
        AtndApi.request()
                .onBackpressureBuffer()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .finallyDo({
                    // 必ずビジー状態を解除する.
                    isBusy = false
                })
                .subscribe(observer)
    }
}
  • fragments/AtndEventListFragment
package me.rei_m.kotlinmvcsample.fragments

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ListView
import com.squareup.otto.Subscribe
import me.rei_m.kotlinmvcsample.R
import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import me.rei_m.kotlinmvcsample.events.AtndEventClickEvent
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent
import me.rei_m.kotlinmvcsample.events.EventBusHolder
import me.rei_m.kotlinmvcsample.managers.ModelLocator
import me.rei_m.kotlinmvcsample.models.AtndModel
import me.rei_m.kotlinmvcsample.views.adaptors.AtndEventListAdapter
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent.Companion.Type as EventType
import me.rei_m.kotlinmvcsample.managers.ModelLocator.Companion.Tag as ModelTag

/**
 * AtndEventをリスト表示するFragment.
 */
public class AtndEventListFragment : Fragment() {

    /** ListViewのアダプター */
    private var mListAdapter: AtndEventListAdapter? = null

    companion object {
        /**
         * ファクトリ.
         */
        fun newInstance(): AtndEventListFragment {
            return AtndEventListFragment()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mListAdapter = AtndEventListAdapter(activity, R.layout.list_item_atnd_event)
    }

    override fun onDestroy() {
        super.onDestroy()
        mListAdapter = null
    }

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val view = inflater!!.inflate(R.layout.fragment_atnd_event_list, container, false)

        val listView = view.findViewById(R.id.list_atnd_event) as ListView

        listView.onItemClickListener = AdapterView.OnItemClickListener { parent,
                                                                         view,
                                                                         position,
                                                                         id ->

            // リスト内の項目がクリックされたらクリックイベントを発火.
            val atndEventEntity = parent?.adapter?.getItem(position) as AtndEventEntity
            EventBusHolder.EVENT_BUS.post(AtndEventClickEvent(atndEventEntity))
        }

        // リストビューにアダプターをセット
        listView.adapter = mListAdapter

        return view
    }

    override fun onResume() {
        super.onResume()

        // EventBus登録
        EventBusHolder.EVENT_BUS.register(this);

        // AtndModelとリストビューに表示中のイベント数を取得
        val atndModel = ModelLocator.get(ModelTag.ATND) as AtndModel

        val displayedCount = mListAdapter?.count!!

        if (displayedCount != atndModel.atndEventList.size) {
            // 表示済の件数とModel内で保持している件数をチェックし、
            // 差分があれば未表示のイベントがあるのでリストに表示する
            mListAdapter?.clear()
            mListAdapter?.addAll(atndModel.atndEventList)
            mListAdapter?.notifyDataSetChanged()
        } else if (displayedCount === 0) {
            // 1件も表示していなければイベント情報を取得する
            atndModel.fetch()
        }
    }

    override fun onPause() {
        super.onPause()

        // EventBus登録解除
        EventBusHolder.EVENT_BUS.unregister(this);
    }

    @Subscribe
    @SuppressWarnings("unused")
    public fun onAtndEventLoaded(event: AtndLoadedEvent) {
        // ModelからLoad完了のイベントを受け取った時の処理
        when (event.type) {
            EventType.COMPLETE -> {
                // 読み込みが正常に完了したらModel内のイベント情報をアダプターにセットして再描画する
                val atndModel = ModelLocator.get(ModelTag.ATND) as AtndModel
                mListAdapter?.clear()
                mListAdapter?.addAll(atndModel.atndEventList)
                mListAdapter?.notifyDataSetChanged()
            }
            EventType.ERROR -> {
                // エラー表示する
            }
        }
    }
}

動かしてみる

f:id:Rei19:20151123103250p:plain

  • 今回のコードはこちらに

GitHub - rei-m/Kotlin_mvc_sample

  • Retrofitとか使うともっと短く書けるのかな。まだまだAndroid歴浅いのでなんとも修行足りない感。
  • ちゃんとテストを書かないとというところがまだ考えきれてないのでその辺もやらないとですね。Stateパターンとかも取り入れてみたら規模がでかくなったら幸せになる気も。しばらく触りそうなので色々試してみます。