Takuji->find;

株式会社はてなでアプリケーションエンジニアやってます、技術的な記事を書いているつもり

LTの資料を作るときにまずブログ記事から書いている

メンターのシニアエンジニアと話していたら、それいいじゃんって感じになったのでブログにまとめてみる。

関西モバイルアプリ研究会でほぼ毎月(といいつつ最近発表回数減ってるけど)Androidネタで発表しているが、最近はそれと同時にブログ記事も公開している。

blog.takuji31.jp

blog.takuji31.jp

これらの記事は資料作成と同時に書いていて、LTの開始のタイミングで公開する。

なぜブログ記事と同時に書くのか

毎月発表していると、資料作成に使える時間は大きなカンファレンスに参加する時より圧倒的に少ない。関西モバイルアプリ研究会はLT形式なので、もちろん資料のボリュームは小さいんだけど、実際に話すネタとして考える内容は20分や50分のトークとさほど変わらないと思う。

人によってはアウトラインをまず書いてから細かく作っていく、という風に作っていくんだろうけど、自分は割とそういうことが得意でないことが多いので、スライドツールを目の前に頭を抱えることが多い。

そこで、先にブログ記事を書いて、それをLTの時間で話せる内容に落とし込んで資料にしている。

どうやって書いているか

はてなブログにはMarkdown記法で書く便利な設定がある。

そして、DecksetというMarkdown記法でスライドツールが書ける便利なアプリがMac App Storeにある、有料。

www.decksetapp.com

記事の内容をコピペしつつ、スライドとしての体裁を整える。

ブログ記事の順番に話が並ぶので、発表として破綻しているような順番になったりすることはまずない。

資料に書かない部分は発表ノートにしたりもしている。

問題点

  • ブログ記事と発表してる内容が大して変わらない
    • ここは補足を入れるなどすると解決しそう
  • ブログ記事ミスってたら資料もミスってマサカリとか椅子が飛んできそう
  • 本当はもっと良いやり方があるのではないか

よいこと

  • 素早く資料作成できる
  • 発表してきましたブログ書く手間が省ける
  • 参加していない人にもだいたい何を話しているか伝えることができる

やってみて

書いているといっても実際やってみたのはまだ2回だけなので、もっと改善の余地はありそう。

ただ、最近やっている「思いついたらとにかくブログに書く」と組み合わせることでアウトプット量は圧倒的に増えたという気がしているので、続けていきたい。

うまくいったらそのうち通常のトーク資料作る時にも使っていきたい。

一気に書いた新規ファイルを一部git addしたい時にはgit add -Nが便利

新機能作ったりする時、コード書くのに集中しすぎるとコミットする前にめっちゃ色々混ざったファイルができてしまったりして、最悪な感じになることが多い。

自分は割と細かい単位でコミットするようにしている(あとで戻せるように)ので、こうなってしまった時今まではチマチマと手動で消してaddして、commitして、undoしてみたいなことをやっていたが、ミスったりすると数時間分の作業が消える。

そういう時にはgit add -N (file path) すると、ファイルの存在だけindexに乗せることができるので、あとはgit add -pでパッチモードにしてがんばる。

そもそもちゃんと細かくコミットしろよって話でもある。

エンジニア立ち居振舞い : 常に問題意識を持つ

お題「エンジニア立ち居振舞い」

エンジニアに限ったことではなさそう。 サービスとかアプリが運用フェーズに入るとつい思考停止して日々のタスクをこなして生きるだけの機械になりそうになるが、現状が例え良さそうに見えても常に問題はあると考えるようにしている。 そうすることでサービスやアプリの改善に繋がるかもしれないし、業務やチーム、組織の改善につながるきっかけが生まれるのではと思っている。

常に思考し続けている結果、夜にも頭が冴えていることが多くてなかなか寝付けないのが最近の悩み。

MySQLの文字列型

雑なメモ

8月からWebのお仕事やってて、5千年ぶりくらいにまともにMySQL触ることが最近増えてきた。

MySQLのテーブルへのカラム追加のレビューをしていたときにわかったことを書いておく。

.*TEXTな型は TINYTEXT TEXT MEDIUMTEXT LONGTEXT があるが、 最大長は TINYTEXT < TEXT < MEDIUMTEXT < LONGTEXTの順らしい。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.1.3 文字列型の概要

MEDIUMなんて言うからてっきり無印と同じかと思ってたら全然そんなことなかった。

他にもつい VARCHAR(255) とか雑に書いてしまうけど、 VARCHAR は最大65535までいけるのね、たぶんCHARの最大長が255だからそれのノリで(学生時代の頃から)書いていたんだと思う。

以外と色々知らずに思考停止気味に使っていたことに気付いた昼下がりでした。

TODOコメントにGitのbranch名を入れたい

めっちゃ雑なメモ。

今いるチームではTODOは"必ずやらなければならないこと"にだけ付けるというルールがあって、だいたいshibayuさんのブログの通りに運用してる。

最近コード中のTODOコメントの書き方を工夫している - $shibayu36->blog;

自分の場合割とGitのbranch名に機能名とかをprefixとしてつけいるので、TODO(feature-phase)的なTODOを延々と量産してはやったら消すし、やってないなら残したままmergeして適切なタイミング(リリース前とか次のbranchとか)で消すようにしている。 残っているTODOを確認するのも TODO(feature をgit grepなりagなりで検索すればよい。

割と量産するので、TODO(branch-name)を毎回打つの面倒だし、コピペするのも行ごとってわけじゃなくてやはり面倒なので、勝手にbranch名取ってきて挿入してくれるneosnippetのスニペットを書いた。

snippet     todo
options     head
    # TODO(`substitute(system('git rev-parse --abbrev-ref HEAD'), '\n\+$', '', '')`) : ${0}

これでtodoを展開すると# TODO(feature-phase) : みたいなのができあがって便利。

git管理下以外では動かないけど、そもそもそういうところで使わないので気にしていない。

Support Library 24.2.0で追加されたDiffUtilを試してみた

こんばんは、最近はAndroid JavaではなくPerlとTypeScriptを書いているid:takuji31です。

この記事は本日開催の関西モバイルアプリ研究会 #17の発表を元に作成しています。

今日はSupport Library 24.2.0でrecyclerview-v7に追加された DiffUtil を試してみたので紹介します。

DiffUtilとは

2つの List の差分を計算するユーティリティー。

List の要素ごとの変化を計算する DiffUtil.Callback を引数に取り、 DiffUtil.DiffResult を受け取る。 デフォルトでは追加と削除と更新のみ受け取れるが、オプション指定することで移動も計算できる。ただし、計算コストが上がる。

DiffUtil.Callback

それぞれの要素の変化を DiffUtil に伝えるCallback。5つのメソッドが用意されていて、4つがabstractになっている。

getNewListSize() / getOldListSize()

名前通り新しい/古い List のサイズを返す。

areItemsTheSame(int, int)

2つのアイテムが同じものであるかどうかを判定する。

例えば2つの List にユーザーのエンティティーが入っていると仮定して、それぞれのユーザーが同一のものかをこのメソッドで判定してやる。

areContentsTheSame(int, int)

areItemsTheSametrue だった時に呼ばれる。

areItemsTheSame はアイテムそのものが同じものであるかどうかの判定だったが、 areContentsTheSame はアイテムの内容が同じかどうかを判定する。

見た目上なんらかの変化があれば false を返す。

getChangePayload(int, int)

areContentsTheSamefalse だった時に呼ばれる。

古いアイテムと新しいアイテムで、どういった変更があったか通知するオブジェクトを生成する。

通知するオブジェクトの型は自由なので、変更通知用のクラスを作ってそのオブジェクトを渡すイメージ。

このメソッドだけabstractになっていないので、実装しなくてもよい。その場合は null が渡される。

DiffUtil.DiffResult

DiffUtil.DiffResult は計算したdiffの結果を保持している。直接結果を読むことはできず、 ListUpdateCallback あるいは RecyclerView.Adapter を経由して取得する。 RecyclerView.Adapter に新しいデーターを渡した後に DiffResult.dispatchUpdatesTo(RecyclerView.Adapter adapter) を呼ぶことで、 RecyclerView の変更通知に使える。

使ってみる

※いつも通りKotlinです

DiffUtil はrecyclerview-v7の24.2.0以降に入っているので、入れておく。

雑にModelを作る

enum class Status {
    INTERESTED, LIKE, LOVE;
}

data class Artist(val name: String, val status: Status) {
    companion object {
        val list: List<Artist> = listOf(
                Artist(name = "小倉唯", status = LOVE),
                Artist(name = "雨宮天", status = LOVE),
                Artist(name = "水瀬いのり", status = LIKE),
                Artist(name = "Trysail", status = LIKE),
                Artist(name = "Minami", status = LIKE),
                Artist(name = "佐倉綾音", status = LOVE),
                Artist(name = "田村ゆかり", status = INTERESTED),
                Artist(name = "ワルキューレ", status = INTERESTED),
                Artist(name = "水樹奈々", status = INTERESTED)
        )
    }
}

DiffUtil.Callback の実装を作る、匿名クラスでも問題ないが、ちゃんとクラス化しておくと使い回せるのでよさそう。

class DiffCallback(val oldList: List<Artist>, val newList: List<Artist>) : DiffUtil.Callback() {
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].name == newList[newItemPosition].name
    }

    override fun getOldListSize(): Int {
        return oldList.size
    }

    override fun getNewListSize(): Int {
        return newList.size
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }

    override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Pair<Status, Status> {
        return Pair(oldList[oldItemPosition].status, newList[newItemPosition].status)
    }
}

このケースでは name をIDととらえて、 name が同じなら同じアイテムで、かつ他のプロパティー(といっても status だけだが)が一致した場合に同じコンテンツとみなしている。

あとは適当に RecyclerView.Adapter やら Activity やら必要なものを実装してやって、リストの更新をするときに以下のように実行してやる。

    fun updateItems(items : List<Artist>) {
        val oldItems = adapter.items
        val diffResult = DiffUtil.calculateDiff(DiffCallback(oldList = oldItems, newList = items), true)
        adapter.items = items
        diffResult.dispatchUpdatesTo(adapter)
    }

するといい感じにリストの更新がアニメーションされる。

RecylerView 以外と組み合わせる、Change payloadの中身を見て何かしたい、などといった場合は ListUpdateCallback と組み合わせて使うこと。

制限事項

  • 古い List と新しい List の中身を比較するので、 List の要素が変わる、あるいは要素そのものの値が自動更新されるような仕組み(Realmとか)とは一緒に使えなさそう
  • List の要素数は2^26(=67108864)まで

最後に

  • DiffUtil を使うことで RecylerView への詳細な更新通知が楽になる
  • 手で更新通知投げるの地味に面倒だったが、これだと DiffResult.dispatchUpdatesTo(RecyclerView.Adapter adapter) 呼ぶだけでよい
  • パフォーマンスが気になるが、全部同期的に実行されるので、Rxなんかを使ってバックグラウンドで実行してやるとよさそう
  • RecylerView 以外との組み合わせなど凝ったことをやりたい時は ListUpdateCallback を使う
  • Realmなど自動更新されるようなものとの組み合わせは悪そうだが、ViewModelを挟んでやるとよいのではないか

サンプルコードはGithubにあげてます。

github.com