Dagger に馴染めない人のためのいくつかの原則

Keeping the Daggers Sharp ⚔️ – Square Corner Blog – Medium

Dagger2 は 素晴らしい Dependency Injection ライブラリですが, なかなか上手に使いこなせません.

分かりやすくするための考え方や実装方法をいくつか見てみましょう.

フィールドよりコンストラクタのインジェクションを使う

フィールドインジェクションは, finalでなく, privateでないフィールドに使います.


// BAD
class CardConverter {

  @Inject PublicKeyManager publicKeyManager;

  @Inject public CardConverter() {}

}

フィールドに @Inject を忘れると NullPointerException の原因となります.


// BAD
class CardConverter {

  @Inject PublicKeyManager publicKeyManager;
  Analytics analytics; // Oops, forgot to @Inject

  @Inject public CardConverter() {}

}

コンストラクタインジェクションはイミュータブルですので, 局所的な状態を持ちませんのでスレッドセーフにつながります.


// GOOD
class CardConverter {

  private final PublicKeyManager publicKeyManager;

  @Inject public CardConverter(PublicKeyManager publicKeyManager) {
    this.publicKeyManager = publicKeyManager;
  }

}

Kotlinでは, さらに簡素化してくれます.


class CardConverter

@Inject constructor(
  private val publicKeyManager: PublicKeyManager)

それでも, フィールドインジェクションを使いたい場合は以下のようになります.


public class MainActivity extends Activity {

  public interface Component {
    void inject(MainActivity activity);
  }

  @Inject ToastFactory toastFactory;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Component component = SquareApplication.component(this);
    component.inject(this);
  }

}

Singleton はあまり使う必要はない

ミュータブルな状態に対して多くのアクセスが必要な場合は Singleton は便利です.


// GOOD
@Singleton
public class BadgeCounter {

  public final Observable<Integer> badgeCount;

  @Inject public BadgeCounter(...) {
     badgeCount = ...
  }

}

しかし, 変化しない状態に対しては Singleton にする必要はありません.


// BAD, should not be a singleton!
@Singleton
class RealToastFactory implements ToastFactory {

  private final Application context;

  @Inject public RealToastFactory(Application context) {
    this.context = context;
  }

  @Override public Toast makeText(int resId, int duration) {
    return Toast.makeText(context, resId, duration);
  }

}

まれに, 作成のコストがかかるキャッシュインスタンスの走査に使うことがあります. そうすることで, 繰り返し作成して破棄することを避けることができます.

@Provides でなく @Inject を使う

@Provides はコンストラクタを複製すべきではありません.
関係する部分を一つにするとコードがわかりやすくなります.


@Module
class ToastModule {

  // BAD, remove this binding and add @Inject to RealToastFactory
  @Provides RealToastFactory realToastFactory(Application context) {
    return new RealToastFactory(context);
  }

}

このことは, 特に singleton においては重要です. そのクラスを読むときの重要な実装の内容一覧となります. 一部分をみればすべての内容が把握できます.


// GOOD, I have all the details I need in one place.
@Singleton
public class BadgeCounter {

  @Inject public BadgeCounter(...) {}

}

static @Provides を使う

@Provides は static にすることができます.


@Module
class ToastModule {

  @Provides
  static ToastFactory toastFactory(RealToastFactory factory) {
    return factory;
  }

}

モジュールインスタンスを作成する代わりに, 直接メソッドを実行することができます. このときそのメソッドの呼び出しはコンパイラによってインライン化されています.


@Generated
public final class DaggerAppComponent extends AppComponent {

  // ...
  @Override public ToastFactory toastFactory() {
    return ToastModule.toastFactory(realToastFactoryProvider.get())
  }

}

一つだけのメソッドを static にしてもあまり変化はないですが, すべてを static にすると, かなりのパフォーマンスが向上します.

また, モジュールを abstract にすると, static でない @provides メソッドが ひとつでもあるとコンパイルに失敗します.

@Provides よりも @Binds を使う

あるタイプを他にマッピングするときは @Provides でなく @Binds を使う.


@Module
abstract class ToastModule {

  @Binds
  abstract ToastFactory toastFactory(RealToastFactory factory);

}

このメソッドは abstract でなければなりません. @Generated コードは実装内容をそのまま使おうとします.


@Generated
public final class DaggerAppComponent extends AppComponent {

  // ...
  private DaggerAppComponent() {
    // ...
    this.toastFactoryProvider = (Provider) realToastFactoryProvider;
  }

  @Override public ToastFactory toastFactory() {
    return toastFactoryProvider.get();
  }

}

@Singleton の interface binding は避ける

Statefulness is an implementation detail

集中するアクセスがミュータブルな状態にアクセスする必要があるかは実装のみが知っていますので, 実装をインターフェースにバインドさせるとき, アノテーションをつけるべきではありません.


@Module
abstract class ToastModule {

  // BAD, remove @Singleton
  @Binds @Singleton

  abstract ToastFactory toastFactory(RealToastFactory factory);

}

error-prone を使おう

一般的な Dagger のエラーはこれを使うことで分かりやすく検出できます.

👉 MVVM で Hilt のパターン化 💉  hatena-bookmark


日本国内キャリアに Pixel2 はLTEバンドが対応していないのか?

最近の記事をみながら思ったりしますが,

グーグル、リファレンス機スマートフォン「Pixel 2」と「Pixel 2 XL」が日本で発売されないことに関しての深刻な状況と危機感 | GPad

Pixel2が日本で出ないとなんでAndroid開発者が死亡するのか

海外でPixelを買って日本で使っている人もいますが、技適違反になるのでがまんしました。Pixel 2はLTEのバンドが日本のキャリア向けになっていないので、そもそも違反してまで買ってもいいことはなさそうです。

「Pixel 2」を買えなくてがっかりな気持ちをGoogleさんに伝えてみた - ITmedia NEWS

Pixel など, これまでの Android では, 「北米向け」ではない「グローバル向け」の端末では日本のキャリアでも利用できる周波数設定になっていたような気がします.

Network スペックを探してみる.

GSM/EDGE: Quad-band (850, 900, 1800, 1900 MHz)
UMTS/HSPA+/HSDPA: Bands 1/2/4/5/8
CDMA EVDO Rev A: BC0/BC1/BC10
FDD-LTE : Bands 1*/2*/3*/4*/5/7*/8/12/13/17/20/25/26/28/29/30/32/66*2
TD-LTE: Bands 38*/40/412

Google Pixel 2 and Pixel 2 XL specs: Google’s vision for the modern flagship | AndroidAuthority

FDD-LTE 1, 3, 28
いけそうな気がするが...

日本で使える LTE/3G バンド は?

ああ, そうか「技適」を通過していないので使ったらダメなのか...

技適についてだけはレスしておこう。 技適があるからガラパゴスみたいなク..

以下, 参考.

その「SIMフリー端末」は実質ロックされてないか?

Google Pixel 2の予約をCloveが受付開始。予定価格は約87,300円から | そうすけブログ.com

NTTドコモ系MVNOの周波数帯(バンド) | スマホゴーゴー

格安スマホ向け、ドコモとauとソフトバンクの対応バンドのまとめ(4G / LTE / VoLTE / 3G / WCDMA / CDMA2000)

日本の大手キャリアの対応周波数バンドをチェック!

SIMフリースマホを購入するときに知っておきたい「バンド」とは | 格安スマホ・格安SIMなら【SIM通】

ご利用いただける国 - Google Store ヘルプ


【Mac】言語設定をアプリごとに行う

気持ちが悪いですよね日本語名のディレクトリ.

macのつらい日本語ホームディレクトリを英語にする - Qiita

macのホームディレクトリの日本語表記を英語表記に変更する - Qiita

そんな方法があるのだなあ,と思いつつも,OS自体の言語設定は「English」でいいや,と思い過ごしていましたが,

使用するアプリによっては,そのアプリ内には言語の設定はなく,OSの言語設定を見ながらメニューや操作のラベルをその英語で表示する.

複雑なアプリの場合,細かい操作方法をググるとなると英語では辛くなってきた.

やっぱ日本語だよなと,OSの言語設定を「日本語」に戻す.

すると,最近 Google から登場した Backup and Sync がすでに存在する「Google Drive」ディレクトリとは別に「Google ドライブ」ディレクトリを作成してクラウド上から無駄にPC内に重複するファイル群をダウンロードし始める.

当然,前述の「ダウンロード」や「ドキュメント」などの日本語名ディレクトリも復活している.

 

アプリごとに言語設定をする

OSの言語設定は「English」にしたまま,特定のアプリだけ「日本語」設定にする.

TJ-HD Software - Language Switcher

アプリごとにOSで利用可能な言語の中から言語を設定できます.

便利です.

MBP2017-mid/Sierra にて.