どくぴーの備忘録

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

お酒を飲みながら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アプリケーション開発を使ってちょくちょく勉強しています。お二人まじでありがとう。

ノリと勢いで買ってよかったものベスト10 2017

まずこの記事について

この記事は #kosen10s Advent Calendar の24日目の記事です。

<- 前日の記事(@sonet_sou) 翌日の記事(最後なんだけど何故か埋まっていない)->

今年もいっぱい散財した

メリークリスマス、メリー散財。今年もいろんなものを皆さん買いましたね?僕もいろんなものを買いました。 みんなに「物買い過ぎでは…?」って言われるんだけど多分飲み会とかのお金がこっちに回ってきてるだけな気がしています。でも来年はもうちょっと節約したい気もしますね!

そんなわけで今年何買ったかを覚えていないと来年また散財マンになってしまいそうなのでここはガジェオタっぽく買ってよかったものランキングでもしてみようとかいうモチベーションです

ちなみに誕生日等でいろんなものをkosen10sの人であったりから頂きました。だいたい以下の感じ

送ってくれた方々本当にありがとうございました :bow: :bow:

そんな感じで始めていきましょう!

10. 君の名は。BDスペシャルエディション(Amazon限定版)

言わずと知れた去年公開の名作映画ですね。僕も劇場で何回も見ました。 予約開始で速攻で予約して、届いて即一回見ました。やっぱり良いですよね。最高。先週の日曜早起きに失敗して新海誠展行けなかったのが本当に悔やまれる。 ちなみにUHDBD版だと縮刷版の台本がついてくるのですが、お値段とUHDBDを見れる環境がいつまでたっても整わなさそうなので見送りました。悔しい。

9. ジャンクPC + GeForce GT 1030 + WD Green 240GB

同期が秋葉原に8月当時出回っていたThinkCentre M73 Tinyを見に行っていた横でしれっと購入。MouseProがi5-4430と8GB RAMが乗っかった状態で13000円で売られていたので割安では…?って思って買って、その場でSSDとSteamで持っていた古めのゲームを動かしたくてグラボを買いました。

ちなみに今でも一応動いています。結構Windowsがあると便利なんですよね…

まぁ安いなーとは思っていたんですがちゃんと裏があって、通電時間が長くなったときに再起動をするとBIOS POSTが走らず再起動を繰り返す多分電源周りの病気持ちでした。うーん、まぁ起動はするし…って気持ちです

8. 新宿御苑の年間パスポート

新宿御苑、いいです。

いや何がいいって自然に囲まれてひたすら芝の上でゴロゴロしてもいいし、カメラを担いで写真を撮りに行ってもいいし、PCとモバイル回線を持っていって作業をしてもいいしと、とにかく僕にとっては非常にリラックスできるスペースだと思います。夏とか秋口とかは @puhitaku とかとカメラを持っていって写真を撮っていたり、一人でPCを持っていってコーディングや何かしらの書き物をしたりしてました。何故か僕は結構捗ってめっちゃ良かったです。

f:id:e10dokup:20170708144415j:plain

ちなみに通常入場200円、年パス2000円です。僕はもう10回は軽く通っている記憶があるので元を取ってしまった感がすごい。流石に冬は寒いので行けてないですけど…。

7. LINE WAVE(先行体験版)

clova.line.me

日本で最初に買えるスマートスピーカーだと言うことで飛びつきました。当時15000円(LINIE MUSIC 6ヶ月分バンドル)結構今でも便利に使っていて、明日の天気を聞いたりアラームを掛けたりラジオ的に音楽を垂れ流してもらったりしてます。案外喋って動かすインターフェースって使わないんじゃないのかなーって思ってたのですがあると結構使ってますし案外便利なんですよね。使ってみないとわからない感じというか…。

個人的にはその後に出た正式版がキャンペーン価格とは言え12000円でLINE MUSIC 12ヶ月分バンドルなのが悲しいですね…。

LINE WAVE + Clovaだとオレオレカスタマイズが出来ないなぁって言うのがあって、最近半額になっていたGoogle Home Miniを買ってきました。GoogleアシスタントSDKの日本語対応もあったので何かおうちハック的なことをやろうかなーなんて考えています。

6. 単焦点レンズ(PENTAX DA50mmF1.8)

僕はPENTAX K-S2を使っているのですが、PENTAXで手頃な単焦点というとこの50mm/F1.8か35mm/F2.4な感じがあります(どちらも15000円前後)。個人的には風景とかを明るいレンズで撮ってみたかったので50mm/F1.8にしてみました。上の御苑の写真とかはこのレンズを使って撮ってみたものだったりします。ただふと寄りたいなーって思うと50mmじゃちょっと不自由なのでうーん、35mmが良かったかなーって思わなくもない感じだったりします。こんなことをやってるから沼にハマる。

5. PlayStation 4 + HORI Racing Wheel Apex + グランツーリスモSPORT

PS4自体はじゃんぱらで16000円できれいなものが売られてたので衝動買いでした。そのきっかけで「やっぱグランツーリスモSPORTしたいじゃん」となり、せっかくだからハンドルコントローラーも一緒に買おうってなったので一緒に購入しました。これまでレースゲームは普通のパッドでやっていたのですが、やっぱりハンコンを使うと別の楽しさがありますね。GT SPORTも「GTらしくない」と酷評され気味ですが最近ついにオフラインモードに昔のGTっぽいモードが実装されたのでまた熱が再燃しています。というかGT SPORTは写真を撮るのがくっそ楽しいですね、こんな写真が撮れたりします。4Kサイズでレンダリングも可能で、しかも撮ったデータはUSBメモリにエクスポートできるのできれいに撮れるとPCの壁紙にしたりとかして楽しい。

f:id:e10dokup:20171026020645j:plain

f:id:e10dokup:20171224002203j:plain

ちなみにハンコンは上のジャンクPCに繋いでEuro Track Simulator 2をマルチプレイでやったりしています、こっちもかなり楽しいのでおすすめです。

4. Sony Xperia XZs(au SOV35)

www.sonymobile.co.jp

以前使っていたau isai Vividがよく壊れて3台目になっていたのとXperia XZsの発売日と更新月が重なっていたり、キャッシュバックキャンペーンがいい感じに重なって適用できたりと何かとタイミングが良くて買い替えました。なにげに初めてのXperia

そもそもisai Vividがよく壊れたりでかなり怪しかったのもあって、とても快適です。Snapdragon 808からSnapdragon 821になったものRAMが4GBになったのも十分大きい気がします。なんとなくバッテリーの持ち具合が良くない気がするけど普段の使い方だとよく充電器につながりっぱなしなので別に気にはならないレベル。

ちなみに今年は他にiPhone 6s(同期に譲ってもらった)、Moto G5 Plus(検証機)、NuANS Neo(Windows 10 Mobileの方、公式に終了したのでいよいよ使いみちが…)を買ったりしています。

3. Sony PHA-1A + Sony XBA-N1

カメラの沼にハマりかけてると思ったら今度はオーディオ沼です。

PHA-1Aが何故かじゃんぱらできれいなものが13000円くらいで売ってたので衝動買いでした。中古屋ウォッチング、時々こういうのが出てくるので本当に良くない。

実はXBA-N1は昨日届いたので実質セルフクリスマスプレゼントです。溜まりに溜まっていた楽天ポイントをぶっこみました。

XBA-N1の前はJVC HA-FXT100を使っていましたが乗り換えてみて個人的にすごい好みの音が出るようになった気がします。HA-FXT100より中音域が厚くなった感じがあってすごい好きな感じです。完全に主観なのでレビューというよりプラシーボという説もあるのですが多分こういうのって本人が満足できればそれが一番なんだろうなって思わなくもないです。

同期には「どうせ二ヶ月後くらいにXBA-N3を買うやつ」って言われたんですが流石にそんなつもりはないしこれ以上オーディオ沼に足を取られたくないのでこれで僕は満足です。そういうことにさせてくださいマジで。

2. iMac 5K Retina

www.apple.com

今年最大のお買い物です。仕事用じゃないです。

実はApple Storeからの購入ではなくビックカメラのアップグレードプログラムでの購入です。ビックロの店舗で購入したのですが店員さんに「明日の配送でよろしいですか?」と聞かれたときに何故か満面の笑みで「いや、ここから自分で持って帰ります!」と言ってしまいクソデカい台形の箱を抱えて新宿のビックロから家に1時間かけて帰る謎の体験をしました。

「なんでiMac買ったの?」っていう話になるのですが使っていたMacBook Pro 13インチ(Early 2015)が二年を迎えるのですが案外元気に動いているのでリプレースするにはもったいない感じで、コスパ的にiMacを手に入れればいい感じになるのではないかというところです。買ったのCore i5-7500搭載モデルで、それに自前で8GBメモリを増設して16GBで運用しているのですがやはりUシリーズではないデスクトップ版Kaby Lakeのパワーはすごくて、ものすごい速さでAndroidプロジェクトのビルドをしてくれます。最高。

流石にお仕事でiMac…となると会議とかでPCを持ち運びたいのでそこはMacBook ProとかのノートPCのほうが良さそうですよね。難しいところです。ただ本当にiMac 5K Retinaはすごいので購入直後には下のツイートみたいな気持ちになります。

1. Nintendo Switch

www.nintendo.co.jp

なんか本当にありきたりなんですが、Nintendo Switchは今年本当に買ってよかったなって思います。僕は予約購入勢です。

長い時間遊んでいるタイトルがこれまたマリオカート8DX・スプラトゥーン2スーパーマリオオデッセイとありきたりなタイトルですが、久々に楽しんでゲームしたなっていう薄っぺらい感想が出るくらいには現在進行形で楽しんでいます。スプラトゥーン2スマホ連携でボイスチャットしながらサーモン・ランとかできるのでくっそ楽しいですよね。

おすそ分けプレイをやりたくて何処かで友達とボンバーマンをやったり、飲み会の席でぷよぷよテトリスをやったりしたのですが、携帯ゲーム機じゃありえなかった「外でみんなでプレイする」ことを成し遂げているのが本当に新体験で良かったです。一人プレイでもいつでもどこでもプレイできる仕組みあるおかげでゼルダの伝説とかでみんなが時間を大量に溶かしていたりするので、PS4Xbox Oneなんかにはないオンリーワンの体験を提供しているのはさすが任天堂って感じです。はい。

ちなみにスプラトゥーンはクソ雑魚です。時々上手い人のプレイ動画を見たりすると何が起きているのかわからない感じになります。

大散財を並べた後に思うこと

普通にベスト10がスッと埋まるあたりどんだけ物欲があったんだ…ってなりました。上京1年目だったので色々物が揃ってなかったのもあるなーって感じで一年かけてやっといろんなものを揃えていい感じに家の中が充実してきたなーって思ってます。流石に来年はこんな大散財を控えるつもりです。

案外ポジティブなのが「買ったものはだいたい使い続けていられてる」って言うところなのかなと。安物買いの銭失いで買ってすぐ壊れちゃった、みたいなものはあったのですが、なんだかんだ買ったものを一度も使わなかったとか、すぐ使わなくなっちゃったみたいなケースが実はないので衝動買いと言いながら意外と個人的ニーズがあるものを買っていたという説がある。

来年はもっと計画的に衝動買いをしていきます。みんなも軽率にお買い物をしていきましょう。