我目前一直在研究Android体系结构,并试图在Google guide之后使用MVVM构建一个简单的应用程序。我按照本教程进行操作,直到进入缓存部分(此刻对我而言这并不重要)。问题是我遇到了一些无法解决的问题。
这是我的片段:
public class OutboundFragment extends Fragment {
private OutboundFlightsViewModel viewModel;
public OutboundFragment() {
// Required empty public constructor
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(OutboundFlightsViewModel.class);
viewModel.init();
viewModel.getFlights().observe(this, flights -> {
// Update UI.
});
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_outbound, container, false);
}
}
这是我的存储库:
@Singleton
public class FlightsRepository {
public LiveData<Flights> getFlights() {
final MutableLiveData<Flights> data = new MutableLiveData<>();
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<Flights> call = apiService.getFlights();
call.enqueue(new Callback<Flights>() {
@Override
public void onResponse(Call<Flights>call, Response<Flights> response) {
data.setValue(response.body());
}
@Override
public void onFailure(Call<Flights>call, Throwable t) {
// Log error here since request failed
}
});
return data;
}
}
这是我的ViewModel:
public class OutboundFlightsViewModel extends ViewModel {
private LiveData<Flights> flights;
private FlightsRepository flightsRepo;
@Inject
public OutboundFlightsViewModel(FlightsRepository flightsRepo) {
this.flightsRepo = flightsRepo;
}
public OutboundFlightsViewModel(){}
public void init() {
if (this.flights != null) {
return;
}
if (flightsRepo != null) {
flights = flightsRepo.getFlights();
}
}
public LiveData<Flights> getFlights() {
return this.flights;
}
}
这些是我在gradle文件中具有的依赖项:
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
// Dagger
implementation 'com.google.dagger:dagger:2.20'
implementation 'com.google.dagger:dagger-android-support:2.20'
annotationProcessor 'com.google.dagger:dagger-compiler:2.20'
// Retrofit, gson
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.squareup.retrofit2:retrofit:2.0.2'
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
// RecyclerView
implementation 'com.android.support:recyclerview-v7:28.0.0'
// butter knife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
最后,这是包含两个问题的logcat:
2019-02-20 13:19:12.864 3864-27637/? E/ExternalAccountType: Unsupported attribute viewStreamItemActivity
2019-02-20 13:19:13.069 1181-1181/? E/LoadedApk: Unable to instantiate appComponentFactory
java.lang.ClassNotFoundException: Didn't find class "android.support.v4.app.CoreComponentFactory" on path: DexPathList[[],nativeLibraryDirectories=[/system/app/OPBackup/lib/arm64, /system/app/OPBackup/OPBackup.apk!/lib/arm64-v8a, /system/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:169)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.LoadedApk.createAppFactory(LoadedApk.java:226)
at android.app.LoadedApk.updateApplicationInfo(LoadedApk.java:346)
at android.app.ActivityThread.handleDispatchPackageBroadcast(ActivityThread.java:5524)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at com.android.server.SystemServer.run(SystemServer.java:482)
at com.android.server.SystemServer.main(SystemServer.java:322)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838)
2019-02-20 13:19:13.092 10596-10622/com.example.mguimaraes.maxmilhas E/libc: Access denied finding property "vendor.debug.egl.profiler"
2019-02-20 13:19:13.093 10596-10622/com.example.mguimaraes.maxmilhas E/libc: Access denied finding property "vendor.debug.prerotation.disable"
2019-02-20 13:19:13.073 1181-1181/? E/LoadedApk: Unable to instantiate appComponentFactory
java.lang.ClassNotFoundException: Didn't find class "android.support.v4.app.CoreComponentFactory" on path: DexPathList[[],nativeLibraryDirectories=[/system/app/OPBackup/lib/arm64, /system/app/OPBackup/OPBackup.apk!/lib/arm64-v8a, /system/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:169)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.LoadedApk.createAppFactory(LoadedApk.java:226)
at android.app.LoadedApk.updateApplicationInfo(LoadedApk.java:346)
at android.app.ActivityThread.handleDispatchPackageBroadcast(ActivityThread.java:5524)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at com.android.server.SystemServer.run(SystemServer.java:482)
at com.android.server.SystemServer.main(SystemServer.java:322)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838)
2019-02-20 13:19:13.149 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.153 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.172 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.172 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.184 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.184 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.192 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.192 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.192 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.197 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.198 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.198 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.201 12490-12516/? E/neplus.launche: Invalid ID 0x00000000.
2019-02-20 13:19:13.343 10596-10596/com.example.mguimaraes.maxmilhas E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.mguimaraes.maxmilhas, PID: 10596
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.arch.lifecycle.LiveData.observe(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Observer)' on a null object reference
at com.example.mguimaraes.maxmilhas.Fragments.OutboundFragment.onActivityCreated(OutboundFragment.java:30)
at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:2460)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1483)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2243)
at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:654)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:146)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1244)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1092)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1622)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:733)
at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:95)
at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1556)
at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:803)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.support.v7.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:401)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1535)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:825)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:704)
at android.view.View.measure(View.java:23355)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6758)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:717)
at android.view.View.measure(View.java:23355)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2917)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1747)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2040)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1635)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7795)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1172)
at android.view.Choreographer.doCallbacks(Choreographer.java:984)
at android.view.Choreographer.doFrame(Choreographer.java:809)
2019-02-20 13:19:13.343 10596-10596/com.example.mguimaraes.maxmilhas E/AndroidRuntime: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1158)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6863)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
我错过了什么吗?我一步一步地遵循了教程。
答案 0 :(得分:0)
首先,为了将Dagger与ViewModel
配合使用来注入依赖关系,您需要创建ViewModelProvider.Factory
的实现:
@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
ViewModelFactory(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 viewmodel class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
此实现将提供ViewModel
子类的实例。
好,现在我们需要配置将提供实例的ViewModelModule
。但是在此之前,我们需要创建一个注释来标识要提供的ViewModel
的类型:
@MapKey
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
@MapKey
是Dagger的注解,用于标识@ Provides`方法的返回类型。
现在,这是我们的ViewModelModule
:
@Module
public interface ViewModelModule {
@Binds
ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
@Binds
@IntoMap
@ViewModelKey(OutboundFlightsViewModel.class)
ViewModel bindOutboundFlightsViewModel(OutboundFlightsViewModel viewModel);
}
好,所以我们需要配置组件:
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ViewModelModule.class
// other modules goes here
})
interface AppComponent extends AndroidInjector<App> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {}
}
请注意,已经安装了名为AndroidSupportInjectionModule
的特定模块。该模块是dagger.android
的一部分,其中包含许多类,这些类使Dagger for Android易于使用。
最后,我们需要初始化Dagger生成的类以创建依赖图:
public class App extends DaggerApplication {
@Override
protected AndroidInjector<? extends App> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}
请注意,App
类扩展了DaggerApplication
而不是Application
。 DaggerApplication
是dagger.android
软件包的一部分。
举一个完整的例子,我在Github上有一个使用MVVM + Dagger 2的项目。
项目:https://github.com/WellingtonCosta/android-mvvm-databinding
此外,我还有一个很小的库,可以轻松将Dagger与Android View Model一起使用。
项目:https://github.com/WellingtonCosta/viewmodel-dagger
希望对您有帮助!
答案 1 :(得分:0)
如果您发布一些匕首代码以查看发生了什么,那将是很好的。看来您没有很好地注入您的存储库。
另一方面,您在ViewModel初始化中遇到了一些麻烦。
如果调试视图模型,则会看到LiveData
始终为空,因为无法注入FlightsRepository
。您正在尝试在片段中观察它,而不检查它的状态-> NullPointerException。
首先,尽管您有存储库,也可以初始化MutableLiveData
变量:
// View Model snippet
private MutableLiveData<Flights> flights;
private FlightsRepository flightsRepo;
@Inject
public OutboundFlightsViewModel(FlightsRepository flightsRepo) {
this.flightsRepo = flightsRepo;
}
public OutboundFlightsViewModel(){}
public void init() {
flights = new MutableLiveData<Flights>;
if (flightsRepo != null) {
flights.postValue(flightsRepo.getFlights());
}
}
public LiveData<Flights> getFlights() {
return this.flights;
}
在这种情况下,您可以在片段中观察到一个空的LiveData,因此,当您修复存储库注入时,只要有值要发布,就可以对这些更改做出反应并做任何您想做的事。
关于Dagger类:
您需要您的ApplicationComponent绑定您想提供的每个模块:
@Singleton
@Component(modules = [(AndroidInjectionModule::class), (BuildersModule::class), (RepositoryModule::class)])
interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: AppController): Builder
fun build(): ApplicationComponent
}
fun inject(app: AppController)
}
您的BuildersModule类提供了将要注入的每个Activity。 如果您的活动中有需要注入的片段,也需要在此处。
@Module
abstract class BuildersModule {
@ContributesAndroidInjector(modules = [(YourViewModelModule::class)])
internal abstract fun contributeYourActivity(): YourActivity
}
在此模块中,您将声明要注入的每个存储库。
@Module
class RepositoryModule {
@Provides
fun yourRepository(): YourRepository {
return YourRepository()
}
}
这是您的视图模型模块,您需要创建视图模型工厂并将其提供以便注入。
@Module
class YourViewModelModule {
@Provides
fun providesYourViewModelFactory(yourRepository: YourRepository): YourViewModelFactory {
return YourViewModelFactory(yourRepository)
}
}
您将需要视图模型工厂,以便将参数注入到视图模型类中。
class YourViewModelFactory(private val repository: YourRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(YourViewModel::class.java)) {
return YourViewModel(repository) as T
}
throw IllegalArgumentException("unknown view model class")
}
}
这是包含片段的活动。为了注入您的片段,您需要实现HasSupportFragmentInjection。
class OutboundActivity : AppCompatActivity, HasSupportFragmentInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return dispatchingAndroidInjector
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
}
}
这是您的片段,现在它可以接收注入的视图模型工厂了。请注意,现在您的ViewModelProvider接受两个参数,分别为 this 和 viewModelFactory ,以便您的视图模型可以接收注入的参数。
class OutboundFragment: Fragment {
@Inject
lateinit var viewModelFactory: YourViewModelFactory
private var viewModel: OutboundFlightsViewModel
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AndroidSupportInjection.inject(this)
viewModel = ViewModelProviders.of(this, viewModelFactory).get(OutboundFlightsViewModel.class);
viewModel.init();
viewModel.getFlights().observe(this, flights -> {
// Update UI.
});
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_outbound, container, false);
}
}
最后但并非最不重要的一点,您需要在App类中重写onCreate方法,并使用其组件初始化匕首。注意,这里需要实现HasActivityInjection。
class AppController : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
// Registered a global instance of AppController
appControllerInstance = this
// Init Dagger
DaggerApplicationComponent.builder()
.application(this)
.build()
.inject(this)
}
override fun activityInjector(): AndroidInjector<Activity>? {
return dispatchingAndroidInjector
}
}
对不起,我每天都在Kotlin中写这篇文章,而我却忘记了您使用Java进行编码。但请不要害怕,类似,您会毫无问题地理解。 如果您听不懂,请告诉我。