代码之家  ›  专栏  ›  技术社区  ›  Riley MacDonald

MVVM Dagger2组件中存在具有匹配键的绑定

  •  7
  • Riley MacDonald  · 技术社区  · 7 年前

    https://github.com/googlesamples/android-architecture-components

    下面是编译时出现的错误

    Error:(22, 8) error: [dagger.android.AndroidInjector.inject(T)] com.apps.myapp.ui.common.MainActivity cannot be provided without an @Inject constructor or from an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
    com.apps.myapp.ui.common.MainActivity is injected at
    com.apps.myapp.ui.common.NavigationController.<init>(mainActivity)
    com.apps.myapp.ui.common.NavigationController is injected at
    com.apps.myapp.ui.addContacts.AddContactsFragment.navigationController
    com.apps.myapp.ui.addContacts.AddContactsFragment is injected at
    dagger.android.AndroidInjector.inject(arg0)
    A binding with matching key exists in component: com.apps.myapp.di.ActivityModule_ContributeMainActivity.MainActivitySubcomponent
    

    @Module
    public abstract class ActivityModule {
    
        @ContributesAndroidInjector(modules = FragmentBuildersModule.class)
        abstract MainActivity contributeMainActivity();
    
        @ContributesAndroidInjector(modules = FragmentBuildersModule.class)
        abstract ContactActivity contributeContactActivity();
    } 
    

    应用组件

    @Singleton
    @Component(modules = {
            AndroidInjectionModule.class,
            AppModule.class,
            ActivityModule.class})
    public interface AppComponent {
        @Component.Builder
        interface Builder {
            @BindsInstance Builder application(Application application);
            AppComponent build();
        }
        void inject(App app);
    }
    

    应用注入器

    public class AppInjector {
        private AppInjector() {}
        public static void init(App app) {DaggerAppComponent.builder().application(app).build().inject(app);
                        app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                        @Override
                        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                            handleActivity(activity);
                        }
    
                        @Override
                        public void onActivityStarted(Activity activity) {
    
                        }
    
                        @Override
                        public void onActivityResumed(Activity activity) {
    
                        }
    
                        @Override
                        public void onActivityPaused(Activity activity) {
    
                        }
    
                        @Override
                        public void onActivityStopped(Activity activity) {
    
                        }
    
                        @Override
                        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    
                        }
    
                        @Override
                        public void onActivityDestroyed(Activity activity) {
    
                        }
                    });
        }
    
        private static void handleActivity(Activity activity) {
            if (activity instanceof HasSupportFragmentInjector) {
                AndroidInjection.inject(activity);
            }
            if (activity instanceof FragmentActivity) {
                ((FragmentActivity) activity).getSupportFragmentManager()
                        .registerFragmentLifecycleCallbacks(
                                new FragmentManager.FragmentLifecycleCallbacks() {
                                    @Override
                                    public void onFragmentCreated(FragmentManager fm, Fragment f,
                                            Bundle savedInstanceState) {
                                        if (f instanceof Injectable) {
                                            AndroidSupportInjection.inject(f);
                                        }
                                    }
                                }, true);
            }
        }
    }
    

    应用程序模块

    @Module(includes = ViewModelModule.class)
    class AppModule {
        @Singleton @Provides
        BnderAPIService provideService() {
            return new Retrofit.Builder()
                    .baseUrl("serverurl")
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(new LiveDataCallAdapterFactory())
                    .build()
                    .create(APIService.class);
        }
    
        @Singleton @Provides
        Db provideDb(Application app) {
            return Room.databaseBuilder(app, Db.class,"Db.db").build();
        }
    
        @Singleton @Provides
        UserDao provideUserDao(Db db) {
            return db.userDao();
        }
    
        @Singleton @Provides
        ContactDao provideContactDao(Db db) {
            return db.contactDao();
        }
    }
    

    @Module
    public abstract class FragmentBuildersModule {
    
        @ContributesAndroidInjector
        abstract AddContactsFragment contributeAddUserFragment();
    
        @ContributesAndroidInjector
        abstract ContactsFragment contributeContactsFragment();
    
        @ContributesAndroidInjector
        abstract ChalkboardFragment contributeChalkboardFragment();
    }
    

    可注射

    public interface Injectable {
    }
    

    @Documented
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @MapKey
    @interface ViewModelKey {
        Class<? extends ViewModel> value();
    }
    

    ViewModelModule

    @Module
    abstract class ViewModelModule {
        @Binds
        @IntoMap
        @ViewModelKey(AddContactsViewModel.class)
        abstract ViewModel bindAddContactsViewModel(AddContactsViewModel addContactsViewModel);
    
        @Binds
        @IntoMap
        @ViewModelKey(ContactsViewModel.class)
        abstract ViewModel bindContactsViewModel(ContactsViewModel contactsViewModel);
    
        @Binds
        @IntoMap
        @ViewModelKey(ChalkboardViewModel.class)
        abstract ViewModel bindChalkboardViewModel(ChalkboardViewModel chalkboardViewModel);
    
        @Binds
        abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
    }
    

    应用

    public class App extends Application implements HasActivityInjector {
    
        @Inject
        DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            MultiDex.install(this);
        }
        @Override
        public void onCreate() {
            super.onCreate();
            if (BuildConfig.DEBUG) {
    
            }
            AppInjector.init(this);
        }
    
        @Override
        public DispatchingAndroidInjector<Activity> activityInjector() {
            return dispatchingAndroidInjector;
        }
    }
    

    导航控制器

    public class NavigationController {
        private final int containerId;
        private final FragmentManager fragmentManager;
        @Inject
        public NavigationController(MainActivity mainActivity) {
            this.containerId = R.id.container;
            this.fragmentManager = mainActivity.getSupportFragmentManager();
        }
    
        public void navigateToUsers() {
            Log.i("TAG", "Navigate to users");
            String tag = "users";
            AddContactsFragment userFragment = AddContactsFragment.create();
            fragmentManager.beginTransaction()
                    .replace(containerId, userFragment, tag)
                    .addToBackStack(null)
                    .commitAllowingStateLoss();
        }
    
        public void navigateToContacts() {
            Log.i("TAG", "Navigate to contacts");
            String tag = "contacts";
            ContactsFragment contactsFragment = ContactsFragment.create();
            fragmentManager.beginTransaction()
                    .add(contactsFragment, tag)
                    .addToBackStack(null)
                    .commitAllowingStateLoss();
        }
    
        public void navigateToChalkboard() {
            Log.i("TAG", "Navigate to chalkboard");
            String tag = "chalkboard";
            ChalkboardFragment chalkboardFragment = ChalkboardFragment.create();
            fragmentManager.beginTransaction()
                    .add(chalkboardFragment, tag)
                    .addToBackStack(null)
                    .commitAllowingStateLoss();
        }
    }
    

    主要活动

    public class MainActivity extends AppCompatActivity implements LifecycleRegistryOwner, HasSupportFragmentInjector {
        private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
        @Inject
        DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
        @Inject
        NavigationController navigationController;
        private Toolbar toolbar;
        @Override
        public LifecycleRegistry getLifecycle() {
            return lifecycleRegistry;
        }
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            binding.setHandler(this);
            binding.setManager(getSupportFragmentManager());
            toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
        }
    
        @Override
        public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
            return dispatchingAndroidInjector;
        }
    
        static class ViewPagerAdapter extends FragmentPagerAdapter {
            private final List<Fragment> mFragmentList = new ArrayList<>();
            private final List<String> mFragmentTitleList = new ArrayList<>();
    
            public ViewPagerAdapter(FragmentManager manager) {
                super(manager);
            }
    
            @Override
            public Fragment getItem(int position) {
                return mFragmentList.get(position);
            }
    
            @Override
            public int getCount() {
                return mFragmentList.size();
            }
    
            public void addFragment(Fragment fragment, String title) {
                mFragmentList.add(fragment);
                mFragmentTitleList.add(title);
            }
    
            @Override
            public CharSequence getPageTitle(int position) {
                return mFragmentTitleList.get(position);
            }
        }
    
        @BindingAdapter({"handler"})
        public static void bindViewPagerAdapter(final ViewPager view, final MainActivity activity) {
            final ViewPagerAdapter adapter = new ViewPagerAdapter(activity.getSupportFragmentManager());
            adapter.addFragment(new ChalkboardFragment(), "Chalkboard");
            adapter.addFragment(new ContactsFragment(), "Contacts");
            view.setAdapter(adapter);
        }
    
        @BindingAdapter({"pager"})
        public static void bindViewPagerTabs(final TabLayout view, final ViewPager pagerView) {
            view.setupWithViewPager(pagerView, true);
        }
    }
    

    联系人活动

    public class ContactActivity extends AppCompatActivity implements LifecycleRegistryOwner, HasSupportFragmentInjector {
        private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
        @Inject
        DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
        @Override
        public LifecycleRegistry getLifecycle() {
            return lifecycleRegistry;
        }
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_contact);
            if (savedInstanceState == null) {
    
            }
        }
    
        @Override
        public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
            return dispatchingAndroidInjector;
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  22
  •   David Rawson B Aristide    6 年前

    “组件中存在具有匹配键的绑定”意味着您已将依赖项绑定到整个对象图中的某个位置,但无法从需要注入该依赖项的子组件访问该依赖项。以下是javadoc:

    A diagram of where the binding is present in the sibling component

    例如,假设您有两个活动,ActivityA和ActivityB。生成子组件时使用 @ContributesAndroidInjector Foo 在ActivityA模块中,但不在ActivityB模块中。如果您要求注射 在ActivityB中 @Inject Foo foo 您将收到该错误消息。

    在您的特定情况下,您得到的错误可以通过以下方式再现:

    1. 从GitHub克隆项目

      git clone https://github.com/googlesamples/android-architecture-components.git`
      
    2. MainActivity 放入新文件 ContactsActivity

    3. 修改 MainActivityModule 看起来像你的 ActivityModule

    因此,我们可以得出结论,您的 活动模块 是有问题的。这个 @贡献AndroidInjector docs here

    子组件可以使用父组件的绑定,但不能使用同级组件的绑定。你的两行 ContributesAndroidInjector 活动模块 一个用于 接触活性 .

    NavigationController 取决于 在对象图中为 接触活性 AddContactsFragment 已成为 接触活性 子组件,并且无法访问 不再这意味着当匕首2试图注入 在您的 添加联系人碎片 主要活动 作为对它的依赖。这解释了错误消息的“无法提供”部分。

    虽然它不能提供 AndroidInjector 大致了解 主要活动 MainActivity.class 到a MainActivityFactory 活动模块 当你写下 @贡献AndroidInjector 对于 主要活动

    导航控制器 主要活动 AppCompatActivity 因为这是你们两个活动的超类。然后你需要停止使用 并为两个活动编写显式模块,其中包括 AppCompatActivity

    然而,现在请回到最基本的,从更简单的开始。在没有完全理解的情况下,从一个复杂的项目开始,仅仅修改它,希望它能起作用,这是一种灾难。

    这个 Codepath Dagger 2 tutorial project 更容易理解,并使您熟悉匕首2中涉及的基本概念。一旦您熟悉了基本概念并理解了依赖组件和子组件,那么您可以尝试一个更难的示例。祝你好运