おでーぶでおでーぶ

いろいろ書く。いろいろ。

`Error:Build-in class shrinker and multidex are not supported yet.`

色々と開発を進めていたところ、64K問題に引っかかってしまったので Multidexを有効にしたら以下のエラーに遭遇してしまった。

Error:Build-in class shrinker and multidex are not supported yet.

文字通り build-in class shrinker と multidex は同時使用できないよという話なんだけど、build-in class shrinkerを使うようにした覚えはない。

結果から言えば 「useProguard false を明示的に呼ばない」だけで解決した。

proguard設定を作っていけば build-in class shrinker でもいいんだけれど、そうもいかない場合のために原因を一応探しておいたのでメモ。

まず、build-in class shrinkerを使うためには以下のAND条件を満たす必要があると思っていた。

  • useProguard == false
  • minifyEnabled == true

ところがそれは build-in class shrinkerによるcode minifyingを行わない状態になる、というだけであり、build-in class shrinker自体はavailableになるようだ。 このエラーはminifyする/しないを判定する以前に吐かれるらしいので、build-in class shrinkerがavailableなだけで駄目だったというわけ。

つまり「明示的にuseProguardをfalseに設定した時点で、minifyEnabledの値に関わらずbuild-in class shrinkerを利用していると認識される」ことが原因だった。

Support LibraryのPreferenceFragmentCompatでPreferenceScreenによる遷移を有効にする

TL;DR

  • AppCompatActivityはもちろん、PreferenceActivityに載せても動かない
  • 自前でハンドリングする必要があり、以下のいずれかの手法を取る必要がある。
  • ActivityにOnPreferenceStartScreenCallback や OnPreferenceStartFragmentCallback を実装させる (Activity起動 or View使い回し)
  • PreferenceFragmentCompat#onNavigateToScreen(PreferenceScreen) をoverrideする (上記をPreferenceFragmentCompat上で処理する用)
  • boolean PreferenceFragmentCompat#onPreferenceTreeClick(Preference) をoverrideする (Fragment起動用)

PreferenceFragmentCompat

Support Library v23から導入された android.support.v4.Fragment をベースにしたPreferenceFragment。
3rd party ライブラリを使って凌いでいた人も多かったはず。
そして僕が気付いたのはv24.2.0なので今更感がある。

developer.android.com

PreferenceScreenによる遷移が自動では処理されない問題

今までのPreferenceと同様、xmlによる構築が可能。
xml内にルートでないPreferenceScreenがある場合、そのPreferenceScreenをクリックすると新しい画面が開かれる仕様となっている。

公式リファレンスを見るとxmlにPreferenceScreenが書かれており、その子要素の注釈として、Next screenで表示されるものを記述できるぞ!と書いてある。

f:id:jmatsu:20160925175244p:plain

ということで、PreferenceFragmentCompatでもやってみたが、画面が開かれない・・・
でもIntentを記述するとちゃんと起動するので、click処理は諸々走っている様子。

A PreferenceScreen object should be at the top of the preference hierarchy. 
Furthermore, subsequent PreferenceScreen in the hierarchy denote a screen break -- that is 
the preferences contained within subsequent PreferenceScreen should be shown on another screen. 
The preference framework handles this by calling onNavigateToScreen(PreferenceScreen).

こうも書いてあるけど、これで「自分でハンドリングして」と読むのはちょっと厳しくない・・・?

とりあえず onNavigateToScreen はいつ呼ばれるのかというと、PreferenceScreen#onClick() で処理されていた。

    @Override
    protected void onClick() {
        if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) {
            return;
        }
        final PreferenceManager.OnNavigateToScreenListener listener =
                getPreferenceManager().getOnNavigateToScreenListener();
        if (listener != null) {
            listener.onNavigateToScreen(this);
        }
    }

ちなみに PreferenceManager#getOnNavigateToScreenListener() はPreferenceFragmentCompat自身を返し、それは OnNavigateToScreenListener を実装している。
うーん、いやしかし、これを見ると中のListenerを呼ぶ条件は満たしている。
とりあえず、Intentは別の部分で処理されていると考えて良さそうで、分けて考えて良さそう。
一個一個自分でclick listenerをbindしてもいいけど・・・それは根本解決ではないし、今後困ってしまうので探してみる。

まずPreferenceScreenをクリックしたときに呼ばれる上述のlistenerの中身は以下のようになっている.(v24.2.0)

// in PreferenceFragmentCompat.java

 /**
     * Called by
     * {@link android.support.v7.preference.PreferenceScreen#onClick()} in order to navigate to a
     * new screen of preferences. Calls
     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback#onPreferenceStartScreen}
     * if the target fragment or containing activity implements
     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback}.
     * @param preferenceScreen The {@link android.support.v7.preference.PreferenceScreen} to
     *                         navigate to.
     */
    @Override
    public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
        boolean handled = false;
        if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) {
            handled = ((OnPreferenceStartScreenCallback) getCallbackFragment())
                    .onPreferenceStartScreen(this, preferenceScreen);
        }
        if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) {
            ((OnPreferenceStartScreenCallback) getActivity())
                    .onPreferenceStartScreen(this, preferenceScreen);
        }
    }

つまり以下の条件のうち一方を満たす必要がある。

  • PreferenceFragmentCompat#callbackFragment is an instance of OnPreferenceStartScreenCallback
  • PreferenceFragmentCompat#activity is an instance of OnPreferenceStartScreenCallback

で、じゃあそこらへんどうなってるんだっけ・・・っていうと

  • PreferenceFragmentCompat#getCallbackFragment() は常にnullを返す
  • Preference用のCompatActivityはないのでAppCompatActivityを使っていたが、当然実装してない

何もしてないやんけ。自前でハンドリングが必要だなぁ・・・。

とここまでやったところで記述を見つける。

f:id:jmatsu:20160925180541p:plain

・・・うぇい!

調べたら onPreferenceTreeClick と OnPreferenceStartFragmentCallback 周りも同じ感じだった。

実装方法の検討

じゃあどれが一番いいんだろう、と。
このdocによると、Activityに実装させる方法が正攻法っぽい。
他のやり方でもできそうなので色々調べてみる。

PreferenceFragmentCompat#getCallbackFragment()Javadocを見てみると

/**
     * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib.
     * @return Fragment to possibly use as a callback
     * @hide
     */

あー・・・なんか用途が違う。実現可能ではあるけれど、提供された目的と違う方法はあまり好ましくない。
なのでやるとしたら、まあ以下の通りになりそう。

  • ActivityにOnPreferenceStartScreenCallback や OnPreferenceStartFragmentCallback を実装させる
  • onNavigateToScreen(PreferenceScreen) をoverrideする
  • boolean onPreferenceTreeClick(Preference) をoverrideする
  • getCallbackFragment() をoverrideして、OnPreferenceStartScreenCallback や OnPreferenceStartFragmentCallback を実装したFragmentを返すようにする (本来の用途と違うのでやめた方がいい)

実装にあたって

気をつけないといけないことはいくつかあるが、PreferenceScreen はParcelableでもなんでもないということを念頭に置く必要がある。
つまりそのPreferenceScreenをそのまま新しく作成するPreferenceFragmentCompatのインスタンスに渡して云々・・・は出来ない。

備忘録も兼ねて、いくつか思いついた実装を書いておく。

OnPreferenceStartScreenCallback

  • Fragment, Intentが設定されておらず、そのPreferenceScreenは1個以上の子要素を持つことが保証されている。

xmlに書いたPreferenceScreen(子孫)に表示したい全ての要素が記述してあるとき

PreferenceFragmentCompatを再利用する
boolean onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen) {
    if (pref.getKey() == null) {
        return false;
    }

    caller.setPreferencesFromResource(R.xml.setting, pref.key);
    return true;    
}

どこからか、利用されているxmlのidを拾ってくる必要はあるが、PreferenceFragmentCompatの使い回しができる。
けどback処理が面倒くさそう。

sample: https://gist.github.com/jmatsu/f7a7f0a8384c84e50ea5211e1207bd8c#file-reuse_preferencefragmentcompat-kt

PreferenceFragmentCompatを新しく作る
class HogeFragment extends PreferenceFragmentCompat {
    static PreferenceFragmentCompat newInstance(String rootKey) {
        PreferenceFragmentCompat fragment = new PreferenceFragmentCompat();
        Bundle bundle = new Bundle();
        bundle.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, rootKey);
        fragment.setArguments(bundle);
        return fragment;
    }

    void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.setting, rootKey)
    }
}

// ↓ Activityで実装する

boolean onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen) {
    if (pref.getKey() == null) {
        return false;
    }

    getSupportFragmentManager().beginTransaction().replace(R.id.container, HogeFragment.newInstance(pref.key)).addToBackstack(null).commit();
    return true;    
}

どんなFragmentで扱うかを知っている必要がある。Fragment attributeを設定してしまうと、ここが呼ばれないことを留意する必要がある。

sample: https://gist.github.com/jmatsu/f7a7f0a8384c84e50ea5211e1207bd8c#file-renew_preferencefragmentcompat-kt

PreferenceScreenのkeyとxmlのid名を対応させる

boolean onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen) {
    if (pref.getKey() == null) {
        return false;
    }

    int id = getResource().getIdentifier(pref.getKey(), "xml", getPackageName());
    caller.addPreferencesFromResource(id); // lint抑制をする
    return true;    
}

ゴリ押し。PreferenceFragmentCompatの使い回しができる。けどやっぱりback処理が面倒くさそう。

試してないけど。

OnPreferenceStartFragmentCallback

  • Fragmentが設定されていることが保証されている。

Fragment名から愚直に作成する

boolean onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference) {
    if (!(pref instanceof ScreenPreference)) {
        return false;
    }

    String fragmentName = pref.getFragment();
    // name に合わせてFragmentを生成して色々する
    return true;    
}

普通っぽい。

Android上でANTLR4がこける問題。

GraphQLライブラリのgraphql-javaAndroidで動かそうとしたらANTLR4周りでこけた。

f:id:jmatsu:20160917214736j:plain

dexOptions.preDexLibraries = false で直るよ!とかあったけど試したらエラーが増えたし、これで直るならcleanでも直りそうなもんですよね・・・

結局ANTLR4のバージョンが低くてgui依存周りがこけてるだけだったので、ANTLR4を更新しておけば解決した。

ref: https://github.com/antlr/antlr4/issues/1160

compile('com.graphql-java:graphql-java:2.1.0') {
    exclude group: 'org.antlr'
}
compile group: 'org.antlr', name: 'antlr4-runtime', version: '4.5.3'

でも結局deserializeに利用できそうになく、とりあえず依存から廃した。 一応備忘録的な感じで残しておく。

compile('com.graphql-java:graphql-java:2.1.0')

サラダうどんの影響力

www.hanamaruudon.com

みんな大好き,はなまるうどん
調子に乗らなければ安く済ませることができるし,勤務地の東京駅・京橋駅付近では重宝する.

そんなはなまるうどんにはサラダうどんがある.

コクうまサラダうどん | メニュー | はなまるうどん

小で430円,高い.厳しい.

サラダうどん大好き勢としてもさすがに厳しい.

高いのしかないなら安いのを作ればいいじゃない?

230円サラダうどん

  • かけうどん小130円 のつゆ半分
  • サラダ100円 (焙煎胡麻ドレッシングつき)

最初にかけうどんのつゆをゆっくり全部飲み干す.半分じゃないと塩分も量も何もかもがきつい.
この時点でなかなかの満腹感なのだが,このあとにサラダをうどんにぶち込んでサラダうどん化できる.
お好みで醤油をかける.ちなみに麺の生暖かさがなんとも言えない.

300円サラダうどん

  • おろししょうゆ小200円 ねぎ抜き
  • サラダ100円 (焙煎胡麻ドレッシングつき)

サラダを乗せ,好みで醤油を足す.ちなみにねぎを入れていると結構合わないので,抜いた方がよい.

おわり

これで僕の財布は守られた・・・

と,ここまでして思うのだけど,飲み代だけで10万の大台いってそうな気がするし,本来はそっちを削るべき.

サービスでもそうだけど,結局のところ,影響力の小さいところでいくら頑張っても結果はたかがしれているんだよね.
最初の段階での影響範囲や指標決め,成果予想って非常に大事で,それをなしに他人に「やれ」といったり,それを指摘しないで「やっぱりね」と調子こくのはちょっと無責任だと思うんですよね.

サラダうどんってこういう考えにも至るからすごい.美味しい.

仕事しよう.

「君の名は。」 - 繭五郎の大火の話 【ネタバレ有り】

普通に設定を勘違いしてたので、大変嘘です

君の名は。」最高ですよね.最高.早く4回目を見ないといけない気がする.小説もすごくよかった.

さて,隣の席の人と「繭五郎の大火」なる設定について軽く話したので,自分なりに考察してみる.

とはいえ映画だけでは無理で,Another sideも読む必要があると思っている.
まだAnother sideを読んでいない人は先に本を読んで欲しい.

繭五郎という名前

糸守は組紐などの糸に纏わる伝統があるということで,繭はそれの1つを示唆していそう.
一葉,二葉,三葉,四葉と来ているので,ここで五が出てくる辺り,すごく重要な関係がある.
顔どころか回想もないにも関わらず,「ぞうり屋」とまで設定があり,深掘りできそう.

・・・と見えるだけな気がする.元々はもっと掘り下げて設定が強固だったのかもしれないのだけれど.
しかしながら,少なくとも現状の立ち位置で見ると,特にストーリー上の意味はない.

映画での繭五郎の大火の意味

繭五郎の大火で古文書や元々のお宮等,様々なものが失われ,御神体は作中の山の中に移動させられた. 移動していないっぽい?繭五郎のせいでなぜあそこにあるのか分からないだけ・・・?
またそれ以前より伝えられていた舞や組紐の文様はその形だけが残り,残念ながら一子相伝の史実や意味合いといったものは失われた.
おばあちゃんの持論は「意味は消えても,形は決して消しちゃあいかん」で,実際に舞などは今でもなお行われる.

という話は映画内でも説明されている.
また繭五郎は四葉に「かわいそう」と同情されている.羨ましい.僕も同情されたい.

繭五郎の大火がなかったらどうなっていただろうか.
まず,隕石は宮水神社あたりに落下する.御神体が神社の付近にあったとしたら,全て吹き飛んでいただろう.
つまり瀧くんは御神体に辿りつけなかった可能性がある.
その場合,瀧くんは高山ラーメンを食べ,糸守高校へ行き,絶望する・・・という何とも言えない映画になっていただろう.
それで奥寺先輩と結ばれるのかもしれないが,多分新海誠監督のことだから最後はすれ違いから別れて終わる気がする.
瀧くんもきっと東京で日々弾力を失っていく日々を・・・

それはそれで見たい気もする.
ねえ知ってる?小さい隕石の落ちるスピード.秒速数百メートルなんだって.みたいな.
移動していないとしたらこの物語は存在しない・・・(´;ω;`)ウッ…

というのは冗談で,映画中では「御神体が遠いこと」の説明付けでしかない.(もちろん,このことから必要な出来事であると捉えることも可能)
また「意味は消えても,形は決して消しちゃあいかん」という言葉だけを聞けば良い話に聞こえるが,加えて「繭五郎のせいでわしにも分からん」の言葉を考えると,含まれる意味を探ろうともその価値を見出そうともしていない.ということが分かる.
ステレオタイプかもしれないが,この思考は閉鎖的な環境,田舎の負を連想させる.

Another sideによる補完を用いた繭五郎の大火

Another sideでは

  • 三葉のお父さんは元学者で,神学信仰を研究していた.宮水の失われた伝承の(彼の満足する形での)補完が研究内容.

といった内容が語られる.
その研究の一つとして宮水へ直接赴き,取材をし,そして二葉と出逢ったのだと書かれている.

しかし,繭五郎の大火がなかったらどうだろうか.

三葉のお父さんは,糸守から失われた伝承を自身の満足のいく形で補完するべく,宮水神社を訪れ,二葉と出逢う.
しかし,そもそも糸守から伝承が失われなければみつパパと二葉は出逢うことがない.

つまり繭五郎は恋のキューピッドなのである.

とまあなんか超適当になったけれど,『少なくとも今は』ストーリーとしての意味を特に持っていないと思っている.
隣の人も言っていたけれど,「最初はもっと凝った設定があって削った結果が今」という話で割と納得が言っている.

なんで削ったと考えるかというと,「わざわざ意味のない設定は持ち出さないだろう」という期待と「小説・映画とコミックとの差分」に対する考えにある.

映画や小説では口噛み酒の奉納のための山中,三葉(瀧くん)が『繭五郎って誰』と聞くと四葉は「え?有名やよ?」と返す.
それがコミックでは,四葉が『なにいってんだこいつ』みたいな対応をしている風に描かれる.
個人的にはこの差を原作が書き換えられたとする可能性の1つじゃないんかなと思っている.

というのも,原作も最初は多分コミックの流れで進んでいたんじゃないだろうか.(というかコミックが参考にした段階での原作がそれ)
そのときの設定ではもっと繭五郎の大火に対する説明や深掘りがあって,「なんでやなんで覚えてないんや」みたいな感じの四葉可愛い の対応が適切なんじゃないかと.
ただそのあと繭五郎の大火の説明を深掘りしても・・・みたいな感じで削らざるを得なくなり,「含まれる意味を探ろうともその価値を見出そうともしていない」みたいな感じをより強くするために四葉に知ったかぶりをさせたんじゃないのかな・・・と勝手に思っている.

まあ何にせよ,最高の映画だった.

「君の名は。」を見た

この感想を早く共有したい,けれどまだ見ていない人もいてそれは難しいのでここに書く.

公開初日に「君の名は。」を見た.2回.

一回目は新海誠監督の舞台挨拶つき,二回目はレイトショー.完全に勢いだった.

端的に言って,最高だった.普通に泣いた.仕事したくなかった.

絵の綺麗さは勿論のこと,音楽も良かった.個人的にストーリーも好み.

没入した,という意味では今までの作品の中でもダントツだった.

登場人物から見た景色を先に一度見せてから登場人物が入ってくるカメラワークがいくつかあったからなのかな,と勝手に思っている. ただ登場人物の目線から見るのと違い,強制的に目線を移動させられることもなく,主体的に画を捉えられた気がする.

あとレイヤーが圧倒的に多かったような. それぞれのレイヤーがある一点を中心に3次元的にひねられて動くような感じで立体感がすごかった. 奥行き感,すごい.ただ画が綺麗とかそういうんじゃなくてもう最高.

あと三葉がかわいい.声の透明感もすごい.浴衣着てる三葉ぐうかわ.

しかし・・・観てから最高しか言ってない気がする.