パルカワ2

最近はFlutterをやっています

人によって“当たり前”は違う

自分にとって当たり前と思ってることはたくさんある。僕の場合、小学生まで家で食べる天ぷらには醤油とマヨネーズを付けて食べるのが当たり前だった。他の家は天つゆで食べると聞いてマジで驚いた。

コードにおいても、自分の中に当たり前がある。僕にとってはインデントが揃ってるのは当たり前だし、コードに対してテストがあるのも当たり前のことだと思ってる。

コードにおいての当たり前も天ぷらの話と同じように人によって違う。ある人はテストがないのが当たり前なのかもしれないし、インデントバラバラが当たり前なのかもしれない。

自分の当たり前は正しいのか、相手の当たり前はなんなのか、そしてそれは正しいのかを考える。そうやっていくことで、自分の当たり前をより良くし、お金を大量に得たい。

YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa に出た。

既存のAndroidアプリをリファクタリングをしていく話をしました。


目次

なぜMVPアーキテクチャなのか

他のアーキテクチャも考えたんですが、自分が一番理解しやすくて、他の人を巻き込めるなと思ったのがMVPアーキテクチャだったからです。
ちなみに、アーキテクチャを決める方針として以下を最初に決めていました。


MVPアーキテクチャの課題

今のところヤベ〜〜みたいなのになってない!!!!!!!!!!!!!!!!!
今後出てくる可能性はあるので、その時は話すかもしれません。

その他課題

  • テストカバレッジなどの計測まわりがちゃんと出来ていないのでやりたい
  • 静的に解析してアレコレみたいなの、小さく始めたい

ヤパチー感想

1日目しか参加してないんですが楽しかった。
Android界隈の知り合いは少ないんですが、ちょっと増えたのも嬉しかった。
またあるなら行きたい。

自分のトークに関しては、もっと元気に自由に話せばよかったという反省をしています。

「Androidチームのこれまでとこれから」という話をした。

Androidチームに新しいメンバーが増えた事とテクニカルリードという役割を僕が担う事になったので、Androidチームのメンバーに僕が現状考えている「テクニカルリードの役割と責任」「Androidチームの方針」「Androidチームの文化」について簡単にまとめました。

方針に関しては、今年のはじめに決めて結構良い感じだったので、そのまま進めるという感じです。

追記: 来てくれ!!!!!1
minne Androidアプリエンジニア(正社員) | エンジニア | 職種詳細 | GMOペパボ株式会社

RecyclerView.AdapterでViewTypeによってViewHolderを切り分ける ViewHolderBinder を作った

RecyclerView.Adapterは、Item毎にViewTypeを指定することでItem毎にViewHolderを指定出来る。なので「itemが映画ならFilmViewHolderを利用する」とか「itemがドラマならTvDramaViewHolderを利用する」とか「positionが0ならAdViewHolderを利用する」などが出来る。

ただそれをやっていると以下のようにViewHolderのインスタンスを作成するonCreateViewHolderとViewHolderを利用するonBindViewHolderがItemViewTypeが増えるたび膨らむ。

public FilmographyAdapter(Context context) {
    this.context = context;
}

...

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case VIEW_TYPE_1: {
            return new TvDramaViewHolder(LayoutInflater.from(context).inflate(R.layout.row_filmography, parent, false));
        }
        case VIEW_TYPE_2: {
            return new FilmViewHolder(LayoutInflater.from(context).inflate(R.layout.row_filmography, parent, false));
        }
        default:  {
            throw new IllegalArgumentException("みつからんかった");
        }
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    switch (holder.getItemViewType()) {
        case VIEW_TYPE_1: {
            ((TvDramaViewHolder) holder).render(items.get(position));
            break;
        }
        case VIEW_TYPE_2: {
            ((FilmViewHolder) holder).render(items.get(position));
            break;
        }
        default:  {
            throw new IllegalArgumentException("みつからんかった");
        }
    }
}

RecyclerView-Binderというのもあって、それを利用すれば上記のようにswitchで頑張る必要は なさそう。ただViewHolder以外に自分でBinderクラスを作らなければならない事, Adapterに継承が必要なことが理由で利用しなかった。

なので、aptの勉強がてら自分で作ってみた。
github.com

さっきのswitch文べったりなコードがこうなる。

public FilmographyAdapter(Context context) {
    // Binderクラスが自動で生成されるのでそれのインスタンスを持つ
    this.binder = new FilmographyAdapterViewHolderBinder(context, this);
}

...

// @ViewHolderでviewTypeとlayoutを指定してあげる
void onBindViewHolder(
        @ViewHolder(viewType = VIEW_TYPE_1, layout = R.layout.row_filmography) TvDramaViewHolder holder,
        int position) {
    holder.render(items.get(position));
}

void onBindViewHolder(
        @ViewHolder(viewType = VIEW_TYPE_2, layout = R.layout.row_filmography) FilmViewHolder holder,
        int position) {
    holder.render(items.get(position));
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // @ViewHolderで指定されたViewType, layoutを元に
    // ViewHolderのインスタンスを作成する。
    return binder.create(parent, viewType);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // @ViewHolderで指定されたViewTypeを元に利用するViewHolderのインスタンスを決めて
    // そのインスタンスにあった onBindViewHolder() を利用する。
    binder.bind(holder, position);
}

ViewHolderのインスタンスを作成する部分は、僕はいつもこう書いてるだけなので、人間が書く必要はないなと感じた。なので、全部自動生成するようにした。

return new ItemViewHolder(layoutInflater.inflate(R.layout.hogehoge, parent, false));

その結果、ViewTypeが増えれば、 対応したViewHolderとonBindViewHolder(...)を追加してやるだけになった。

まとめ
  • RecyclerViewでViewType毎にViewHolderを分けれるのは便利
  • ただ onCreateViewHolderなどが膨らんでしまい書くコードも多い
  • なので、ViewHolderBinderというのを作った

github.com

AndroidでサーバなしのA/Bテストを行う Hanaten を作った

AndroidでサーバなしのA/Bテストを行いたかったが、それらしいライブラリを見つける事が出来なかったので作った。iOSでいうmattt/SkyLabみたいなのです。

github.com

振る舞いを変えたい場合はこうする。

new Hanaten(context, "presenter test")
  .add(10, new Runnable() {
    @Override
    void run() {
      mPresenter.showRed();
    }
  })
  .add(20, new Runnable() {
    @Override
    void run() {
      mPresenter.showGreen();
    }
  })
  .add(30, new Runnable() {
    @Override
    void run() {
      mPresenter.showBlue();
    }
  })
  .start();

返ってくる値を変えたいとかはこうする。

String color = new Splitter<String>(context, "color test")
  .add(10, "Red")
  .add(20, "Green")
  .add(30, "Blue")
  .split();

github.com
ちゃんとjcenterとかに登録したほうがいいかなーと思いつつjitpack.io使ってる。

RecyclerViewにHeaderViewとFooterViewを付けられる HeaderFooterAdapter を作った

HeaderやFooterで各々ViewHolderを作る必要もなかったので、それすらないシンプルなAdapterです。
github.com

// HeaderFooterAdapterを継承する
class MovieAdapter extends HeaderFooterAdapter<ItemViewHolder> {
    private List<String> movies;
    private LayoutInflater layoutInflater;

    MovieAdapter(LayoutInflater layoutInflater) {
        this.layoutInflater = layoutInflater;
    }

    void setMovies(List<String> movies) {
        this.movies = movies;
        notifyDataSetChanged();
    }

    // ItemのViewHolderを作成して返す
    @Override
    protected ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
        return new ItemViewHolder(
                this.layoutInflater.inflate(R.layout.row_movie, parent, false));
    }

    // ItemのViewHolderを利用する
    @Override
    protected void onBindItemViewHolder(ItemViewHolder holder, int position) {
        holder.setName(this.movies.get(position));
    }

    // Itemの数を返す
    @Override
    protected int getAdapterItemCount() {
        return movies.size();
    }
}

// ActivityなどでAdapterをセットする前にHeaderViewとFooterViewをセットする
MovieAdapter adapter = new MovieAdapter(layoutInflater);
adapter.setHeaderView(headerView);
adapter.setFooterView(footerView);
recyclerview.setAdapter(adapter);

github.com