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 ヘルプ


パソコンやAndroid間で通信環境なしに簡単にファイルを移動する方法

以下の間でWiFiなど通信環境なしにファイル移動を迫られることとかありません?

パソコン USB 2.0/3.0 Type-A

パソコン USB 3.1 Type-C

Android USB 3.1 Type-C

海外などのネット環境の良くない旅行先や外出先,あるいは,Wifiなどの通信状態が遅い,スマホキャリアの通信料制限で時間がかかりすぎるなど...

これ便利です.


aceyoon USB Type C カードリーダー OTG対応 スマホでも使える USB2.0高速 アルミ合金制 Micro SD カード 4GB ~ 256GBまでの大容量対応 MicroSDXC / MicroSDHC規格 メモリカード ライター

USB Type-A/C対応 SDカードリーダーです.

2016年以降のMacBookPro から それ以前のパソコンへ.

いろいろなパソコンとAndroidとのファイルのやり取りに.

「OTGに対応」していること大事です.

USB OTGを知ってますか? Androidで生かせるクールな5つの使い方 | ライフハッカー[日本版]

USB OTG(USB On-The-Go)でAndroidを活用 - サンワサプライ株式会社

Easy OTG Checker - Android Apps on Google Play

そして今更驚いたのが,

「リーダー」と書いてあるが普通は 読出だけでなく書込もできる

価格.com - カードリーダーの選び方

とういうことでしたがー.