Ubarjohade Ubarjohade - 4 months ago 17
Android Question

Context Service race condition

My application seems to be experiencing a race condition and I am unsure why. It only happens when I first open the application. According to the error the static variable instance in MainActivity is null but I set the variable before I start the service. It almost seems that the service starts running before the onCreate finishes within the activity but I don't believe that is possible. Any ideas what is happening?

Error message

E/AndroidRuntime: FATAL EXCEPTION: Timer-

java.lang.NullPointerException
at NetworkCalls.getRequestQueue(NetworkCalls.java:42)
at NetworkCalls.addToRequestQueue(NetworkCalls.java:48)
at NetworkCalls.createLocationPost(NetworkCalls.java:72)
at StaticMethods.handleWarnings(StaticMethods.java:84)
at LocationService.checkInPost(LocationService.java:73)
at LocationService$1.run(LocationService.java:66)
at java.util.Timer$TimerImpl.run(Timer.java:284)


Networks Class

public class NetworkCalls {
private static NetworkCalls singleton;
private RequestQueue mRequestQueue;
private static Context warningCtx = WarningActivity.instance;
private static Context mCtx = MainActivity.instance;

private NetworkCalls() {
}

public static synchronized NetworkCalls getInstance() {
if (singleton == null) {
singleton = new NetworkCalls();
}
return singleton;
}

private RequestQueue getRequestQueue() {
if (mRequestQueue == null) {

//Problem here is that mCtx is null
if(mCtx == null){
Log.e("Error", "It's FUBAR");
}else {
Log.e("Error", "It's Okay");
}
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}

private <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}

public void createLocationPost(LocationData locationData) {
String url = "http://httpbin.org/post";
HashMap<String, String> params = new HashMap<>();
params.put("Token", "key");

JsonObjectRequest jsObj = new JsonObjectRequest(url, new JSONObject(params), new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
Log.i("NetworkCalls Successful", response.getJSONObject("json").toString());
} catch (JSONException ex) {
Log.i("Parsing response", "Unable to get json string");
}
// On Response recieved
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.i("NetworkCalls Error", error.toString());
}
});
addToRequestQueue(jsObj);
}


Main acivity

public class MainActivity extends AppCompatActivity {

public static MainActivity instance = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ColorDrawable colorDrawable = new ColorDrawable(Color.parseColor("#000000"));
getSupportActionBar().setBackgroundDrawable(colorDrawable);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);


instance = MainActivity.this;
startService(new Intent(getBaseContext(), QueuePostService.class));
}
}
}

Answer
private static Context mCtx = MainActivity.instance;

This is initialized when the class is loaded. It takes the value of the field at that time, not when you subsequently use it.

That means that if it is null when the classloader loads NetworkCalls, it remains null until it is reassigned.

You would need to use MainActivity.instance instead of mCtx to get the field's current value.