Sriram R
Sriram R

Reputation: 2229

Error injecting dependencies in Fragment using Dagger 2

I am trying to use dagger 2 to inject dependencies in a fragment in my application

I have the following modules

Network Module

@Module(includes = ContextModule.class)
public class NetworkModule {

    @Provides
    Cache getCacheFile(Context context) {
        File cacheFile = new File(context.getCacheDir(), "okhttp-cache");
        return new Cache(cacheFile, 10 * 1000 * 1000);
    }

    @Provides
    HttpLoggingInterceptor getHttpLoggingInteceptor() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        return logging;
    }

    @Provides
    OkHttp3Downloader getOkHttp3Downloader(Context context) {
        return new OkHttp3Downloader(context);
    }

    @Provides
    OkHttpClient getOkHttpClient(HttpLoggingInterceptor interceptor, Cache cache) {
        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.newBuilder().cache(cache)
                .addInterceptor(interceptor)
                .build();
        return okHttpClient;

    }

    @Provides
    Retrofit getRetrofit(OkHttpClient client, GsonConverterFactory gsonConverterFactory, RxJava2CallAdapterFactory callAdapter) {
        return new Retrofit.Builder()
                .addConverterFactory(gsonConverterFactory)
                .addCallAdapterFactory(callAdapter)
                .baseUrl("https://api.themoviedb.org/3/")
                .client(client)
                .build();
    }

    @Provides
    GsonConverterFactory getGsonConverterFactory() {
        return GsonConverterFactory.create();
    }

    @Provides
    RxJava2CallAdapterFactory getRxJavaFactory() {
        return RxJava2CallAdapterFactory.create();
    }

}

ActivityBuilder Module

@Module
public abstract class ActivityBuilder {

    @ContributesAndroidInjector
    abstract NewsListActivity bindMoviesListActivity();

    @ContributesAndroidInjector(modules = NewsListFragmentModule.class)
    abstract NewsListFragment bindNewsListFragment();
}

Here is the AppComponent

@Component(modules = {AndroidSupportInjectionModule.class, ContextModule.class, ActivityBuilder.class})
public interface AppComponent {

    @Component.Builder
    interface Builder {

        @BindsInstance
        Builder application(Application application);

        AppComponent build();

    }

    void inject(NewsApp app);

}

This is my activity class where the fragment is embedded

class NewsListActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, HasSupportFragmentInjector {

    @Inject
    lateinit var mAndroidInjector: AndroidInjector<Fragment>

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news_list)
        setSupportActionBar(toolbar)

        val toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        drawer_layout.addDrawerListener(toggle)
        toggle.syncState()

        nav_view.setNavigationItemSelectedListener(this)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                    .add(R.id.frame_main, NewsListFragment(), "news-list-fragment")
                    .commit()
        }

    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return mAndroidInjector
    }

}

And finally this is my Fragment class

class NewsListFragment : Fragment() {

    lateinit var picasso: Picasso
    @Inject

    lateinit var newsService: NewsService
    @Inject

    lateinit var newsFragmentViewModel: NewsFragmentViewModel

    lateinit var newsListAdapter: NewsListAdapter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_news_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        swipe_layout.setColorSchemeColors(resources.getColor(R.color.orange),resources.getColor(R.color.green),resources.getColor(R.color.blue))

        newsListAdapter = NewsListAdapter(newsItemClickListener, picasso)
        val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

        rv_news_top_headlines.layoutManager = layoutManager
        rv_news_top_headlines.setHasFixedSize(true)
        rv_news_top_headlines.adapter = newsListAdapter

        newsFragmentViewModel = ViewModelProviders.of(this).get(NewsFragmentViewModel::class.java)

        swipe_layout.isRefreshing = true

        newsFragmentViewModel.init(newsService)
        loadDataFromApi()

        swipe_layout.setOnRefreshListener {
            loadDataFromApi()
        }

    }

    private fun loadDataFromApi() {
        // Load data from API
    }

    override fun onAttach(context: Context?) {
        AndroidSupportInjection.inject(this)
        super.onAttach(context)
    }

    private val newsItemClickListener = fun(article: Articles) {
        toast(article.title.toString())
    }

}

I followed many different tutorials to find out how to use the new AndroidInjection in Fragments so I don't have the links of those tutorials?

Is there anything I'm doing wrong?

The error

e: /Users/sriramr/Desktop/android/NewsApp/app/src/main/java/in/sriram/newsapp/di/AppComponent.java:12: error: [dagger.android.AndroidInjector.inject(T)] dagger.android.AndroidInjector<android.support.v4.app.Fragment> cannot be provided without an @Provides- or @Produces-annotated method.
public interface AppComponent {
       ^
      dagger.android.AndroidInjector<android.support.v4.app.Fragment> is injected at
          in.sriram.newsapp.ui.newslist.NewsListActivity.mAndroidInjector
      in.sriram.newsapp.ui.newslist.NewsListActivity is injected at
          dagger.android.AndroidInjector.inject(arg0)

Upvotes: 0

Views: 821

Answers (1)

Sandip Fichadiya
Sandip Fichadiya

Reputation: 3480

Try to change following in NewsListActivity

AndroidInjector<Fragment>

to:

DispatchingAndroidInjector<Fragment>

Also You haven't posted your application class. Make sure it has following things:

class NewsApp : Application(), HasActivityInjector {

    @Inject
    lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()

        DaggerAppComponent.builder()
            .application(this)
            .build()
            .inject(this)
        // some other initialization
    }

    fun activityInjector(): AndroidInjector<Activity>? {
        return activityDispatchingAndroidInjector
    }
}

Upvotes: 1

Related Questions