web-dev-qa-db-ja.com

AndroidでDagger 2を使用した遅延注入

私はダガー2を使い始めました。このシナリオがあります。アプリ全体(プレゼンター、API)にオブジェクトを挿入する必要はありません。

最初は提供する方法がありません。私のアプリのある段階で認証が完了するまで作成されません。

ドキュメントから http://google.github.io/dagger/

私は遅延読み込みがこれを解決する方法かもしれないと思います

@Inject 
Lazy<Grinder> lazyGrinder;

そして、次のようにして、このような値を取得します。lazyGrinder.get()。Grind();

私の質問は:

  • この後、オブジェクトを新しいオブジェクトと安全に交換できますか?
  • これを行うための他の推奨される方法はありますか?

ありがとう

12

これはLazyには適していません。 Lazyは、負荷の高いオブジェクトの初期化を遅延させる優れた方法ですが、特に必要な「安全なスワップ」動作に関して、不要または不要なセマンティクスを意味します。

簡単に言うと、Lazyは memoize をローカルに提供するプロバイダーラッパーです。

  • getを呼び出さない場合、Daggerは問題のオブジェクトを作成しません。
  • getへの最初の呼び出しは、オブジェクトインスタンスを作成して保存します。
  • getへの2回目の呼び出しでは、オブジェクトがシングルトンとしてマークされているかどうかに関係なく、同じインスタンスが返されます。

これにより、Lazyは、フィールドである(ただし、使用されることはない)高価なオブジェクトの優れた選択肢となります。ただし、参照が(意図したとおりに)変更される可能性がある場合、Lazyは単純に混乱します。最初の使用時に値を格納し、ローカルで更新することはないため、複数の古いコピーがアプリケーション内で浮いている可能性がありますいつでも「正しい」値が何であるかについて。


あなたの例からグラインダーの使用を借りるために、より良い解決策は以下を含みます:

  • を使って @Providesメソッドは、後で更新できるモジュールのフィールドを返します。注入する必要がありますProvider<Grinder>長期間有効なオブジェクトインスタンスごと。これは、Grinderへの注入された参照だけでは更新されないためです。寿命の短いオブジェクトがたくさんある場合は、これが最善の策かもしれません。

    参照は暗黙的にシングルトンですが、インスタンスを自分で制御しているため、注釈は付けられていません。 DaggerはgetGrinderメソッドを頻繁に呼び出します。

    @Module public class YourModule {
      private Grinder grinder;
    
      public void setGrinder(Grinder grinder) {
        this.grinder = grinder;
      }
    
      @Provides public Grinder getGrinder() {
        return grinder;
      }
    }
    
    /* elsewhere */
    YourModule module = new YourModule();
    YourComponent component = DaggerYourComponent.builder()
        .yourModule(module)
        .build();
    /* ... */
    module.setGrinder(latestAndGreatestGrinder);
    
  • EpicPandaForceがコメントで述べたように、現在のインスタンスを提供し、更新を可能にするシングルトンGrinderHolder、GrinderController、またはAtomicReferenceオブジェクトを作成/バインドします。この方法では、Grinderを直接注入することは不可能ですが、現在の正しいGrinderをフェッチするオブジェクトを注入することは簡単で明白です。シングルトンGrinderHolder実装が最初に要求するまでGrinderを作成しない場合は、自分でLazyシングルトンを効果的に作成したことになります。

23
Jeff Bowman

コンポーネントの作成時にオブジェクトを提供できない場合は、コンポーネントグラフに追加しないでください。それは、混乱するグラフの依存関係と矛盾を求めています。検討していることに対するより良い解決策は、@Subcomponentアプローチです。これにより、親から依存関係を継承するだけでなく、新しいコンポーネントを追加する新しいコンポーネントを作成できます。次に例を示します。

@Component
interface RegularComponent {
  @AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
  AuthenticatedComponent newAuthenticatedComponent();
}

@Subcomponent
interface AuthenticatedComponent {
  Set<Friend> friends();
  @AccountId String accountId();
}

ここで、サブコンポーネントはその親コン​​ポーネントと依存関係を共有しているため、サブコンポーネントの@AccountIdappInstanceIdを使用してアカウントID(必要な場合)を提供できます。

(accountId、authトークンなどを使用して)サブコンポーネントのモジュールに状態を提供する必要がある場合は、@Moduleにパラメーターとして渡し、private finalフィールドに保存してください。サブコンポーネントモジュールを提供する方法の詳細については、 ドキュメント内 を参照してください。

5
rdshapiro