Search code examples
androidandroid-serviceandroid-broadcast

How to execute an asynchronous web request from a BroadcastReceiver in Android


I have a WakefulBroadcastReceiver in my Android application, which successfully receives incoming SMS to the device. Following this, what I am attempting to do, is to submit a request to my web server, using the Google Volley library. The issue, is that Volley makes this web request asynchronously. As a result, the "onReceive" method code in the BroadcastReceiver is completed before the asynchronous web request is completed. I understand that this is an issue when using the BroadcastReceiver with asynchronous requests, but I'm making that request through a service, (instead of directly in the BroadcastReceiver), so I don't understand why this is happening.

I receive the below NULLPOINTEREXCEPTION, which I'm assuming is because the context of the BroadcastReceiver has already been destroyed before the request is completed:


11-04 19:13:43.465  32385-32441/com.niiche.pinch E/Volley﹕ [5542] NetworkDispatcher.run: Unhandled exception java.lang.NullPointerException
java.lang.NullPointerException
        at libcore.net.UriCodec.encode(UriCodec.java:132)
        at java.net.URLEncoder.encode(URLEncoder.java:57)
        at com.android.volley.Request.encodeParameters(Request.java:456)
        at com.android.volley.Request.getBody(Request.java:442)
        at com.android.volley.toolbox.HurlStack.addBodyIfExists(HurlStack.java:236)
        at com.android.volley.toolbox.HurlStack.setConnectionParametersForRequest(HurlStack.java:210)
        at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:106)
        at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:96)
        at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:110)

Below is the code for the WakefulBroadcastReceiver (SmsReceiver):

public void onReceive(Context context, Intent intent) {
    //retrieve the SMS PDUs, from the intent extras
    Bundle pdusBundle = intent.getExtras();
    Object[] pdus = (Object[])pdusBundle.get("pdus");

    //create sms message object from the PDU
    SmsMessage message = SmsMessage.createFromPdu((byte[])pdus[0]);

    Intent service = new Intent(context, SmsIntentService.class);
    service.putExtra(SmsIntentService.EXTRA_SMS_MESSAGE, message.getMessageBody());

    //start the service, keeping the device awake while it is launching
    startWakefulService(context, service);
    setResultCode(Activity.RESULT_OK);
}//End method

Below is the code for the IntentService (SmsIntentService):

protected void onHandleIntent(Intent intent) {
    if (intent != null) {
        Bundle extras = intent.getExtras();

        if(!extras.isEmpty()){ // has the effect of unparcelling the bundle
            String smsMessage = extras.getString(EXTRA_SMS_MESSAGE);

            submitVolleyServerRequestAsync(smsMessage);
        }//End if
    }//End if

    // Release the wake lock provided by the WakefulBroadcastReceiver
    SmsReceiver.completeWakefulIntent(intent);
}//End method

Any assistance that can be provided here is greatly appreciated. Thanks in advance!


Solution

  • Thanks for the pointers @Aun and @CommonsWare. My issue was actually with how I was creating the Volley request. Volley apparently does not handle input parameter values, (i.e. HTTP Get/Post Params), being passed as null. I had to check therefore whether each param was null, before adding it to the param map for the request:

    Map<String, String> mapPostArgs = new HashMap<String, String>();
    if(name != null) {
            mapPostArgs.put("name", name);
        }
    
    GsonRequest<APIResponse<Integer>> request = new GsonRequest<APIResponse<Integer>>(
                uri,
                new TypeToken<APIResponse<Integer>>(){}.getType(),
                getRequestHeaders(),
                mapPostArgs,
                listener,
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        handleError(error);
                    }//End method
                });
    

    Thanks alot for the guidance!