Infinito Nirone 7

白羽の矢を刺すスタイル

Canon EOS 6D MarkII を持って散歩した

前まで使っていた EOS Kiss X3 が天寿を全うしまして、新しいのほしいなと思っていたときに神楽坂つむりさんのブログ記事を読んでフルサイズよさそうだな?と思っていたところ、フルサイズはいいぞという声をTwitterでも頂いたので、自転車もあることだしあちこち行った先でパシャパシャ写真でも!ということでいろいろ撮ってみました。写真のウデマエは……構図の基本が知識としてあるくらいなのでウマいわけではないんですが、色んな人の写真を見ているとなにで撮ってても「この構図いい!」と思ったら即パク真似してみたりしています。来年はもっとカメラと一緒に出かけたいなぁ。ちなみに新しいカメラはEOS 6D MarkIIで今年一番高い買い物でした。たぶん。結果フルサイズはいいぞってことで、買ってから既に2ヶ月くらいたってますがあちこちで撮りまくってます。

f:id:KeithYokoma:20171105123249j:plain

f:id:KeithYokoma:20171105124956j:plain

f:id:KeithYokoma:20171105125352j:plain

f:id:KeithYokoma:20171105125403j:plain

f:id:KeithYokoma:20171105153920j:plain

f:id:KeithYokoma:20171105154317j:plain

f:id:KeithYokoma:20171105154434j:plain

f:id:KeithYokoma:20171105154451j:plain

f:id:KeithYokoma:20171106230925j:plain

f:id:KeithYokoma:20171111130529j:plain

f:id:KeithYokoma:20171111180813j:plain

f:id:KeithYokoma:20171112152543j:plain

f:id:KeithYokoma:20171112152643j:plain

f:id:KeithYokoma:20171218134844j:plain

f:id:KeithYokoma:20171218132403j:plain

f:id:KeithYokoma:20171209142001j:plain

f:id:KeithYokoma:20171209140651j:plain

f:id:KeithYokoma:20171210130839j:plain

フルサイズはいいぞ。

レンズは標準のやつを使っています。EOS Kiss のときのものは使いまわせないのでどうにかしよう。フルサイズともなるとレンズ沼にはまったらすごい勢いでお金が溶けていくのが目に見えているので、レンタルとかで楽しむのもアリかなと思っています。一応パンケーキレンズもあるし、困るのはすごい遠い被写体に寄りたいときだけ。でも知ってるんだ、望遠レンズは暗くなりがちでその中でも明るいレンズを選ぼうとするとカメラがもう一台買えるくらいすることを……やはりレンタルだ。

SparseArray から要素を取り出したときに ClassCastException が発生するパターン

SparseArrayAndroidフレームワークにあるコレクションの一種で、Integer を key にした HashMap よりもメモリ効率がよいとされるコレクションです。

SparseArray には 2 通りの値を取り出すメソッドがあります。一つはSparseArray#get(int)もう一つはSparseArray#valueAt(int)です。 どちらのメソッドも同じint型の引数をとりますが、getメソッドの引数はkeyで渡された値をもとにバイナリサーチをかけて内部の配列のindexを決めており、valuesAtメソッドの引数はindexで値がそのまま内部の配列のindexとして扱われます。

SparseArray はまた要素を追加した後に削除することもできます。こちらも 2 通りのメソッドがあり、それぞれSparseArray#delete(int) / SparseArray#remove(int)SparseArray#removeAt(int)で、delete(int) / remove(int)の引数はkeyでこれをもとにバイナリサーチをしてアクセスすべきindexを決め、removeAt(int)の引数はindexでそのまま内部の配列のインデックスとなります。

他にも同じパターンで引数のintkeyなのかindexなのかで挙動の異なるメソッドがあります。

さてここで、一度 SparseArray に保存した値を削除し、再度取り出すことを試してみます。

SparseArray#put(int, V)で指定したkeyに保存したのち、SparseArray#remove(int)で指定したkeyに対応する値を削除、SparseArray#valueAt(int)で先頭の要素を取り出します。

val array: SparseArray<String> = SparseArray()
array.put(0, "hoge")
Log.d("SparseArray", "Value at [0] == ${array.valueAt(0)}")
array.remove(0)
Log.d("SparseArray", "Value at [0] == ${array.valueAt(0)}")

同じようなことをSparseArray#get(int)で実行する場合は次の通りで、get メソッドに渡すkeykeyAt(int)で取り出します。

val array: SparseArray<String> = SparseArray()
array.put(0, "hoge")
Log.d("SparseArray", "Value at [0] == ${array.get(array.keyAt(0))}")
array.remove(0)
Log.d("SparseArray", "Value at [0] == ${array.get(array.keyAt(0))}")

それぞれどのような結果になるかというと、SparseArray#valueAt(int)の場合は最後の行でClassCastExceptionが発生してクラッシュし、SparseArray#get(int)の場合は最後の行でログに"Value at [0] == null"と出力されます。

SparseArray では要素の削除を実行すると、内部で保持している配列の該当箇所に削除したことを示す DELETED という Object 型の定数を代入します。SparseArray は型パラメータでどの型のオブジェクトが保存されるか指定できますが、実際には内部で要素を保持している配列は Object[] です。そして SparseArray#get(int) はその場所にある要素が DELETED なら null ないしは指定した値を返すようになっていますが、SparseArray#valueAt(int)は特にそのようなチェックなしに指定した場所にある要素を返しています。これがSparseArray#valueAt(int)を使ったときにClassCastExceptionが投げられる理由です。

要素が全部なくなったのに要素にアクセスしようとするというのはよくない状況です。get(int)keyAt(int)を組み合わせて null チェックをすることでClassCastExceptionは回避できますが、根本的に並行処理に問題がある(SparseArray はスレッドセーフではない)ということなので、クラッシュレポート等で身に覚えのない ClassCastException がある時にはこのパターンを疑ってみると良いと思います。

例外をネストしたクラスとして定義するときは必ず static なネストしたクラスにする

いい具合にトラブってしまったのでメモです。

クラスの定義にはいくつかの種類がありますが、あるクラスの内部にネストした(入れ子になった)クラスを定義することができます。このとき、修飾子として static をつけたネストしたクラスと、それがないネストしたクラスのどちらも正しい記述です。

public class OuterClass {

  // static でないネストしたクラス
  public class InnerClass {

  }

  // static なネストしたクラス
  public static class StaticInnerClass {

  }
}

static の有無で何が違うかといえば、static でない方は暗黙的に外側のクラス(上記の例ではOuterClass)への参照を持つため、外側のクラスのメンバにアクセスできるという点があります。 ここで、例外を何かのクラスにネストしたクラスとして定義してみたとすると、直列化(シリアライズ)で問題が起こります。

public class OuterClass {

  // static でないネストしたクラスとして例外を定義
  public class BadException extends RuntimeException {}
}

暗黙のうちにもつ外側のクラスへの参照は transient ではないので、シリアライズの対象になります。例外はSerializableを実装しているので直列化ができますが、このBadExceptionを直列化すると外側のクラスへの参照もシリアライズの対象となるものの、肝心のOuterClassSerializableを実装していないので、直列化できずに例外が投げられます。

暗黙の内に持つ外側のクラスへの参照が引き起こす問題は、メモリリークの文脈で語られることが多いように思いますが、このように直列化でも問題となります。

public class OuterClass {

  // static なネストしたクラスとして例外を定義
  public static class MyException extends RuntimeException {}
}

例外にかぎらず、ネストしたクラスがSerializableなクラスを継承する場合も同様に、static なネストしたクラスとして定義しましょう。

RxJava1 から RxJava2 へ移行する時に nullable な値とうまくつきあう

次のような RxJava1 のコードを RxJava2 に移行することを考えます。

import rx.Observable; // RxJava1

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.fromEmitter(emitter -> emitter.onNext(nullableValue))
      .filter(nullableValue -> nullableValue != null);
}

Observable のソースとなる値は Nullable で、それを filter で null チェックをかませることで値を受け取る側は NonNull を前提にできるような感じですね。 RxJava2 では onNext() に値を渡す時点で NonNull であることを求められるため、これをそのまま RxJava2 に書き換えると実行時に例外がスローされます。

import io.reactivex.Observable; // RxJava2

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.create(emitter -> emitter.onNext(nullableValue)) // nullable な値を onNext には渡せない
      .filter(nullableValue -> nullableValue != null); // 無意味な filter になる
}

ここで、nullableValue を onNext() に渡す時点で Optional*1 を使ってラップしてみると、うまく onNext には NonNull な値を渡せるようになります。 また、その後に filter と map を駆使すると、メソッドの返り値の宣言を変更することなく RxJava2 に移行できます。

import io.reactivex.Observable; // RxJava2

private Value nullableValue;

public Observable<Value> observeValue() {
  return Observable.create(emitter -> emitter.onNext(Optional.of(nullableValue))) // Optional<Value> は NonNull
      .filter(Optional::isPresent) // RxJava1 での null チェックと同じ効果が得られる
      .map(Optional::get); // filter で値が存在することをチェックしているので安全に get できる
}

Android の場合 Optional は GitHub - memoizr/retro-optional: A backport of Java8 optionals for Java7 を使うと古い OS バージョンでも利用できます。

*1:java.util.Optional では ofNullable でラップします。バックポート版ではofでラップします。

自転車でめぐる SHIROBAKO の聖地

この記事は SHIROBAKO Advent Calendar 2017 の 17 日目の記事です。

adventar.org

SHIROBAKO には木佐さんという自転車が趣味のアニメーターさんがいます。えくそだすっ!のときには、落合がカナンの人と話をしているところを目撃したり、8 カット上げたことあるしヨユーヨユーとか言って直後に調子でないと言ったりとしれっと重要な場面に登場していました。 そんな木佐さんが発した「仕事大事!だけどヒルクライム入賞も夢なんだ!」と言う言葉には宮森と同じく自分もはっとさせられ、気がついたら今年は自分もヒルクライムレースに何度か出場していました。分かるんですよ、あの「敵はいるけど、本当の敵はむしろ自分ッ」とか「自分を追い込んで追い込んで……こうねっ」って言いたくなる気持ち。

SHIROBAKO 放送当時はあまり気が付きませんでしたが、よく見ていると自分の家の近所だったり、よく自転車で走っている場所が描かれていることに気が付きました。 そこで今回のアドベントカレンダーのネタとして「自転車でめぐる SHIROBAKO の聖地」と題し、軽い運動がてら気軽に見て回れる場所を吉祥寺駅周辺から武蔵境駅周辺にかけて実際に走ってみたレポートをしてみようとおもいます。

コースは吉祥寺サンロード入口をスタート地点に、武蔵境駅周辺を巡ったあと松亭でフィニッシュです。

行程

今回の行程です。途中徒歩の区間があるのでその部分は自転車を押し歩きしています。

スタート:吉祥寺サンロード入口

4話で、神仏混淆七福神を作ったメンバーが吉祥寺に集まったときにいた場所です。メンチカツやコロッケで有名なサトウ(作中ではすどう)、帽子をいろいろと試着していたのは SHAZBOT、映画館(作中の NEW バウスシアター)は既に取り壊されて ROUND1 になっています。サンロードは自転車をおして歩きましょう。

f:id:KeithYokoma:20171210144056j:plain

吉祥寺駅

サンロード付近で遊ぶために集合した地点です。現在クリスマスシーズン真っ只中ですので、原画売の少女のときのような雰囲気を楽しめますね!

f:id:KeithYokoma:20171210144009j:plain

マルイ:Pancake House

作中では Pancake Home になっていたお店です。マルイの1Fに入っています。反対側には the 3rd Burger があります。

f:id:KeithYokoma:20171210144519j:plain

井の頭公園:弁財天

マルイからならば七井橋通り(一番人通りのあるスタバなどが面している通り)を通って七井橋をわたり、弁財天に向かいます。 この後吉祥寺通りに出たらまっすぐ北に向かいます。途中アニメイト吉祥寺の入っているパルコ(作中時点ではパルコではなくユニクロの向かい側にあった)が右手に見えます。

f:id:KeithYokoma:20171105125352j:plain
井の頭弁財天

青梅街道・都道7号線(五日市街道)分岐点

吉祥寺通りを青梅街道との交差点まで行き左折。西にしばらく行くとジョジョなどの制作を手掛けた会社が右手に見えます。

さらにそこから西へ行くと左車線が斜めに分岐する地点に到達します。

f:id:KeithYokoma:20171210150554j:plain

この地点は11話で宮森の車がドリフトしながら走り抜けていった場所です。

この都道7号線、SHIROBAKOではかなりの頻度で登場します。 落合がカナンスタジオの人と話しをしているところを木佐さんが目撃したのもこの道です。

ちなみに青梅街道をそのまままっすぐ進んで新青梅街道との交差点まで進むと、絵麻ちゃんと井口さんが散歩に来た場所が近くにあったり、ちょくちょく背景にうつるタワー(田無ワター)があったり、 新青梅街道をずっと西へいくと1話で宮森と富ヶ谷がレースをしていた陸橋2つがあったり、ゴスロリさんのオアシスがあったり(現在閉店)します。

柳橋交差点

えくそだすっ!に出てくる交差点です。3人が武蔵野ドームを飛び出してから間もないところで出てきます。作中ではいなげやの方角から青梅街道に向かって逃げていきます。

f:id:KeithYokoma:20171210151333j:plain

今回はここを左折して武蔵境通りを武蔵境駅に向かいます。

いなげや

2期オープニングで宮森の車がグイッと曲がって井の頭通りに出るシーンにうつるいなげやは武蔵境通りと井の頭通りの交差点にあります。

f:id:KeithYokoma:20171210151601j:plain

桜橋

1期オープニングで宮森の車が朝日にむかってシュッと飛んだり宮森が自転車で走っているところなど多々使われています。

武蔵野アニメーション社屋

桜橋を超えてすぐ、現在は老人ホームが建っているあたりが武蔵野アニメーション社屋があった場所なはずです。

f:id:KeithYokoma:20171210152002j:plain

社屋すぐ南の交差点は6話で宮森の姉が絵麻ちゃんを見かけて声をかけようとした場面で使われています。

f:id:KeithYokoma:20171210152055j:plain

TAIRAYA

5話で絵麻ちゃんと宮森が買い物をしたスーパーです。作中ではAIRAYAになっています。

f:id:KeithYokoma:20171210152207j:plain

すきっぷ通り

6話で宮森の姉がきたとき、またタローと平岡が酔いつぶれたいた場所でもあります。今でも日高屋が営業を続けています。

f:id:KeithYokoma:20171210152255j:plain

武蔵境駅

ここも宮森の姉の上京シーンのほか、りーちゃんが舞茸さんの弟子になりたくて「頑張りマスタング!」と食い下がったところでもあります。 駅の西側にあるSWINGビルは木下監督が複葉機にのって飛び去るシーンにも描かれていますね。

f:id:KeithYokoma:20171210153119j:plain

武蔵野プレイス

みーちゃんが宮森と話をするシーン、13話でりーちゃんが宮森と話をするシーン等あります。

f:id:KeithYokoma:20171210152826j:plain

ウルソン

本田さんが転職した先の洋菓子屋さん(作中ではウルリン)です。

f:id:KeithYokoma:20171210152418j:plain

ちなみにこの道を少し言って亜細亜大学の裏手を曲がると油そばで有名な珍々亭があります。

境橋

1話で瀬川さんの家に緊急のカットを取りに行く場面で出てきます。 この場面で宮森が走っていった方角の都道7号線を行くと小金井公園があり、矢野さんと平岡がタイタニックに向かうときに出てくる風景でもあります。 今回はこの逆、吉祥寺方面へ向かいます。

ガスト

境橋の次の信号、関前五丁目の交差点にあります。 9話でずかちゃんのガヤアフレコ後に夕食をとっていたところです。みーちゃんがプリウスを見かけてホイールに見とれている場所でもあります。

f:id:KeithYokoma:20171210153914j:plain

ゴール:松亭

誰もが知る松亭ですね。いい運動をしたので定食を食べても大丈夫なはずです。気をつけて帰りましょう。

f:id:KeithYokoma:20171216104301j:plain

動画

youtu.be

さいごに

最終話、木佐さんの自転車のサドルがシートポストごと外されブロッコリーが刺さっていたのは、一時期すこし話題になった事件をネタにしているのでしょうか。いずれにしても最後の最後完パケてから返してもらうことが出来たようです。最終話といえば、宮森の新幹線が雪で徐行するシーンがありましたが、あれは間違いなくうちの地元のちかくで毎年恒例なので何はなくともすまんッという気持ちで見ていました。

都内にはまだまだいろんな聖地があるようです。行ったことのない場所も多いので、年が明けたらまた巡ってみようと思います。

甘い口溶けのスタウトビールを飲みました

この記事はBeer Advent Calendar 2017の9日目の記事です。

adventar.org

きっかけ

f:id:KeithYokoma:20171208225419j:plain

なにがきっかけでチョコレートビールの存在を知ったのかは忘れてしまいましたが、その後たまたま見かけたアニメでも取り上げられていて、なるほどこれは良さそうと思いアドベントカレンダーの記事にしようと購入しました。 なかなか置いているお店もないのでアマゾンでポチッとお取り寄せ。注文を受けてから瓶詰めしてくれるそうで相当気合が入っています。今回は4本セットの商品を買いました。この記事にはそのうち、Sweet Vanilla StoutとBrown Porterについて書きます。

Sweet Vanilla Stout

f:id:KeithYokoma:20171208230323j:plain

名前の通り、ほんのりバニラの香りがする甘い口当たりのスタウトビールです。もともとスタウトビールが好きで、特に柔らかい感じがある濃い目のスタウトが好みです。 そしてこのSweet Vanilla Stoutは香りもいい😋。最高です。

Brown Porter

写真撮り忘れましたorz

こちらはSweet Vanilla Stoutにくらべるとすこし苦味が強いビールです。コーヒーのような風味が苦味を出していますが、こちらも柔らかい口当たりで飲みやすいです。 コーヒー風味はあくまで風味として楽しめる感じなので、コーヒーが苦手なひとでも飲みやすい気がします。最高です。

おわりに

本当はインペリアルチョコレートスタウト買うぞって思ってたのに、気がついたら同じ醸造所の違うビールを頼んでいました。 酔った勢いでポチッとしてはいけませんね。でも美味しかったので結果よしです。

potatotips #45 で発表してきた

potatotips #45 にて WebView😇😇😇 と題して発表してきました。

speakerdeck.com

概要

ChromeCustomTabs の登場で使い所が限られてきている WebView ですが、法的情報(プライバシーポリシーや規約など)の簡素な静的 HTML を表示するために手軽に使えるため、意外に使う機会があります。ただし、WebView で表示する HTML 内に file:///android_asset/ の中にある別の HTML へのリンクが有ると、Nougat からのセキュリティ仕様によって FileUriExposureException でクラッシュする場合があります。

なぜクラッシュするのか

WebView には WebViewClient というクラスがあり、このクラスの shouldOverrideUrlLoading メソッドをオーバライドすることで、ページ内のリンクを踏んだときにアプリがどのような挙動をするかを決められます。ただしこの WebViewClient の設定は任意のため、WebView に何も設定しないことも可能です。この WebViewClient の有無がみそで、WebViewClient を WebView に設定した場合は shouldOverrideUrlLoading の返り値をもとに挙動が決められますが、WebViewClient が無い場合はリンクを踏んだときに、そのリンクを Uri として適切にハンドリングできるアプリを探しに行きます。ここで Intent に file スキームのリンクを踏んでしまうと、そのリンクが Uri になり Intent に詰め込まれてしまうため、FileUriExposureException が発生するというわけです。

ドキュメントへの記載はあるものの WebViewClient の shouldOverrideUrlLoading メソッドにしか書いていないこと、StrictMode でも検出できるものではありますが、Debug 用途にカスタマイズしている場合設定漏れで検出されなくなることから、地味に気づきにくい問題です。

FileUriExposureException と言うと、アプリが生成したファイルを直接他のアプリに共有しようとして起こるものというイメージがありますが、WebView でも起こりうるということを気に留めておきましょう。