どくぴーの備忘録

真面目なことを書こうとするクソメガネのブログ。いつ投げ捨てられるのかは不明

DroidKaigi 2019に参加していました(スタッフ)

感想録です。DroidKaigiも終わって2週間位たったのでエモい気持ちも落ち着いてきたかな?という感じなので気持ち的なものを備忘しておこうかなという…。

今年もDroidKaigiが行われました。 DroidKaigi 2016からなんやかんやで就活といい感じに予定を合わせて参加させてもらったり、上京したので気兼ねなく参加できるようになっていったのですが、今回はスタッフになっていました。登壇者になりたいなーって言っていたらスタッフになっていました。

スタッフになるまで

2018に参加する前後(だった気がする)に @pside さんとランチに行ってそのタイミングで「スタッフ興味ある?」「それなりにはあります」みたいな会話をしていたら、忘れた頃にDMでスタッフにリクルーティングされました。「一緒に血反吐を吐こうな」という言葉で契約(?)を結んだのがとても印象的でした。

何してたの

当日に向けては大体Codelabs班・写真班として活動していました。

Codelabs

Day 1/2ともにRoom5(ロビー入って左に進むとある小さめの部屋)でやっていたCodelabsのスタッフを主たる業務として動いていました。いわゆる「メンターさん」的な感じにしようかなーと意識はしていて、大体次のムーブをできればいいなと思いながらこなしていました。

  • 部屋の隅に固まらない、くるくる歩く
  • 手の上がった人がいればすぐ行く
  • 手の上がっていない人にも軽く話しかけたり、困ってそうであれば一緒にコード見てみたり
  • 本当は今何をしているか、どうすれば解決できるかをうまいこと理解レベルの溝を埋めながら話ができると良かった
    • 気にはしていたものの多分できてない…

「初心者からその先へ」みたいな感じをキーワードに当初はしていて、Google Codelabs で選べるものとは別に以下のような題材を選んでいました。

developer.android.com

Googleが2018/10に公開したAndroidの基礎コースですね。ActivityやUI周りといった基礎的なコンポーネントや非同期処理、ユーザデータの保存と言ったトピックをConstraintLayoutやRoomなどの新しいポイントを使って学んでいく、という感じのCodelabs集という感じです。

medium.com

github.com

App Improvement Challengeという、DroidKaigiのためにCodelabs班で開発したオリジナルのCodelabsです。イメージとしては「Androidを始めた人が現場に入って、案件とどう向き合うべきなんだろう…?」みたいなものを題材に、既存コードにどう影響を加えずに手を入れていくか、また余裕があるときに Jetpack Architecture Guide を基準としてどうリファクタしてくべきなんだろう…?みたいな思いを込めていたはずだったのですが、題材開発がやたらと盛り上がった結果何かよくわからないけどすごいものが出来上がりました(ちょっと難易度がハードコアすぎた感があるので反省点かなと思ったりしています…)。このタイミングに「修正すべき点があるコード」というapproveを行うために LBTM(Looks Bad To Me)という謎の褒め言葉が生まれました。メイン開発をしていた @ymnd さん、 tomoya0x00 さん、本当にお疲れ様でした!

このCodelabsにずっと入り浸って参加してくださった方々がいらっしゃったり、参加してくださった方々からリファクタの結果をPRとして送っていただいていたりしていて、なんか語彙力がない感じでいうと「すごい、うれしい」としか言いようのない感じでした。本当にありがとうございました!

ちなみにこのリポジトリの中で一番好きな部分は計測通信を行っている(という体の) IngestManager クラスとやたらめったらテクニカルなAsyncTaskが跋扈する記事一覧を取得する loadTopStories メソッドでした。

カメラ班

上のアルバムに載せるような写真を撮影をするなどしておりました。腕章付けてカメラ2台を肩から下げて、基本Codelabs部屋を常駐しながらワイワイ撮影していました。

装備としては

自前:Sony α7II + SEL24105G + Godox TT350s(ほぼストロボ炊いていない)
レンタル:Sony α7III + SEL70200GM

という感じで二日間過ごしていました。新古品からα7IIを買っただけだったのでα7IIIにあまり食指が動いていなかったんですがα7IIIってすげぇんだなと思いました。AF-Cで瞳AF追尾できるってすごい。

後日、社内で撮影の用事があったので自前装備を使ったんですが足りねぇ…!!という気持ちになったので近い内にSEL70200G(GMじゃないよ)を買うことになりそうです。さっき発表されていたタムロンの35-150mmとかすごくいいなぁと思っているのですがEマウント用出ないですかね…?

Fireside Chat

Codelabs班の一環なのですが、パーティ前にホールでやっていたFireside Chatの話題の用意やスライド作成といった下準備をやっていました。当日は撮影担当をしていたので司会は @satsukies@shanonim さんにお願いしました。@takahirom さんを始めとした公式アプリメンバーの方々にも急に参加をお願いしてしまったんですが最終的には沢山の方々に来ていただいて、盛況だったと思います(盛況すぎてほぼ最前にいないと話がよく聞こえてこないようなレベルだったので申し訳ないです…)。本当にいい感じに進めていただいてありがとうございました…!

Visualizer

同時期にスタッフに参加した @satsukies@pside さんからVisualizerの開発を引き継いだので、二人で2019年対応をしていました。これまでのVisualizerについては次の記事を見ると良さそうです。

p-side.net

OP前に流れていたアレです。リポジトリも今の時点で公開されているものをそのまま引き継いで二人で開発していました。

やったこととしては大体以下の感じでした

  • 画面サイズが去年の4:3から16:9に変化したのでそれに合わせて画面を有効に使えるように変更してみた
    • 会場では左右が若干見切れていたみたいでくるくる回っていた部分がよくわからない感じになっていたぞ
  • 色とかロゴの2019化
  • スライドの告知文言の追加・変更
  • VisualizerのVisualizer(?)部分が動かなくなっていたので動かせるように修正
    • ChromeのAutoplay Policyのせいでユーザ起因でないとAudioContextが取れなくなってたので、マイクが使えなくなっていた
    • なのでもうSTARTボタンを押すと始まるようにした(安直)

satsukiesもぼくもJSよくわかんないReact.jsよくわかんない言いながらなんとかそれっぽくなったので一旦満足です。ただ、コードベースには昨年比でほとんど手を加えていないのと、Typescript化したいとかいうissueが立っていたりするので勉強して来年頑張る…頑張る…?という気持ちです。

ほか

Codelabsの参加者アンケートでお配りしてめっちゃデカイ缶バッジ、自称ドデ缶バッジを作っていました。直径は10cmです。

あとは開催後に荷物の片付けをしたりしていました、帰りは @satsukies と @ymnd さんとsteamだったりゲームボーイカラーだったり、ゲームの話をしながらゆるゆる帰っていました。

終わってみて

なんやかんや12月付近からすっごい慌ただしくなったなという感じでした(忙しくてもなんやかんやOK出してくれた会社の人には感謝です…)。

とは言うものコアな部分にはあまりいなかった(ここで言うコアは会場周りとか司会周りとかの人だと思っています)ので、本当に忙しい人はもっと忙しかったんだよなぁ…と。

来年もなにか力になれるといいなと思ったし、来年こそ忙しくなってもいいから登壇をしたいという気持ちになりました。来年は時期が来たらCfPを頑張って書いていこうな。

かしこ

お酒を飲みながらWorkManagerのCodelabsをしてお勉強をした

なにこれ

本記事は 飲酒プログラミング Advent Calendar 2018 - Adventar の22日目の記事になります。

え?投稿日がそれより遅いって?知らないなぁ…。初日がまだ未投稿らしいのでそれより早ければセーフという謎理論で挑みたいと思います。

どうやらdescriptionを見ている限り、お酒を飲みながら何かしらのプログラミングに関するアクションを起こせばセーフということで、前々から理解しないとなぁと思っていたWorkManager周りのお勉強をCodelabsベースで概観だけでも把握すべくお酒を飲んでダラダラと実行しました。

書き始める前に

やっぱりお酒を飲むということで今回の飲酒の証拠を残しておきます。

ヤッホーブルーイングのTOKYO BLACKです。なんでまともに正面から撮らないんだこいつは。
ちなみにこれはCodelabs実践中の飲酒で、記事執筆のためにスーパードライと余っていた久保田 千寿も摂取するなどしております。昨日は飲んでコード書いていたら終了しました。

WorkManagerとは?

Android Jetpackのひとつであり、アップロード処理やフォアグラウンドで実行してしまうとUIを長時間ロックしてしまいそうな適宜実行されたり実行保証が必要なバックグラウンドタスクを行うArchitecture Componentです。

これまでAndroidのバックグラウンド処理を扱う物というとForeground Serviceでベッタリ書いてみたりJobScheduler, FirebaseJobDispatcher, AlarmManagerといったものをAPIレベルに応じて使い分けたりする必要があったのですが、WorkManager内でこれらを適切に使い分けてくれるようになります。

ちなみに現在はstable、というわけではなく、12/19にbeta01がリリースされました。思ったよりタイムリー。

Codelabsをやっていく

Background Work with WorkManager

Google I/O 2018のタイミングでWorkManagerが登場したのですが、それに合わせてCodelabsも公開されているので、これとにらめっこしながら概観を追いかけていくことができます。

ちなみに12/23時点でCodelabs内で指定されているバージョンが1.0.0-bata01になっているのですが、Codelabs中で扱っているコードがBreaking changeの発生した1.0.0-alpha13より前の物となっており、いわゆるコピペコードでは動作しないものになっているので注意しましょう。Codelabsで用意されているリポジトリはちゃんと1.0.0-beta01に追従されているのでそちらを参考にする場合は特に不都合ないと思います。

参考 -> Architecture Components Release Notes  |  Android Developers

ちなみにCodelabsではバックグラウンドで画像にブラーをかけるアプリが題材になっています。実際にブラーをはける処理はすでに実装されていて、WorkManagerにつないだりする部分だけを自分で加えていく形なので本質を捉えやすいかと。

f:id:e10dokup:20181223225018p:plain
題材となっているアプリ、画像を選んでそれにブラーをかけ、かかった画像をストレージに保存します

とりあえずWorkManagerで出てくる登場人物を知る

Worker

WorkManagerによってバックグラウンドで処理されるロジックを扱うクラス。
Worker をextendsし、 doWork() 中に実行されるロジックを記す。

doWork() の返り値となっている Worker.Result の返り値が1.0.0-alpha13で変更されており、Codelabs中に記されている Worker.Result.SUCCES という定数指定から Worker.Result.success() というような形になりました。後述のoutputDataもここに引数に入れることでよくなります。

WorkRequest

Workerを実行するリクエスト。 作ったWorkerを渡してRequestを作成し、WorkManagerに渡すクラス。このWorkRequestが実行キューに積まれる感じ。 OneTimeWorkRequest(一度だけ実行)/PeriodicWorkRequest(定期的な実行)があり、Constraintsを使って実行条件を指定したりできる。

WorkManager

依頼されたWorkRequestをスケジュールして実行するクラス。WorkRequestで指定したConstraintsを満たしつつ、負荷を分散しながらスケジュールしてキューに入っているWorkRequestを実行する。

簡単な扱い方

  • extends Worker なクラスを用意する
    • doWork() メソッドをオーバーライドして必要なロジックをそこに実装する
    • 処理の成功、失敗を Worker.Result.success() / Worker.Result.failure() でreturnする
  • WorkRequestに作ったWorkerを入れて、Requestを作成する
    • 特に指定がなく、一度だけ実行するだけなら OneTimeWorkRequest.from(HogeWorker.class) でOK
  • WorkManagerにenqueueする
    • 特になんてことはなく、作ったWorkRequestを workManager.enqueue(request) するだけ

Workerに値を渡す

ファイルを取得して実行するためにURI Stringとかが必要な場合はDataクラスがあるのでこれを使います。扱い方としてはIntentのExtraみたいな感じで、Data.Builder().putString("KEY", "VALUE").build() のようにし、WorkRequestを from で一発で作らずにbuilderを使って作ります。

OneTimeWorkRequest.Builder(HogeWorker.class)
        .setInputData(data)
        .build()

実際に受け取った値をWorker中で取り出すためには、Worker側で getInputData().getString("KEY")

複数の処理を連結して実行する。

CodelabsではChain your worksと書いてある節です。複数のWorkerを逐次実行することが可能で、題材となっているアプリでは

ディレクトリのクリーンアップ -> ブラー処理(強度に応じて複数回) -> ファイル保存

という一連の流れをchainしています。実装としてはWorkContinuationクラスを使います。 WorkManager#beginWith で起点となるWorkRequestを渡して、 WorkContinuation#then にそれ以降に続くWorkRequestを渡していき、最終的にWorkContinuation自体をenqueueすることで実行されます。

// AWorker -> BWorker -> CWorkerの順に実行されていくWorkContinuationの例
val continuation = workManager.beginWith(OneTimeWorkRequest.from(AWorker::class.java))
continuation.then(OneTimeWorkRequest.from(BWorker::class.java))
continuation.then(OneTimeWorkRequest.from(CWorker::class.java))
continuation.enqueue()

また、このままだと重複実行を許すので beginWithbeginUniqueWork にすることで重複実行を防ぐことができます

var continuation = workManager.beginUniqueWork(
        "unique_work_id",
        ExistingWorkPolicy.REPLACE,
        OneTimeWorkRequest.from(AWorker::class.java)
)

Constraintsを使って実行を制限する

WorkRequestのBuilderがsetConstraints()というメソッドを持っており、この中に以下のようにビルドしたConstraintsを渡すことでWorkerの実行を状態に応じて制限することができます。

/// 端末充電中でのみ実行するConstraints
val constraints = new Constraints.Builder()
                .setRequiresCharging(true)
                .build();

この他にも、NetworkTypeに応じて実行を制限できたり(METERED/UNMETEREDがあるのでおそらく従量課金かそうでないかを判断している…らしい?)、バッテリ残量、ストレージ残量に応じて実行を制限できたりします。

まとめ

寄った勢いなのでがっさり + この話は色んな人が先に触れている気がするのでなんとなくやってみたレポートでした。これ以外にもWorkerでの実行状態の取得(WorkStatus)ができたり、これまでAPI分岐を考えたり自前で実行可否の判定処理を実装していたりした部分が割とWorkManagerが吸い取ってくれそうでいい話感があります。

なんでこれを扱おうと思ったのかというと、以前勉強会で発表した資料がForeground Serviceをガッツリ使っていて、WorkManagerとか使ったらいいのに…というアドバイスを頂きまして、そのノリで「じゃあお酒飲みながら勉強してみますかー」という感じでした。

実際多分ググったほうが詳しい解説をしていらっしゃる方がいると思うのでほぼ備忘録です。ここまでお付き合いくださりありがとうございました。

中古ガジェット沼について

本記事は 沼 Advent Calendar 2018 - Adventar 8日目の記事となっております。

Advent Calendarは毎年情報量過多なので今年はあまり知見にならない記事を書きたい @e10dokup です。
というわけで趣味を垂れ流して完全に良さそうな沼 Advent Calendar行ってみましょう。

中古ガジェット

文字通り、中古のガジェットですね。
定義は様々なのであれですが、スマホからパソコン、オーディオ機器、カメラなんかが中古でありますね。
結構掘り出し物だったり、特集な品物がある上に新品とは違って常に同じものがあるわけでもないので、金ではなく時間が溶ける特集な沼だと思っています。
そんな中古ガジェット沼の生態を見ていきましょう。

中古ガジェット沼の行動傾向

中古ガジェット沼の朝は早…いわけでは別にないですね。はい。
ただし毎日(というか週末前から?)欠かさずチェックするものがあります。

akiba-pc.watch.impress.co.jp

www.gdm.or.jp

の2つですね。ただしこれは秋葉原が行動圏内に入っていない限りはあまり効果がないので大阪日本橋が行動圏内な人はその限りじゃなかったりします。それでもない人は…どうなんだろう。
これらのサイトは秋葉原の特価情報を扱っており、休日の前日くらいから各ショップがどんな商品を特価で売り出すだの、そんな情報が流れてくるんですよね。
それらを確認して、めぼしいものに目をつけたりつけなかったりやっぱいいやってなったりするわけです。

そして特にめぼしいものがあろうがなかろうがその中古ショップ目指してほぼ毎週秋葉原日本橋に向かいというわけですね。
なんで毎週なんだ、みたいな話があるんですが特価情報に上がっていない商品でも時々良さげなものが仕入れられたりしたりしていて、地味に手が出そうになったりします。
そういう取りこぼしを避けるために物理的にポーリングしに行く、という時間の無駄(重要)を強いられる沼です。

中古ガジェット沼の溶かし方

時間

前述のように物理ポーリングをするわけですが、打率は大概激低で基本的に何もせずに帰る、 みたいな結果を起こすのがザラなので毎週時間が無為に溶けます。
akiba-pc.watchなりで見た商品が気になって見に行くも「うーん、これじゃあないな?」みたいな感想になってしまってスルーすることがやたらあります。つらい。
そのため、せめてもの対策として秋葉原日本橋を巡回する最適ルートを構築しだします。めぼしいものがありそうな店をいかに効率よく周るか、みたいな。
なのでやたらと路地裏に詳しくなったりするのが副次的効果だったりそうじゃなかったりしますね。

もし、何かの間違いか運命のめぐり合わせか欲しいものに出会ってしまった場合、あなたはそれなりの決断に迫られるでしょう。
あなたが欲しいとチラとでも思ってしまったものは誰かも同じことを考えているものかもしれません。中古商品は数に限りがある上、同じコンディションの物は存在しないのです。
そこに「あとで買おう」などという選択肢は存在しておらず「今買うか?」「無視するか?」の2つしか存在していないのです。

後は…、新品で買えばあれくらいだから…と新品に対する金銭的ハードルが少し上がることでしょう。
安く済ませることは大事ではあるのですが、正直新品にまさるものはないので溶かしたくない感覚ですね。
あと、akiba-pc.watch等のサイトでよく輸入端末(Xiaomiとかとか)が仕入れられたりしていて、確認すると「日本での使用可否は不明」などと記述されているのですがつまりそういうことです。 技適

念のためですが、 この記事は決して技適承認のない商品を購入しそれをLTE環境等の電波を発する状況で使用することをおすすめするものでは一切ありません。
私自身も購入するものは技適承認が通っているものを買うようにしています。ですが日本でもXiaomi端末使いたいので(Mi 8ほしい…)なんとかなってほしいなぁと思ってます。頼む総務省

私の今年の沼履歴

2018年。平成最後の沼イヤーに買ったものをプレイバックしていきましょう。

Surface Pro 2

秋葉原イオシスで2月くらいに購入。47800円で箱全部入りのi5-4300U/8GB RAM/256GB SSDのモデルでした。
ちょっと割高じゃなぁい?みたいな雰囲気だったのですがこのSurface Pro 2、Office 2013のライセンスが残っているので実質15000円分くらいアドっぽいです。
ちょっと面倒な書類とかだとわざわざExcel引っ張り出したりしないといけないので、やはりOfficeが使える端末(Windowsが使えるならなおさら)はあるといいんですよね。
あとSurface Pro 2はタッチペンがWacom製(3以降はN-Trig…)でペンの書き味も良かったなぁと。
ちなみに今はもう手元には存在しておらず、知り合いに同額で売り払いました。いい端末だったよSurface Pro 2…。

dtab Compact d-01J

6月くらいに一斉入荷していたHuaweiタブレットですね。未使用で14800円と謎にお安かったのでかなり取り上げられて話題になって、すぐ売り切れていました。

akiba-pc.watch.impress.co.jp

旧dtabを比較するとだいぶスペックが良くなっていて、AndroidのOSバージョンも7.0まではサポートしているのでそれなりどころか割と使えるいいタブレットです。
実はネットワーク制限が△(いわゆる赤ロムになる可能性のある端末)なんですが最近こういうのは赤ロム永久保証がついていて、モバイルネットワークが使えなくなったら全額返金返品ができるのも手軽に買えた理由ですかね。
そもそもタブレットなのでSIM入れて運用する気がなかったっていうのもある気がしますが…。WQHDなのもあっていい読書端末になってしまっています。

Sony WH-1000XM2

言わずとしれたソニーのワイヤレスノイズキャンセリングヘッドホン。今はUSB-C給電になって音質もNC性能も良くなったM3が発売されていますね。
実際M3のほうがいいかなーって思ったりもしたのですが、未使用の品が21800円で売られていたので購入しました。シボ加工がされていて微妙に高級感がある。
仕事場とかで使ったり、新幹線でボーッとするときに使ったりしているのですが、明らかに静かになってすごいという感じです。ソニーのNCをオンにしたときのスゥ…ってノイズが消えていく演出がすごいニクい。

BUFFALO WSR-2533DHP2

buffalo.jp

BAFFALOのいい家庭用ルータですね。こちらは日本橋のお店でアウトレット品として新品(?)を5000円で買いました。
箱もいつものBAFFALOの箱ではなく無地ダンボールだし、保証なんかあるわけ無いだろ!みたいな感じの超ストイックな売られ方をしていたのですが、まぁ5000円でこれ買えるなら…みたいな感じでスッと購入してしまいました。
実家のNEC Atermがオンボロになってしまったのでこれにリプレースしたところ、とりあえず3ヶ月くらい経って元気に動いているし回線速度もベストエフォートに近い値が安定して出るようになったのでとりあえずは健康そうです。
自宅にも一台買えばよかった…

Sony α7II +SEL24105G

こちらは渋谷のじゃんぱらでたまたまシリアルだけ抜き取られた新古品があったので衝動買い。当時15万円弱だったα7IIレンズキットが10万で買えました。
んで、フルサイズEマウントだったらほしいなーと思っていたSEL24105G(24-105mm F4通し)も人気がありすぎて品薄で手に入らないしまってますか…みたいな感じだったのですがマップカメラにある日突然美品が流れてきたのでこちらも衝動買い。
こっちは定価だったんですが、無事にいい感じに整ってしまったので人やイベントを撮りたいときはこれにレンズをレンタルしたりして運用したり、モータースポーツを撮りたいときは以前持っていたPENTAX K-S2を持っていったりしています。楽しいカメラライフ。
ちなみに昨日名古屋に行ってきて雑に長時間露光を試してみました。こんな感じ。

f:id:e10dokup:20181208171143j:plain

総括

中古ガジェット沼、特定のなにかに沈めるものではなくていろんな沼と関連性があり、そして他の沼にズブズブ沈んでいってしまう危険性の高い激しい沼です(今更)。
純粋に楽しむという意味では昔話題になった珍ガジェットが流れてきているのを見て楽しんだり、なぜか業務用バーコードリーダーや病院で使われているらしい端末が売られているのを見て笑ったり、かなりバリエーションの有るものが見れます。
実用的に走ろうと思えば良い品を探して買ってみたり、ネタみたいなゴミ端末に思いを馳せたり、いろんな楽しみ方ができる不思議な沼でした。財布と時間の使い方には気をつけよう!

第51回情報科学若手の会を運営して参加してきた #wakate2018

表題の通りです。

10/6-8に軽井沢研修所というところで行われた第51回情報科学若手の会というイベントに幹事として参加しました。幹事なので運営もしているという感じですね。

wakate.org

幹事って何をしているの?とか若手の会のお金周りってどうなっているの?みたいな雰囲気だと、今回代表幹事を務めた @kuro_m88 さんが記事にまとめているのでそちらを見ると良さそうです。

kurochan-note.hatenablog.jp

幹事としては初めてなんですが、これまで学部3、4年、社会人1年目と3年続けて参加してきたのでこれで4年目4回目の参加という感じです。

セッションについて

上の @kuro_m88 さんの記事にも言及されている通り、情報科学若手の会においては幹事も参加者です。僕はカメラマンの真似事をしたりしながらだったのですが幹事もみんなセッションをずっと聞いていました。 僕情報科学若手の会というと、毎年アカデミックな方面で多方面に深い話題を話してくださる方が多いので、ネイティブアプリ開発一辺倒な僕としては首を縦に振りながら「こういう世界もあるんか…」みたいな感想を抱くことがほとんどという感じで、毎年同じことを言っている気がするのですがいろんな刺激を受けることができたなぁと思っています。

今回のセッションを大まかに分けると

という感じで、その中でも参加者の多数に「僕も私もデータセンターがほしい」と言わせた招待講演のコンテナデータセンターのお話がすごく盛り上がっていた印象があります。自身でデータセンターみたいな大きな設備を作り上げる過程がすごく夢(?)のあるお話でした。

僕個人としては高校生の方が二人発表されていて、結果としてはともかく抱えた課題を解決するためにWebサービスDjangoで書いてたり、課題研究的な名目でIoTなことをされていてすごくものづくりにトライすることへのハードルが下がっていていいなぁ…と思ってました。高校生の時期からものづくり・プロトタイピングにチャレンジできるってすごく経験値としては貴重なものだと考えているので羨ましい限りでした。

毎年「来年は発表しよう」と意気込んでいるのですが、幹事と化したことで忙しさ++って状態になってしまい、特になにか思いつくこともなく…って感じでしたね。来年はなにか用意できるといいなぁって思う程度にしておきます。

交流イベントについて

今年はやる側ではなくやらせる(?)側になったのですが、今年のお題はnanoblockをチームで組み立ててドゥンする感じのアレでした。本当はnanoblockを組んでいる人は目隠しをする予定だったのですが、僕が目隠しをしたときに何もできなくなってすぐ諦めた経緯があります。本当に無理でしたね…。 テストプレイではnanoblockの説明書を見て指示側が言語化をする画像要約的ななにかと組む側が指示側の発言をなんとなく察していく機械学習的ななにかがあって面白かったのですが、皆さんめちゃくちゃ集中されていて茶々を入れる隙もなくてすごかったです。

ちなみに終盤の悲鳴を上げたチームの「単体テストは完璧なのに結合テストが通らない!!」が今回最大の名言だと思いました。nanoblockは開発プロジェクトなのではないかという一説を投じる素晴らしい言葉ではないでしょうか(?)

なんで若手の会に参加し続けているの?

この会に何を求めているのか、みたいな話なんですが、カンファレンスとか勉強会に参加するモチベーションと似ているような違うような、そんなよくわからない気持ちです。 カンファレンスや勉強会だと自分が専攻している・メインに扱っている分野の知見を深めたり、広めたりするようなモチベーションなのかなぁと僕個人のモチベーションとしてはあるのですが、こと情報科学若手の会においてはターゲットが「情報科学」という広い上にがっさりとしたテーマなので、それこそ本当に前述したようないろんな分野の人が来て、いろんなことを話しているんですよね。その中で「何も知らないことを知る」という無知の知を得て、もしその中で興味が生まれるようなことがあれば自分で深く調べてみたりとか、そういうきっかけになるといいなぁと参加しています。大体この会で発表してくださる人ってその分野のことを楽しそうに話されるのでそういう意味ではすごくいい刺激がもらえるなぁ、と思っています。

あとは僕を最初に若手の会に引き込んだ人が「人生をいい意味で狂わされた」と言っていたのですが、いろんな界隈のいろんなバックエンドの方が(お酒飲んで)ワイワイしているので色んなお話や相談ができてすごい、みたいな感じです。ゲラゲラ笑ってるだけかもしれないしすごく真面目に話し合ってたり、進路相談していたり。まさに今の開催理念に掲げている「ルイーダの酒場」な感じがしていていいなぁと思いました。なので来年も頑張っていくぞい、みたいな気持ちです。

初幹事だったけど

初幹事ということで5月に下見に行ったり、準備としてタスクが増えたり、いろいろイベントは増えたんですが当日はほぼ参加者みたいなノリで楽しめたなぁと思っています。閉会後も残ってた幹事で軽井沢ぶらついてビール飲んだり温泉入ったり霧の軽井沢駅モカソフト食べたりしてました。 初めてだったのでよくわからないところは割とぽいっとしちゃって、できることだけをやってフォローに回ったりしてたんですが、多少慣れたはずなのでもうちょっと自分がテキパキ動けるようになるといいですね。はい。

今年は技術書典5と開催日がダブってしまった都合で、毎年いらっしゃってくれていた方々が来れなくなってしまったのが心残りでした…。来年はしっかりイベントスケジュールを立てていきたいですね。

おまけ

軽井沢の写真のせるマンになります。担々麺とビールめっちゃうまかった(感想)

f:id:e10dokup:20181008125700j:plainf:id:e10dokup:20181008131032j:plain

f:id:e10dokup:20181008131314j:plainf:id:e10dokup:20181008141051j:plain

f:id:e10dokup:20181008151809j:plainf:id:e10dokup:20181008172959j:plain

f:id:e10dokup:20181008194821j:plain

写真を自動でアップロードする技術 〜Google Photos API編〜

アブストラク

2018年のGoogle I/O直前にて公開されたGoogle Photos APIを使って一眼レフカメラで撮った写真をよしなに自動でアップロードするための手法録になります。

高専カンファレンスで大慌てで話していた発表を丁寧に焼き直そうとした感じになっています。

e10dokup.hateblo.jp

本記事ではGoogle Photos APIについてまとめます。スライド中で触れられている他の要素については別記事にて…。

Google Photos API

Google Photos APIs  |  Google Developers

我々(主語拡大)は君をずっと待っていた。

これまで名前だけになってしまったPicasa APIを使ってGoogle Photosへの画像アップロード(容量無制限無制限・16MPへの画像縮小制約あり)ができる環境はあったが、Google Photosにオリジナルサイズの画像の投稿が可能になったGoogle Photos APIがついにロンチされました。

トップページから入れるガイドページの概要を見る限りがっさりとした仕様では

  • OAuth 2.0による認証
  • Library
    • ユーザのGoogle Photosアカウントに保存されるメディアについての操作ができるぞ!
  • Albums
    • ほかユーザと共有するためのアルバムに関する操作ができるぞ!
  • Media Items
    • Google Photosにおけるメディア(画像、動画)についての操作ができるぞ!
    • 写真のアップロード自体はここに含まれる
  • Share
    • 自アカウントのメディアを他の人に共有するぞ!

と普通にGoogle Photosを使っている上での通り一遍の操作はできそうな印象があります。

というわけで今回は特に僕が達成したい「画像のGoogle Photos上へのアップロード」について記していきましょう。

というわけで本題。

Google Photos APIを使えるようにする。

いつものMaps等のGoogle APIsを使えるようにするプロセスとほぼ一緒ですね。まず、Google Cloud PlatformのコンソールからAPIの有効化を選び、API ライブラリ上でPhotos Library APIを検索すると、お目当てのPhotos Library APIが出てくるので有効化します。

f:id:e10dokup:20180816204339p:plain

有効化した後に「APIとサービス」のページに移動し、左ペインの認証情報を選んでから新規の認証情報としてOAuthクライアントIDを選択して作ればGoogle Cloud Platformのコンソールでの準備はOKです。僕は今回はAndroidAndroid Things)からアップロードを試みていたので、この中でdebug.keystoreのようなkeystoreファイルのSHA1キーやApplication IDを入れたりしています。

f:id:e10dokup:20180816204511p:plain

Google Photos APIに画像を投稿してみる。

Google Photos APIhttps://photoslibrary.googleapis.com )における画像投稿は以下のようなシーケンスになっています。

  1. クライアントからGoogle Photos APIPOST /v1/uploads に対して画像を application/octet-stream で送信する
  2. Google Photos APIからレスポンスとしてアップロードトークンが返却される
  3. 返却されたアップロードトークンを含めてGoogle Photos APIPOST /v1/mediaItems:batchCreateJSONを送信する
  4. Google Photos APIから送信したJSONGoogle Photos API内での情報が追加されレスポンスとして返却される = アップロード完了

ひとつずつ追っていきましょう。

1. 画像データの送信

なんてことはなく、/v1/uploads に以下の内容のPOSTリクエストを送るだけです。

<Header>
Authorization: Bearer <Token>
Content-Type: application/octet-stream
X-Goog-Upload-File-Name: <ファイル名>

<Body>
画像のバイナリ

POSTが成功すると次のようなレスポンスが特に何かに包まれてるわけでもなく返却されます。この返却されている文字列がアップロードトークンになります。

CAIS6QIApKFirX.....

2. メディアアイテムを登録する

/v1/mediaItems:batchCreate に先程手に入れたアップロードトークンを含めた次のjsonをbodyにしてPOSTリクエストを送ります。

<Header>
Authorization: Bearer <Token>
Content-Type: application/json

<Body>
{“newMediaItems”:[
  {
    “simpleMediaItem":{
      “uploadToken":"CAIS6QIApKFirX....."
    }
  }
]}

POSTが成功すると送ったjsonのパラメータにメタデータ等が肉付けされる感じで返却されます。この時点でGoogle Photosを確認するとアップロードされた写真が登録されているのが確認できます。

f:id:e10dokup:20180916145859p:plain

これで写真の投稿は完了です。この辺に関してはカメラで撮った写真とかに全く関係がないので写真によらない要素でも使えそうな気がします。アルバムとかの操作もできるのですが、それはまた別の機会に…。

高専カンファレンス in 東京 2018に参加してきた #kosenconf

久々にブログ更新するよ

高専カンファレンス in 東京 2018に参加してきた

kosenconf.tokyo

そもそも高専カンファレンスとは一体

高専生が現役・OBOG問わずいっぱい集まってきて自分の好きなことややっていることをえげつない熱量を以てお話するカンファレンスです。
今回はスポンサーにもなっている DMM.com さんの六本木のオフィスをお借りして開催されてました。オサレオフィスって感じでした。

他にもスポンサーとして Cookpad さん、 jig.jp さん、英和システムマネジメントさんとかが名を連ねていました。すごい。

ちなみに開催日自体は先週の日曜なのでもう2週間が経過しているなどしています。ブログを書くまでが高専カンファレンスとは一体何だったのか。

一体何をしてきたの

そそのかされ(?)一般発表枠に応募してみたら選考に通ってしまったので登壇してきました。その時のスライドはこちら。

speakerdeck.com

Google Photos APIとFlashAirを使ってカメラで撮った写真をアップロードする話をしました。いろいろと日常生活が大変すぎた結果1日で話す内容を作り上げたのでまた整理してちゃんとブログの記事なりにまとめる感じでやっていきます。ちなみにAndroidアプリとしてはある程度実装が終わっているのですが、スライド中にもある通りスマホアプリとしてすら起動したくなくて、とするとAndroid Thingsを使ってFlashAirのGPIOと合わせながらよしなにする、みたいなプランを持っているのですがAndroid Thingsにするに当たって「そもそもGoogle OAuthってできるんだっけこれ…?」みたいなのがあり、検証しようにも実際に行動になかなか移せていない感じです。時間確保してぇ…。

あとはなぜか説得感が欲しかったためにSony α7IIとPENTAX K-S2をずっと持っていました。重い。

今更気づいたんですけどSpeakerDeckって4:3のスライドはなんか妙な表示になるんですね…、次からは16:9で作ろう。

途中のスライドで(勝手に)友情出演してもらった @puhitaku の走るルータが一番のターニングポイントでした。keynoteで作ったんだしアニメーションでそのままどこかへ走って行けばよかったかな

ちなみに公式サイトからスケジュールの午前に並んでいる発表タイトルのスクリーンショットはこちら。

f:id:e10dokup:20180729211216p:plain

周りが真面目すぎる。一体何でこんなラノベみたいなタイトルで通過したのか…。

微妙にお手伝いもしていました。

そんなこんなで前日はほぼ徹夜みたいな雰囲気が入っていたのでんぼーっとスライドを書いていたのですが、 kosen10s のslackに運営に入っていた @puhitaku からDMが飛んできたりしていて

puhitaku「当日スタッフしてくんない?」  
ぼく「ええで」  
puhitaku「あざす!09:00にきて!」  
ぼく「ええで」  

みたいな感じだったのですがその二時間後くらいにまた運営に入っていた @pndcat から同じようにDMが飛んできて

pndcat「09:00に来てくれるんだって!?」  
ぼく「あい」  
pndcat「08:30に変更な!仕事も変更!」  
ぼく「…?」  

という感じで凄くあっという間に集合時間を繰り上げられました。こわい。

運営さん本当に大変そうだったのでお疲れ様でした。ありがとうございました。以外の言葉がありませんね…。

久々に高専カンファレンスに行って思うこと

エモい話をするわけじゃないですが、高専カンファレンスってやはり高専生が好きなことを熱量をもって話すカンファレンスなんだなぁと。
開催される回、運営に携わる方々で全く雰囲気が変わる会になるなぁって印象があって、LT会とかオフ会みたいな言葉では片付けられないイベントだと勝手に思ってます。 もちろんその中で知ってる人に久々にあったりとかいうオフ会みたいな成分とかがあったりするんですけどね。

また行動範囲内でスケジュールが合う感じで開催されるようであれば是非参加したいなぁと思いました。

二週間遅れなので記憶が薄れてくるなどしており、これ以上伸ばすともう何も思い出せなくなりそうなので乱筆しました。現場からは以上です。

DB付きWeb APIが急に欲しくなったのでGAE/Goで雑に実装する

まずはじめに

この記事は任意のアドベントカレンダーに属するものではありません。

何の話なの

Golangの勉強をはじめましたという話です。具体的にはいつぞやの記事に書いていたもののWeb API側を作った時の話です。

e10dokup.hateblo.jp

本業はAndroidアプリエンジニアなので、正直に言うとサーバサイドがよくわかってない感じからのスタートなので備忘録的に書いていく感じでやっていきましょう。

GAE/Goを使ってみる

宙に浮いているさくらのVPSがあるのですが、なにぶん時間がないということで今回はGAE/Goを使ってGoogle Cloud SQLと組み合わせることにしました。対して派手なことしないし今回の用途(とりあえずDBを持ったWeb APIの実装がしたい)に限っては必要十分って言う感じです。適当にWAFにはginを、ORマッパーにはgormを選択しています。

Golangのインストール

多分ググったほうが早い。僕はこの辺を見たりしながらインストールしました。

インストール - The Go Programming Language

macユーザならhomebrew一発でやれば良さそうな気がします。

qiita.com

GAE/Goのインストール

多分こちらもググったほうが早い。Google Cloud SDKをインストールしたり、Golang用のAppEngine SDKをダウンロードしてきてPATHを張ったりしました。

とりあえず動くようにしたい!ってモチベーションだったので、このあたりの記事を参考にやっていた記憶があります。

実際に開発する

実際のディレクトリ構成はこんな感じになりました。

sample
├── app
│   ├── appInit.go
│   ├── controllers
│   │   ├── hogeController.go
│   │   └── ・・・
│   ├── db.go
│   └── models
│       ├── hoge.go
│       └── ・・・
├── gae
│   ├── app.yaml
│   ├── init.go
│   ├── static
│   │   ├── main.css
│   │   ├── main.js
│   └── templates
│       ├── hoge.tmpl
│       └── ・・・
└── main.go

ローカルで極力goapp serveをやりたくない、ローカル環境時はIntelliJやGogland等のIDEのデバッガーを使いたい。みたいなことを感じたため、ローカルでビルドする際とgaeでデプロイする際に別の口を用意してそこからapp/app_init.goにアクセスして初期化を行う感じにしました。

とりあえず先にGAEへのデプロイのためのapp.yamlを書くとこんな感じになると思います。env_variables内の各値はCloud SQLの設定値をそのまま引っ張ってくるだけです。handlersにはGAE上でのstaticディレクトリ参照を書くようにしています。

version: 1
application: sample
runtime: go
api_version: go1.8

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: _go_app

env_variables:
  CLOUDSQL_CONNECTION_NAME: <CLOUD_SQL_CONNECTION_NAME>
  CLOUDSQL_USER: <CLOUDSQL_USER>
  CLOUDSQL_PASSWORD: <CLOUDSQL_PASSWORD>

app/app_init.goは次のような感じになっています。

package app

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "github.com/jinzhu/gorm"

    "sample/app/controllers"
)

var IsGAE = false
var db *gorm.DB

func Init(gae bool) *gin.Engine {
    IsGAE = gae

    InitDB(gae)
    db = ConnectDB(IsGAE)

    router := gin.Default()
    router.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello, e10dokup")
    })

    // 静的ファイルの設定

    if gae {
        router.LoadHTMLGlob("./templates/*")
    } else {
        router.Static("/static", "./gae/static")
        router.LoadHTMLGlob("./gae/templates/*")
    }

    return router
}

init関数の中でついでにdbのマイグレーションもしてしまう感じにしました。DBへの接続はgaeであるかどうかのboolとAppEngine SDK自体が持っているローカルか本番用かの判定を行う関数を使って与えるURIを切り替えているだけです。

package app

import (
    "fmt"
    "os"

    "google.golang.org/appengine"

    _ "github.com/go-sql-driver/mysql"

    "github.com/jinzhu/gorm"
    "sample/app/models"
)

func ConnectDB(gae bool) *gorm.DB {

    var uri string

    if gae {
        connectionName := os.Getenv("CLOUDSQL_CONNECTION_NAME")
        user := os.Getenv("CLOUDSQL_USER")
        password := os.Getenv("CLOUDSQL_PASSWORD")

        if appengine.IsDevAppServer() {
            uri = "<LOCAL_MYSQL_URI>/<TABLE_NAME>"
        } else {
            uri = fmt.Sprintf("%s:%s@cloudsql(%s)/<TABLE_NAME>", user, password, connectionName)
        }

    } else {
        uri = "<LOCAL_MYSQL_URI>/<TABLE_NAME>"
    }

    db, err := gorm.Open("mysql", uri)
    if err != nil {
        panic(err)
    }

    return db
}

func InitDB(gae bool) {
    db := ConnectDB(gae)

    db.AutoMigrate(&sample.Hoge{})
}

これをローカルで立ち上げるためのmain.goか、GAEで立ち上げるためのgae/init.goでInit関数を呼ぶときに引数のboolを分けて呼ぶという感じでした。GAE/Goだとmainパッケージのmain()関数を含むファイルが使えなかったり、init()関数が最初に呼ばれたりと色々勝手が違いますよね。あとgin周りだとGAEではrouter.Run()関数ではなくてhttp.Handle()関数に渡すようにしないと動かなかったりしました。

// main.go
package main

import (
    "sample/app"
)

func main() {
    router := app.Init(false)
    router.Run(":8888")
}


// gae/init.go
package gae

import (
    "talknotifier/app"
    "net/http"
)

func init() {
    router := app.Init(true)
    http.Handle("/", router)
}

ModelとかControllerを扱うエンドポイントを作る

後はModelとControllerを立ててinit()関数内にルーティングを生やしていく感じで実装しました。

// models/hoge.go

package models

type Hoge struct {
    ID int `gorm:"NOT NULL;UNIQUE" json:"id"`
    Name string `gorm:"NOT NULL" binding:"required" json:"name"`
}


// controllers/hogeController.go

package controllers

import (
    "github.com/gin-gonic/gin"
    "sample/app/models"
    "github.com/jinzhu/gorm"
)

func GetAll(c *gin.Context, db *gorm.DB) {
    var hogeArray []models.Hoge
    db.Select("*").Find(&hogeArray)
    c.JSON(200, hoges)
}

// appInit.go - init()内

router.GET("/api/hoge", func(c *gin.Context) {
    controllers.GetAll(c, db)
})

REST APIじゃなくてHTMLテンプレートのような静的ファイルを返したいときはgih.Context.HTMLで返してあげるようにします

router.GET("/", func(c *gin.Context) {
    c.HTML(http.StatusOK, "index.tmpl", gin.H{})
})

起動・デプロイ

ローカルで動かしたいときには普通に go build してから出力されたファイルを実行するようにしたり、IntelliJ/Goglandの設定をしてデバッガを走らせたりしていました。

GAEで動かしたいときには goapp deploy gae をすればGoogle Cloud SDKのインストール時に設定していたアカウントにGAEのインスタンスが立ち上がって稼働するので、結構お気軽に立てられていいなーって感じでやってました。GAEデプロイ中のインスタンスでログを取ろうとするとよくあるlog.Printf()関数ではなくgoogle.golang.org/appengine/logのlog.Infof()関数とかにappengine.Contextを引数で渡す必要があったりします。うーん…。

まとめ

当時は大慌てで実装したので色んなところに気づかず実装したりしていましたが、いざ振り返って自分のコードを見ると慣れていないにしても結構無茶苦茶なコードを書いてるなぁ…って思いました。あとGAEなんだかんだ便利ですね。とりあえず雑に動くものをデプロイしたいときにぱぱっと使えるのはいいなって思います。ネイティブ開発だと動く実機を用意したりしないと行けないのでコストが嵩む嵩む…。

結構腰を据えてGolang勉強したいなーってこの開発で思えるようになったので @puhitaku と @orisano にもらったプログラミング言語GoとGo言語によるWebアプリケーション開発を使ってちょくちょく勉強しています。お二人まじでありがとう。