PreferenceFragmentCompat に ViewModel を注入する

今現在「DaggerPreferenceFragmentCompat」はありません。

MVVMなストラクチャで、

ViewModel の Fragment プロパティ への注入。

どうしてますか?

HasAndroidInjector を使う

👉 Dagger2: 2.23に入ったHasAndroidInjectorについて - stsnブログ 


class SettingsFragment : PreferenceFragmentCompat(), HasAndroidInjector {

  @Inject
  lateinit var androidInjector: DispatchingAndroidInjector<Any>

  override fun androidInjector(): AndroidInjector<Any> = androidInjector

  override fun onAttach(context: Context) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
  }

👉 android - Using Dagger2 with PreferenceFragmentCompat - Stack Overflow 

Dagger は Square が管理してたほうが良かったんじゃねか、と思う。

今から経緯を分からず入門は厳しいはず。


ApplicationComponent 実装の変遷 - Dagger2

2022-03-16 追記: 新しいDagger記事は以下リンクから

👉 MVVM で Hilt のパターン化 💉  

--------

へん‐せん【変遷】
[名](スル)時の流れとともに移り変わること。「歌もまた時代につれて変遷する」

Dagger て分かりづらいです。

タイトルがすでに謎ですが、以下のような実装のことを指しています。

アプリケーションコンテキストをオブジェクトグラフに追加する

ApplicationComponent とアプリケーションコンテキストの設定

アプリケーションコンテキスト を依存先として公開する

これまで数年に渡って変化し続けてるそんな必須の実装項目です。

ネット上をただ検索するだけでは、古い記事に最新の実装記述が埋没しています。

Dagger 2.9 以前( - 2017/02/04)


@Component(modules = [ApplicationModule::class, ...])
interface ApplicationComponent {
  ...
}


@Module
class ApplicationModule(private val applicationContext: Context) {
  @Provides fun provideApplicationContext() = applicationContext
}

これは Kotlin 記述で簡略化できます。


@Module
class ApplicationModule(@get:Provides val applicationContext: Context)


DaggerApplicationComponent
  .builder()
  .applicationModule(ApplicationModule(applicationContext))
  .build()

2.9 (2017/02/04 - ) @BindsInstance

👉 Release Dagger 2.9 · google/dagger 

We create a module that receives the application context as a constructor argument, and we create a provide method that exposes it. This works great, but then we can't have static @Provides methods anymore. And besides that, this strategy is actually going against the docs that are pretty explicit when it comes to this:

@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values.

👉 User's Guide 


@Component(modules = ...)
interface ApplicationComponent {
  @Component.Builder
  interface Builder {
    @BindsInstance 
    fun applicationContext(applicationContext: Context): Builder
    fun build(): ApplicationComponent
  }
  ...
}


DaggerApplicationComponent
  .builder()
  .applicationContext(applicationContext)
  .build()

2.22 (2019/04/03 - ) @Component.Factory

👉 Release Dagger 2.22 · google/dagger 


@Component(modules = ...)
interface ApplicationComponent {
  @Component.Factory
  interface Factory {
    fun create(@BindsInstance applicationContext: Context): ApplicationComponent
  }
  ...
}


DaggerApplicationComponent
  .factory()
  .create(applicationContext)

まとめ

最近のコード記述を理解するには、少しだけ

「成り立ちを遡ってみる」

と理解しやすいことが多いように思います。

👉 Releases · google/dagger 
👉 Dagger 2 on Android: the shiny new @Component.Factory 

2022-03-16 追記: 新しいDagger記事は以下リンクから

👉 MVVM で Hilt のパターン化 💉  


@Provides メソッドはすべて static に - Dagger2

👉 Dagger 2 on Android: The Official Guidelines You Should Be Following 

any module whose @Provides methods are all static, the implementation doesn’t need an instance at all.

すべてのモジュールの @Provides メソッドはすべて static。実装部分にインスタンスは必要ない。

👉 User's Guide 

モジュールの @Provides メソッドが static であれば、Daggerはそれをインスタンス化する必要がない。

もし、そうできないのならそのモジュールは「状態」を持っているので修正するべき。

Warning: it’s discouraged for modules to have state; this can lead to unpredictable behavior. Moreover, module instances in general are rarely useful. We have considered removing them.

警告: モジュールが「状態」を持つことは推奨されません。これにより、予期しないふるまいが発生する可能性があります。

👉 Dagger Core Semantics 

Kotlin で static なメソッドを書く場合2つの方法があります。

トップレベルで object を使う方法。


@Module
object DataModule {
  @JvmStatic @Provides fun provideDiskCache() = DiskCache()
}

companion object を使う方法。


@Module
abstract class DataModule {
  @Binds abstract fun provideCache(diskCache: DiskCache): Cache

  @Module
  companion object {
    @JvmStatic @Provides fun provideDiskCache() = DiskCache()
  }
}

👉 Kotlin+Dagger best practices/documentation/pain points · Issue #900 · google/dagger 

この2つの方法に対してJakeさんがコメントしています。

First of all, this speed benefit will be extraordinarily minor. If you are looking to make your app faster then use a profiler. You will find 100 easier optimization targets.
Beyond that, don't use companion object for modules. Use object. In that case the instance will be unused and its initialization code will be removed by R8 and the methods will be truly static and can also be inlined just like Java.

まず第一に、この速度の利点は非常にわずかです。アプリをより高速にしたい場合は、プロファイラーを使用してください。 100の簡単な最適化ターゲットがあります。
さらに、モジュールには「コンパニオンオブジェクト」を使用しないでください。オブジェクトを使用します。その場合、インスタンスは使用されず、その初期化コードはR8によって削除され、メソッドは真に静的であり、Javaと同様にインライン化することもできます。

👉 Kotlin+Dagger best practices/documentation/pain points · Issue #900 · google/dagger 

インスタンスが不要な場合、R8の静的化によりオブジェクトのメソッドを静的にすることができますが、Daggerはコンパイル時にそれらを静的にする必要があるため、@JvmStaticアノテーションは必要です。


Related Categories :  AndroidDevelopmemtKotlin


【Dagger】@Provides vs @Binds

久々に Dagger を使うとかなりの不自由感。

細かく咀嚼しないと全体は理解できない感じなので、まずはこのタイトルから。

少し、細かくシリーズ化してみますが。

👉 Dagger 2 @Binds vs @Provides - Elye - Medium 

👉 Why is @Binds different from @Provides? - Frequently Asked Questions 

Android が登場してから10年程度ですが、経緯というか、伝統というか、歴史というか。

そういうの嫌いだけど、そういうのを理解しようとしないと、その対象に対しての初心者は辛い時期になってるのだろうと思う。

Toolbar / ActionBar もそうでしょう?

シンプルにググるだけでは良いコードには辿り着けません、

この、Dagger2 2019-08-15現在 もそれと同じ雰囲気なのでしょう。

以下、上記リンクより引用。

@Binds methods must have only one parameter whose type is assignable to the return type

So only a single parameter, and the type return is typically the interface of the given parameter object.

Having said that, the other tips is consider using static function for @Provides which would help also reduce some generated codes.

@Bindsメソッドには、戻り値の型に割り当て可能な型のパラメーターが1つだけ必要です

そのため、1つのパラメーターのみが返され、型の戻り値は通常、指定されたパラメーターオブジェクトのインターフェイスです。

そうは言っても、他のヒントは、生成されるコードを減らすのに役立つ「@Provides」の「静的」関数の使用を検討することです。

@Provides, the most common construct for configuring a binding, serves three functions:
1. Declare which type (possibly qualified) is being provided — this is the return type
2. Declare dependencies — these are the method parameters
3. Provide an implementation for exactly how the instance is provided — this is the method body

While the first two functions are unique and critical to every @Provides method, the third can often be tedious and repetitive. So, whenever there is a @Provides whose implementation is simple and common enough to be inferred by Dagger, it makes sense to just declare that as a method without a body (an abstract method) and have Dagger apply the behavior.

But, if we were to just say that abstract @Provides methods should be treated as we do for @Binds methods, the specification of @Provides would basically be two specifications with a bunch of conditional logic. For example, a @Provides method can have any number of parameters of any type, but a @Binds method can only have a single parameter whose type is assignable to the return type. Separating those specifications makes it easier to reason about correctness because the annotation determines the constraints.

バインディングを構成するための最も一般的な構成要素である@Providesは、3つの機能を提供します。
 1.どのタイプ(修飾されている可能性がある)が提供されているかを宣言します—これは戻りタイプです
 2.依存関係を宣言します—これらはメソッドのパラメーターです
 3.インスタンスが提供される方法を正確に実装します—これはメソッド本体です

最初の2つの関数は一意であり、「すべての@Provides」メソッドにとって重要ですが、3番目の関数は多くの場合、退屈で反復的です。そのため、実装がDaggerによって推論されるほど単純で一般的な@Providesが存在する場合はいつでも、それを本体のないメソッド(抽象メソッド)として宣言し、Daggerに動作を適用させることが理にかなっています。

しかし、abstract @Provides methodsを@Binds methodsのように扱うべきであると言うだけなら、@Providesの仕様は基本的に条件付きロジックの束を持つ2つの仕様になります。たとえば、@Provides methodは、任意の型の任意の数のパラメーターを持つことができますが、a @Binds methodは、型が戻り型に割り当て可能な単一のパラメーターのみを持つことができます。これらの仕様を分離すると、注釈によって制約が決定されるため、正確性についての推論が容易になります。

まとめ

kotlin でいうとこの

1. 基本 @Binds を使う。
2. Application 起動の object の場合(static な singleton メソッドの場合)は @Provides を使う。

という理解でいいのか。

👉 ATM - Dagger Tutorial 


Dagger2 Dependency Graph を視覚化する「Daggraph」

クローズドな一般国内環境にて、

他人の書いたコードというのは、

非常に読みづらいものです。

Dagger のようなバイトコードを生成するコンパイルタイムなフレームワークであればさらにその特性は増してしまいます。

図で視覚化して概要を捉えましょう。



👉 dvdciri/daggraph: Dagger dependency graph generator for Android Developers 

シンプルなコマンドラインツールなので入れてみて損はないでしょう。