Reputation: 4389
I have some inherently asynchronous code in an Android app. I'm using RxJava2. The class Thing
is not under my control (but ThingFactory
is). In one method (createThing()
), a new Thing
is instantiated. Thing
's constructor does some work and, when it is complete, notifies us via a callback onThingInitialized()
. At the point that callback is called, we should be guaranteed that thing
exists. In the callback, I schedule work to happen on a separate thread (in this case, using RxJava2, but I don't think it should matter). There is nowhere in this code that I call anything like thing = null
. So, once it's set, it's set forever.
I threw a volatile
onto it because the instance does get updated, but never nulled. If I'm mis-using it, please feel free to berate me.
public class UsesAThing implements ThingCallbacks {
private volatile Thing thing; // I feel like I don't understand 'volatile'
// I call this method
public void createThing() {
thing = thingFactory.newThing(param1, param2);
}
// Thing's constructor does some work and notifies us when it's done
@Override
public void onThingInitialized() {
// Called on main thread, but I want to do some IO work, so:
Schedulers.io().scheduleDirect(() -> {
thing.doStuff(); // NPE!
});
}
}
How is an NPE possible there?
EDIT:
Thing
's constructor does its work asynchronously. As I said, this is in an Android environment, so the work it's actually doing is binding to a Service
. When the Service
is bound, its ServiceConnection::onServiceConnected()
callback is hit, which itself actually fires up an AsyncTask
which, in its onPostExecute()
callback, calls the onThingInitialized()
callback.
EDIT 2:
I should also note that this NPE doesn't happen all the time. I've run through this code hundreds of time, and I've only seen it occur once.
EDIT 3: Sample calling code
I didn't provide sample code because it's about as simple as one might imagine, but here's what it looks like:
Flowable.just(1)
.subscribeOn(Schedulers.io())
.subscribe(i -> createThing());
Upvotes: 0
Views: 130
Reputation: 10308
If I understand your comment, createThing()
is called in a worker thread. It's unlikely but possible that the thread scheduler will halt this worker thread after the Thing
constructor initiates the sequence of events that leads to the callback, but before newThing()
returns and thing
is assigned. If the whole callback sequence runs before the thread calling createThing()
runs again, you will see this NPE.
To test this theory, first create a test that runs repeatedly to reproduce the issue. Then change it so createThing()
is called in the main thread and see if the problem goes away. That would be a workaround, not a fix. But a real fix would involve not doing work in Thing
's constructor, which you stated is out of your control.
Upvotes: 4