I came up with a solution using RXJava and Kotlin. It should be easy enough to recreate it in Java though. This is a singleton class that simply has a getter and setter. The getter creates an observable instance of context, which is set by an Activity via setContext(context: Context)
object AndroidContextObservable { // needs to be singleton.
private lateinit var context: Context
fun context(): Observable<Context> {
return Observable.create { subscriber ->
subscriber.onNext(this.context)
subscriber.onComplete()
}
}
fun setContext(context: Context) {
this.context = context
}
}
Say I have a service that uses GoogleCalendars: I'd first set it in my activity:
class ViewMeetingRoomDataActivity : AppCompatActivity() {
@Inject lateinit var googleCalendarsService: GoogleCalendarService // abstraction over the GoogleCalendarAPI Class (Below)
private var subscriptions: Disposable = Disposable.empty()
private lateinit var meetingRoomBundleData: ParcelableMeetingRoomData
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidContextObservable.setContext(this) // set the context here
DaggerServiceModuleComponent.create().inject(this)
setContentView(R.layout.activity_view_meeting_room_data)
}
override fun onPause() {
super.onPause()
this.subscriptions.dispose()
}
}
And then because the observable comes from a singleton, we can subscribe to it in the init lifecycle hook.
@Module
class GoogleCalendarsApiServiceModule @Inject constructor(): IGoogleCalendarsApiServiceModule {
private lateinit var googleCredential: Credential
private lateinit var googleCalendarService: Calendar
private val httpTransport: NetHttpTransport = NetHttpTransport()
private val subscriptions: Disposable = Disposable.empty()
init {
// Subscribe to the context
this.subscriptions.apply {
AndroidContextObservable.context()
.subscribe { context ->
googleCredential = GoogleCredentialsBuilder.getCredentials(httpTransport, context)
googleCalendarService = ServiceBuilder.buildGoogleCalendarService(googleCredential, httpTransport)
}
}
}
fun finalize() {
this.subscriptions.dispose() // <-- Don't forget to discard Observables :)
}
}
I like this solution because it's testable and the context now comes from a single source of truth. It was the cleanest way I could think of getting context outside of an Activity.
Hoped I've helped.