5

I am learning Android and I am following some guides for Retrofit2 with RxJava and Dagger2. Now I want to handle no internet connection case. I've found this answer, which seems to be elegant, but I do not understand how to apply it.

I've got some NetworkModule, with OkHttpClient provider. I assume I need to create OkHttpClient.Builder with interceptor. So it should look something like this: `

@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
    ConnectivityInterceptor ci = new ConnectivityInterceptor(networkObservable()));
    OkHttpClient.Builder.addInterceptor(ci)
    return builder.build();
}

private boolean networkObservable() {
    ConnectivityManager cm =
            (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}

This isn't working as I don't have Context. In which direction should I go - to try to obtain context there, or maybe I misunderstand the concept of observables?

Community
  • 1
  • 1
Rumid
  • 1,627
  • 2
  • 21
  • 39

3 Answers3

5

You can use the @Provides annotation in your DaggerModule to obtain application Context. Alternatively you can create a module which accepts a Context parameter in its constructor in case you need activity context. Then you can build the component in your activity and inject the arguments into it.

 @Module
public class AppModule {

    private Context context;

    public AppModule(@NonNull Context context) {
        this.context = context;
    }

    @Singleton
    @Provides
    @NonNull
    public Context provideContext(){
        return context;
    }

}

Application class:

public class PFApplication extends Application {

    private static AppComponent appComponent;

    public static AppComponent getAppComponent() {
        return appComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = buildComponent();
    }

    public AppComponent buildComponent(){
        return DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();
    }
}
James Jordan Taylor
  • 1,560
  • 3
  • 24
  • 38
Anton Kazakov
  • 2,740
  • 3
  • 23
  • 34
  • 1
    This is correct but maybe include a qualified (@Named) Context to avoid memory leaks from injecting the app context where the activity or service context should be injected – David Rawson Apr 29 '17 at 06:19
  • 1
    Would be great if you have posted the AppComponent class too. – eC Droid Jul 25 '17 at 07:09
  • 1
    @eCDroid it's just simple component with 1 module and singleton scope. Nothing more – Anton Kazakov Jul 25 '17 at 13:17
  • 1
    I tried above approach but i think there is a problem that we can't provide the application context every where instead of activity context. For instance using application context in progress dialog results in crash i.e WindowManager.BadToken exception. – eC Droid Jul 25 '17 at 13:47
  • @eCDroid its all good cause you should't use application context to show dialog – Anton Kazakov Jul 25 '17 at 15:31
  • @eCDroid so just to clarify what context do you need? – Anton Kazakov Jul 25 '17 at 15:41
  • 1
    In your PFApplication class you are building the graph with application context, so in you app where context is a dependency the application context will be provided. – eC Droid Jul 26 '17 at 11:17
  • 2
    for dialog i need the activity context. – eC Droid Jul 26 '17 at 13:15
  • How would you get an instance of DaggerAppComponent from another place in the app, like an activity, supposing that you provided AppModule(@NonNull Application application) instead of AppModule(@NonNull Context context) ? – strok_777 Sep 22 '20 at 19:17
4

Kotlin approach

You can provide the Application, which can be used to provide the `Context.

Define an AppComponent:

import android.app.Application
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton

@Component
@Singleton
interface AppComponent {
  @Component.Builder
  interface Builder {
    fun build(): AppComponent

    @BindsInstance
    fun application(application: Application): Builder
  }
}

Extend Application like so:

abstract class MyApp : Application() {
    override fun onCreate() {
       super.onCreate()
       DaggerAppComponent.builder().application(this).build()
       // ..
    }
}

Paschalis
  • 11,929
  • 9
  • 52
  • 82
1

Just wanted to supplement @anton-kazakov's answer, to provide a way to supply activity/service context in addition to application context:

ApplicationContext

import javax.inject.Qualifier;

@Qualifier
public @interface ApplicationContext {
}

ServiceScope

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Scope;

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceScope {
}

ApplicationModule

@Module
public class ApplicationModule {

    private MyApplication mMyApplication;

    public ApplicationModule(@NonNull MyApplication myApplication) {
        mMyApplication = myApplication;
    }

    @Singleton
    @Provides
    @NonNull
    @ApplicationContext
    public Context provideApplicationContext() {
        return mMyApplication;
    }
}

ServiceModule (or, activity etc.)

import dagger.Module;
import dagger.Provides;

@Module
public class ServiceModule {

    private final MyService mMyService;

    public ServiceModule(MyService myService) {
        mMyService = myService;
    }

    @ServiceScope
    @Provides
    public Context provideContext() {
        return mMyService;
    }
}

ApplicationComponent

import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

    ServiceComponent newMyServiceComponent(ServiceModule serviceModule);

    // This is optional, just putting here to show one possibility
    void inject(BootCompleteReceiver bootCompleteReceiver);

}

ServiceComponent

import dagger.Subcomponent;

@ServiceScope
@Subcomponent(modules = {ServiceModule.class})
public interface ServiceComponent {

    void inject(MyService myService);

}

Application

public class MyApplication extends Application {

    private ApplicationComponent mApplicationComponent;

    @Override
    public void onCreate() {
        // mApplicationComponent = DaggerApplicationModule.builder()
        //         .applicationModule(new ApplicationModule(this))
        //         .build();
        super.onCreate();
    }

    /**
     * Note: {@link ContentProvider#onCreate} is called before 
     * {@link Application#onCreate}, hence if you have a 
     * {@link ContentProvider}, inject here instead of 
     * in {@link Application#onCreate}.
     * <p>
     * https://stackoverflow.com/a/44413873
     * <p>
     * https://stackoverflow.com/questions/9873669/how-do-i-catch-content-provider-initialize
     *
     * @param base The base Context.
     */
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
    }

    public ApplicationComponent getApplicationComponent() {
        return mApplicationComponent;
    }
}

Service

import javax.inject.Inject;

public class MyService extends Service {

    @Override
    public void onCreate() {
        ((MyApplication) getApplicationContext())
                .getApplicationComponent()
                .newMyServiceComponent(new ServiceModule(this))
                .inject(this);
        super.onCreate();
    }
}

References

https://dagger.dev/api/2.19/dagger/Component.html https://dagger.dev/api/2.19/dagger/Module.html#subcomponents--

Yida Lin
  • 167
  • 2
  • 10
  • [@ec-droid](https://stackoverflow.com/users/5214893/ec-droid) [@strok-777](https://stackoverflow.com/users/4818812/strok-777) service/activity context can be provided as described above – Yida Lin Feb 20 '21 at 21:05