2017-09-08 5 views
3

私は、ダガーを注入しながら新しいアーキテクチャコンポーネントViewModelをアプリケーションに追加しようとしています。 googleがhereと表示したコードに基づいています。私はそれぞれViewModelタイプのためにViewModelFactoryを避けようとしているので、Map<Class<? extends ViewModel>, Provider<ViewModel>> creatorsを受け取るViewModelFactoryを使用しました。これは@Singletonスコープの依存関係を持つViewModelsで動作します。しかし、私のViewModelsの1つは、そのフラグメントに由来する依存性を持っています。これは、そのフラグメントのモジュールです:アーキテクチャのコンポーネントViewModelsは、アクティビティ/フラグメントからのパラメータを持つダガーを挿入します。

@Module 
public abstract class DownloadIssueDialogFragmentModule { 

    @Binds 
    abstract DialogFragment dialogFragment(DownloadIssueDialogFragment dialogFragment); 

    @Provides 
    @FragmentScope 
    static Issue provideIssue(DownloadIssueDialogFragment dialogFragment) { 
     return dialogFragment.getIssue(); 
    } 
} 

そして、私のViewModelModule

@Module 
public abstract class ViewModelModule { 

    @Binds 
    abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory); 

    @Binds 
    @IntoMap 
    @ViewModelKey(DownloadIssueViewModel.class) 
    abstract ViewModel bindDownloadIssueViewModel(DownloadIssueViewModel viewModel); 

} 

短剣は、それがIssueを提供することができないと言います。 Map<Class<? extends ViewModel>, Provider<ViewModel>>はコンパイル時に作成されたようです。しかし、私は、その断片の範囲内でのみパラメータを知っているでしょう。どうすればこれを達成できますか?

ありがとうございます。

EDIT:最後に

私は別のアプローチを行ってきました。今度はViewModelごとにファクトリを作成し、ViewModelをインジェクトするのではなく、ファクトリを挿入します。

私はこのライブラリを作成しました:AutoViewModelFactory

自動的に工場を生成するには。これまでに私が見つけた最高のソリューションです。

+0

'@ Binds'と' @ Provides'を同じクラスで一緒に使うことはできません。あなたはそれらの1つを使うべきです。ちょうどfyi。 – mertsimsek

+1

@mertsimsek提供が静的である場合は、 '@ Binds'と' @ Provides'を持つことができます。私は自分自身の 'ViewModel'を使ってこれをGoogleのものに変更しようとしました(私は' ViewModel'を回転中に保持する必要がありました)。 –

答えて

2

Android ArchitectureのコンポーネントViewModelsは、Fragmentよりもスコープが大きく(永続的なライフサイクルを読みやすく)、ViewModelをFragmentのフィールドに依存させないようにする必要があります。

ただし、Issueが実行時にのみ認識され、フラグメント内のロジックによって生成されている場合は、ホルダーパターンを使用して小さな依存性サイクルの問題をエスケープすることができます。

これはsome other Dagger 2 StackOverflow questionsで議論されてきたが、あなたは、単に公共のアクセサ/ミューテータでJava Beanを定義します

class IssueHolder { 
    private Issue issue; 

    @Inject 
    IssueHolder() {} //empty explicit constructor as required by Dagger 2   

    public void setIssue(@Nullable Issue issue) { 
     this.issue = issue; 
    } 

    @Nullable 
    public Issue getIssue() { 
     return issue; 
    } 
} 

次に、あなたがあなたのViewHolderがIssueIssueHolderのではなく、直接に依存することができます:

@Inject IssueHolder issueHolder; 

public void doSomething() { 
    if (issueHolder.get() == null) { 
     throw new IllegalStateException("Expected IssueHolder to be set by IssueFragment at this point"); 
    } 
    //TODO: the logic you want here 
} 

どのようなパターンと同様に、このホルダーパターンは、容易に退化する可能性があるため、控えめに使用する必要があります。可能であれば、サイクルの可能性を排除するようにモジュールと依存関係を設計するのが最善の解決策です。

+0

ありがとうございました。私は、このソリューションは、フラグメントから、ViewModel上にオブジェクトを直接取得した後に設定することと同じになると思います。このスクリーンはディテールのようなものですが、私は 'Issue'オブジェクトが必要です。フラグメント(引数で渡されたもの)から来ています。私は、ViewModelが作成/取得され、概念的には、依存関係は正しいと思いますが、オブジェクトを取得する方法を短剣に伝える必要はありません。 –

+0

@RicardoCarrapiçoupvoteに感謝します。 ViewModelのどのコンシューマがHolderへのアクセスを制御して設定できるかを制御できるため、ViewModelで直接設定するのとまったく同じかどうかは分かりません。 –

関連する問題