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

@Inject lateinit var mediaItemRepository : MediaItemRepository 

fun init() { 
    What goes here? 
} 

所以我試圖學習Dagger2,這樣我就可以讓我的應用程序更具可測試性。問題是,我已經集成了Kotlin,並且正在開發Android架構組件。我明白建設者注入是可取的,但這是ViewModel不可能的。相反,我可以使用lateinit來注入,但我很難弄清楚如何注入。使用Dagger注入ViewModel 2 + Kotlin + ViewModel

我需要爲SlideshowViewModel創建一個Component,然後注入它嗎?或者我使用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,所以我可以讓我的應用程序更可測試」 - 我會說匕首2對可測試性沒有影響,或者它的影響過小。 如果這是唯一的原因,你可能想要傳遞它。 *作爲Dagger的用戶發佈了很多關於它的教程 – Vasiliy

+0

不是你的問題的答案,但你忘了在你的'build.gradle'文件中應用插件:'kotlin-kapt''。另外,你應該使用'kapt'而不是'annotationProcessor'。 – Benjamin

回答

2

號你在哪裏聲明(使用)您的視圖模型創建的組件。它通常是一個活動/片段。 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:

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 
} 

註冊您的ViewModelModule在組件

進樣ViewModelProvider.Factory您的活動:

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

通過你的modelFactory每個ViewModelProviders。方法:

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 
     } 

    } 

匕首設置看起來並不複雜:

 

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

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

這裏的活動類(可能是片段以及),其中實際注射發生:

 

    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

與下面的代碼試試:

@Provides 
@Singleton 
fun provideRepository(): Repository { 
    return Repository(DataSource()) 
}