17

I am trying to handle exceptions in my application. I am trying to log the exception, then use Toast to alert the user that there was a problem. I have this working find in all my class's that extend Activity.

However, in any class that does not extended activity I can not use the toast method as I can't get the current context. Is there a simple way to get around this or should all my class's extend Activity?

Joseph
  • 2,706
  • 6
  • 42
  • 80
  • Specifically what other structures are you working with? Many of them either have a Context already or get a context passed to them at some point. – FoamyGuy Mar 15 '12 at 17:57
  • You could make context a member field and set in the constructor. But this could lead to memory leaks, so use with caution (avoid static references to Context). – j0ntech Mar 15 '12 at 17:57
  • I've got two class's: 1) RegistrationScreen which extends Activity. 2) SocketClient which does not extend Activity. When a user submits a form in RegistrationScreen it creates a new SocketClient object, which registers via a socket connection (the bit that throws an IOException). If successful the SocketClient returns values to the RegistrationScreen class, but its when its not successful that I am struggling. Should I just make SocketClient throw the exceptions and catch them in RegistrationScreen? – Joseph Mar 15 '12 at 18:01
  • In addition to Samir's direct answer below, take a look at Roboguice. – Jeff Axelrod Mar 15 '12 at 18:01

3 Answers3

18

You just pass Context When You call Non-Activity class from Activity class call Like

YourNonActivtyClass obj = new YourNonActivtyClass(Activity.this);
Samir Mangroliya
  • 39,918
  • 16
  • 117
  • 134
  • 1
    Thanks. Just one problem though, when using the above I get "No enclosing instance of the type Activity is accessible in scope". This is from within the class that extends Activity. – Joseph Mar 15 '12 at 18:07
  • First of all android activity class is call and then you can call any class wihout Activity so no one problem in above code for me.... – Samir Mangroliya Mar 15 '12 at 18:09
  • Isn't context a very HEAVY object to be post like this? – Wooff Aug 22 '14 at 11:45
  • As HEAVY as any. Java is passing objects by reference: http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value (primitive types by value) – Mars Robertson Apr 29 '15 at 11:44
0

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.

Marc Freeman
  • 713
  • 2
  • 7
  • 30
-13

Toast messages should work within the application context, i.e.

Toast.makeText(getApplicationContext(), ...);
  • 2
    If he isn't inside an activity, then there may not be a getApplicationContext() method. Additionally there is no need to use this method. The activity object itself is a context. So using YourActivityName.this will work fine instead of getApplicationContext(); – FoamyGuy Mar 15 '12 at 18:05