2017-06-23 16 views
9
class SlideshowViewModel : ViewModel() { 

@Inject lateinit var mediaItemRepository : MediaItemRepository 

fun init() { 
    What goes here? 
} 

私はDagger2を学習しようとしていますので、アプリケーションをよりテスト可能にすることができます。問題は、私はすでにKotlinを統合していて、AndroidのArchitecturalコンポーネントに取り組んでいます。私はコンストラクタの注入が望ましいことを理解していますが、これはViewModelでは不可能です。代わりに、私はlateinitを注射するために使用することができますが、私は注射する方法を理解することができません。Daggerを使用してViewModelを注入する2 + Kotlin + ViewModel

SlideshowViewModelComponentを作成して注入する必要がありますか?または、Applicationコンポーネントを使用しますか?

のGradle:

apply plugin: 'com.android.application' 
apply plugin: 'kotlin-android' 

kapt { 
    generateStubs = true 
} 
dependencies { 
    compile "com.google.dagger:dagger:2.8" 
    annotationProcessor "com.google.dagger:dagger-compiler:2.8" 
    provided 'javax.annotation:jsr250-api:1.0' 
    compile 'javax.inject:javax.inject:1' 
} 

アプリケーションコンポーネント

@ApplicationScope 
@Component (modules = PersistenceModule.class) 
public interface ApplicationComponent { 

    void injectBaseApplication(BaseApplication baseApplication); 
} 

BaseApplication

private static ApplicationComponent component; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 

     component = DaggerApplicationComponent 
       .builder() 
       .contextModule(new ContextModule(this)) 
       .build(); 
     component.injectBaseApplication(this); 
    } 

    public static ApplicationComponent getComponent() { 
     return component; 
    } 
+0

「だから私はダガー2を学ぼうとしているので、アプリをもっとテスト可能にすることができます。 Dagger 2はテスト容易性に影響を与えないか、またはその効果が過小であると言います。 それが唯一の理由なら、それを渡すことができます。 *多くのチュートリアルを公開したDaggerのユーザーと話す – Vasiliy

+0

あなたの質問に答えられませんが、 'build.gradle'ファイルに' apply klugin:kotlin-kapt''を忘れました。また、 'annotationProcessor'の代わりに' kapt'を使うべきです。 – Benjamin

答えて

2

あなたはあなたのViewModel(使用)宣言されているコンポーネントを作成号。これは、通常、アクティビティ/フラグメントです。 viewModelには依存関係(mediaitemrepository)があるため、ファクトリが必要です。このような何か:

class MainViewModelFactory (
      val repository: IExerciseRepository): ViewModelProvider.Factory { 

     @Suppress("UNCHECKED_CAST") 
     override fun <T : ViewModel?> create(p0: Class<T>?): T { 
      return MainViewModel(repository) as T 
     } 
    } 

そして、短剣の一部(活動モジュール)

@Provides 
    @ActivityScope 
    fun providesViewModelFactory(
      exerciseRepos: IExerciseRepository 
    ) = MainViewModelFactory(exerciseRepos) 

    @Provides 
    @ActivityScope 
    fun provideViewModel(
      viewModelFactory: MainViewModelFactory 
    ): MainViewModel { 
     return ViewModelProviders 
       .of(act, viewModelFactory) 
       .get(MainViewModel::class.java) 
    } 
+0

これには注意してください。私は同様の設定を使用していましたが、私の 'ViewModel'は設定の変更が生き残っていないことに気付きました。 'ViewModel'は、' Activity'がスコープされていなければならないので、 'Activity'が破壊されて再作成されたときに使用することができます。 – Brucelet

+0

フラグメントとビューモデルであなたの答えをどのように使っていますか?少しの情報を提供してください。 –

2

あなたはあなたのためのviewmodelsコンストラクタ・インジェクションを有効にすることができます。 Google samplesをチェックして、Javaでそれを行う方法を確認することができます。

ViewModelKey注釈追加:追加

import android.arch.lifecycle.ViewModel 

import java.lang.annotation.Documented 
import java.lang.annotation.ElementType 
import java.lang.annotation.Retention 
import java.lang.annotation.RetentionPolicy 
import java.lang.annotation.Target 

import dagger.MapKey 
import kotlin.reflect.KClass 

@Suppress("DEPRECATED_JAVA_ANNOTATION") 
@Documented 
@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
@MapKey 
internal annotation class ViewModelKey(val value: KClass<out ViewModel>) 

をViewModelFactory:

import android.arch.lifecycle.ViewModel 
import android.arch.lifecycle.ViewModelProvider 

import javax.inject.Inject 
import javax.inject.Provider 
import javax.inject.Singleton 

@Singleton 
class ViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>> 
) : ViewModelProvider.Factory { 

    @Suppress("UNCHECKED_CAST") 
    override fun <T : ViewModel> create(modelClass: Class<T>): T { 
     var creator: Provider<out ViewModel>? = creators[modelClass] 

     if (creator == null) { 
      for ((key, value) in creators) { 
       if (modelClass.isAssignableFrom(key)) { 
        creator = value 
        break 
       } 
      } 
     } 

     if (creator == null) { 
      throw IllegalArgumentException("unknown model class " + modelClass) 
     } 

     try { 
      return creator.get() as T 
     } catch (e: Exception) { 
      throw RuntimeException(e) 
     } 
    } 
} 

追加ViewModelModule:ここ

はKotlinで同様のことを行う方法です

import dagger.Module 
import android.arch.lifecycle.ViewModel 
import dagger.multibindings.IntoMap 
import dagger.Binds 
import android.arch.lifecycle.ViewModelProvider 
import com.bubelov.coins.ui.viewmodel.EditPlaceViewModel 

@Module 
abstract class ViewModelModule { 
    @Binds 
    @IntoMap 
    @ViewModelKey(EditPlaceViewModel::class) // PROVIDE YOUR OWN MODELS HERE 
    internal abstract fun bindEditPlaceViewModel(editPlaceViewModel: EditPlaceViewModel): ViewModel 

    @Binds 
    internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory 
} 

コンポーネントあなたの活動の

を注入ViewModelProvider.FactoryであなたのViewModelModuleを登録します。

@Inject lateinit var modelFactory: ViewModelProvider.Factory 
private lateinit var model: EditPlaceViewModel 

は各ViewModelProvidersにごmodelFactoryを渡します。この方法の:ここで

model = ViewModelProviders.of(this, modelFactory)[EditPlaceViewModel::class.java] 

サンプルが必要なすべての変更を含むコミットです:Support constructor injection for view models

1

あなたはダガーで注入することができるRepositoryクラスとRepositoryに依存しているMyViewModelクラスを持っていると仮定すると、などのように定義:

 

    class Repository @Inject constructor() { 
     ... 
    } 

    class MyViewModel @Inject constructor(private val repository: Repository) : ViewModel() { 
     ... 
    } 
 

は、今すぐあなたのViewModelProvider.Factory実装を作成することができます

class MyViewModelFactory @Inject constructor(private val myViewModelProvider: Provider<MyViewModel>) : ViewModelProvider.Factory { 

     @Suppress("UNCHECKED_CAST") 
     override fun <T : ViewModel> create(modelClass: Class<T>): T { 
     return myViewModelProvider.get() as T 
     } 

    } 

ダガーセットアップはあまりにも複雑に見えるしません:

 

    class MainActivity : AppCompatActivity() { 

     @Inject 
     lateinit var factory: ViewModelProvider.Factory 
     lateinit var viewModel: MyViewModel 

     override fun onCreate(savedInstanceState: Bundle?) { 
     super.onCreate(savedInstanceState) 
     setContentView(R.layout.activity_main) 

     // retrieve the component from application class 
     val component = MyApplication.getComponent() 
     component.inject(this) 

     viewModel = ViewModelProviders.of(this, factory).get(MyViewModel::class.java) 
     } 

    } 
 
1

以下のコードで試してみてください。

 

    @Component(modules = [MyModule::class]) 
    interface MyComponent { 
     fun inject(activity: MainActivity) 
    } 

    @Module 
    abstract class MyModule { 
     @Binds 
     abstract fun bindsViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory 
    } 
 

ここで実際の噴射が行われるアクティビティクラスは、(同様の断片であるかもしれない)、です:

@Provides 
@Singleton 
fun provideRepository(): Repository { 
    return Repository(DataSource()) 
} 
関連する問題