2017-05-30 126 views
27

我有一個ViewModel類,就像連接ViewModel和存儲庫部分Architecture guide中定義的一樣。當我運行我的應用程序,我得到一個運行時異常。有誰知道如何解決這個問題?我應該不注入ViewModel嗎?有沒有辦法告訴ViewModelProvider使用Dagger創建模型?Android生命週期庫ViewModel使用匕首2

public class DispatchActivityModel extends ViewModel { 

    private final API api; 

    @Inject 
    public DispatchActivityModel(API api) { 
     this.api = api; 
    } 
} 

產生的原因:java.lang.InstantiationException:java.lang.Class中沒有零參數的構造函數 在java.lang.Class.newInstance(本機方法) 在android.arch.lifecycle.ViewModelProvider $ NewInstanceFactory.create(ViewModelProvider.java:143) 在android.arch.lifecycle.ViewModelProviders $ DefaultFactory.create(ViewModelProviders.java:143) 在android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:128) 在android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:96) at com.example.base.BaseActivity.on創建(BaseActivity.java:65) at com.example.dispatch.DispatchActivity.onCreate(DispatchActivity.java:53) at android.app.Activity.performCreate(Activity.java:6682) at android.app.Instrumentation。 callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727) at android.app.ActivityThread.-wrap12( ActivityThread.java) 在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1478) 在android.os.Handler.dispatchMessage(Handler.java:102) 在android.os.Looper.loop(活套。 java:154) at android.app.ActivityThread.main(ActivityThrea d.java:6121)

+0

你正在尋找https://developer.android。 com/reference/android/arch/lifecycle/ViewModelProvider.Factory.html – EpicPandaForce

回答

39

您需要實現自己的ViewModelProvider.Factory。 Google創建了一個示例應用程序,演示如何將Dagger 2與ViewModels連接起來。 LINK。你需要的4兩件事:

在視圖模型:

@Inject 
public UserViewModel(UserRepository userRepository, RepoRepository repoRepository) { 

在ViewModelModule:

@Module 
abstract class ViewModelModule { 
    @Binds 
    @IntoMap 
    @ViewModelKey(UserViewModel.class) 
    abstract ViewModel bindUserViewModel(UserViewModel userViewModel); 

在片段:

@Inject 
ViewModelProvider.Factory viewModelFactory; 

@Override 
public void onActivityCreated(@Nullable Bundle savedInstanceState) { 
      userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class); 

廠:

@Singleton 
public class GithubViewModelFactory implements ViewModelProvider.Factory { 
    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators; 

    @Inject 
    public GithubViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) { 
     this.creators = creators; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends ViewModel> T create(Class<T> modelClass) { 
     Provider<? extends ViewModel> creator = creators.get(modelClass); 
     if (creator == null) { 
      for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) { 
       if (modelClass.isAssignableFrom(entry.getKey())) { 
        creator = entry.getValue(); 
        break; 
       } 
      } 
     } 
     if (creator == null) { 
      throw new IllegalArgumentException("unknown model class " + modelClass); 
     } 
     try { 
      return (T) creator.get(); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 
+3

歡迎使用Stack Overflow。一個可能的解決方案的鏈接總是受歡迎的,但請在鏈接上添加上下文,以便您的同行用戶可以瞭解它是什麼以及它爲什麼在那裏。如果目標網站無法訪問或永久離線,請始終引用重要鏈接中最相關的部分。看看[爲什麼和如何刪除一些答案?](https://stackoverflow.com/help/deleted-answers)和[我如何寫出一個好的答案?](https://stackoverflow.com/help/how-to-answer) – Gary99

+3

您還需要定義@ViewModelKey https://github.com/googlesamples/android-architecture-components/blob/388a10dd5a814ba6aaa9bf8dee8e7c1c5840b3a5/GithubBrowserSample/app/src/main/java/com/android/example /github/di/ViewModelKey.java – nmu

+2

你也必須聲明: '@Binds抽象ViewModelProvider.Factory bindViewModelFactory(GithubViewModelFactory廠);' 在ViewModelModule –

4

我相信如果你不想使用羅伯特答案中提到的工廠,還有第二種選擇。這不一定是更好的解決方案,但知道選項總是很好的。

您可以使用默認構造函數保留viewModel並注入您的依賴項,就像在系統創建活動或其他元素的情況下一樣。 實施例:

視圖模型:

public class ExampleViewModel extends ViewModel { 

@Inject 
ExampleDependency exampleDependency; 

public ExampleViewModel() { 
    DaggerExampleComponent.builder().build().inject(this); 
    } 
} 

組件:

@Component(modules = ExampleModule.class) 
public interface ExampleComponent { 

void inject(ExampleViewModel exampleViewModel); 

} 

模塊:

@Module 
public abstract class ExampleModule { 

@Binds 
public abstract ExampleDependency bindExampleDependency(ExampleDependencyDefaultImplementation exampleDependencyDefaultImplementation); 

} 

乾杯, 彼得

+0

但是這並沒有解決如何在單元測試中初始化具有模擬依賴關係的ViewModel –

3

我想提供第三個選項,以幫助任何人絆倒這個問題。 Dagger ViewModel library將允許您使用ViewModels以類似Dagger2的方式注入,並可選擇指定ViewModel的作用域。

它消除了很多的樣板,並且還允許的ViewModels以聲明的方式使用註釋被注入:

@InjectViewModel(useActivityScope = true) 
public MyFragmentViewModel viewModel; 

它還需要的代碼量小,以設置一個模塊從其中完全依賴注入的ViewModels可以被生成,之後就像調用一樣簡單:

void injectFragment(Fragment fragment, ViewModelFactory factory) { 
    ViewModelInejectors.inject(frag, viewModelFactory); 
} 

在ViewModelInjectors類上生成。

免責聲明:這是我的圖書館,但我相信它也對這個問題的作者和任何想要實現相同目的的人有用。

2

什麼可能不會在這個問題明顯是視圖模型不能注入,那是因爲你從

ViewModelProvider.of(LifecycleOwner lo) 

方法只用LifecycleOwner參數得到ViewModelProvider默認出廠只能實例化一個視圖模型是有一個無參數的默認構造函數。在構造函數「API」:

你有一個PARAM

public DispatchActivityModel(API api) { 

爲了做到這一點,你需要創建一個工廠,讓你可以告訴它如何建立自己。谷歌示例代碼爲您提供了Dagger配置和工廠代碼,如接受的答案中所述。

DI的創建是爲了避免在依賴項上使用new()運算符,因爲如果實現發生更改,則每個引用都必須更改。 ViewModel實現明智地使用了一個靜態工廠模式,這個模式已經和ViewProvider.of()。get()一起使用,這使得在沒有參數構造函數的情況下它的注入是不必要的。所以在這種情況下你不需要寫工廠,你當然不需要注入工廠。

5

今天我學到了方法,以避免不必編寫工廠爲我ViewModel類:

class ViewModelFactory<T : ViewModel> @Inject constructor(
    private val viewModel: Lazy<T> 
) : ViewModelProvider.Factory { 
    @Suppress("UNCHECKED_CAST") 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T = viewModel.get() as T 
} 

而不是注入ViewModel,你可以注入一個通用ViewModelFactory到你的活動和片段,並獲得其中任意一個實例ViewModel

class MyActivity : Activity() { 

    @Inject 
    internal lateinit var viewModelFactory: ViewModelFactory<MyViewModel> 
    private lateinit var viewModel: MyViewModel 

    override fun onCreate(savedInstanceState: Bundle?) { 
     AndroidInjection.inject(this) 
     super.onCreate(savedInstanceState) 
     this.viewModel = ViewModelProviders.of(this, viewModelFactory) 
      .get(MyViewModel::class.java) 
     ... 
    } 

    ... 
} 

我用AndroidInjection.inject(this)作爲與dagger-android庫,但你可以注入你的活動或片段,你喜歡的方式。所有剩下的是要確保您的ViewModel從一個模塊提供:

@Module 
object MyModule { 
    @JvmStatic 
    @Provides 
    fun myViewModel(someDependency: SomeDependency) = MyViewModel(someDependency) 
} 

或應用上@Inject註釋給它的構造:

class MyViewModel @Inject constructor(
    someDependency: SomeDependency 
) : ViewModel() { 
    ... 
} 
+1

inline fun ViewModelFactory .get(activity:FragmentActivity):T = ViewModelProviders.of(activity,this).get (T :: class.java) – nxdrvr