I'm trying to perform a relatively simple task: I have a fragment that starts a service to pull some JSON from a remote server. I then want to pass that JSON back to the original calling fragment using a broadcast, with the BroadcastReceiver defined as an anonymous class in the fragment.
However, whenever I try to do this, I keep getting the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.roundarch.codetest, PID: 21974
android.app.RemoteServiceException: can't deliver broadcast
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1631)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
code:
public class Part3Fragment extends Fragment {
PostCodeAdapter postCodeAdapter;
ListView listView;
BroadcastReceiver receiver;
IntentFilter intentFilter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_part3, null);
initialiseReceiver();
View emptyView = (View) view.findViewById(R.id.empty_textview);
ListView listView = (ListView) view.findViewById(R.id.part3_listview);
listView.setEmptyView(emptyView);
// TODO - the listview will need to be provided with a source for data
// TODO - (optional) you can set up handling to list item selection if you wish
return view;
}
@Override
public void onResume() {
super.onResume();
initialiseReceiver();
getActivity().startService(new Intent(getActivity(), Part3Service.class));
}
public void initialiseReceiver(){
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra("bundle");
ArrayList<PostCodesResult.Result> postcodes = (ArrayList<PostCodesResult.Result>) bundle.getSerializable("postcodeArray");
updatePostcodes(postcodes);
}
};
intentFilter = new IntentFilter("JSON_RECEIVED");
getActivity().registerReceiver(receiver, intentFilter);
}
@Override
public void onPause() {
super.onPause();
}
private void updatePostcodes(ArrayList<PostCodesResult.Result> postcodes) {
if (postcodes.size() > 0){
postCodeAdapter = new PostCodeAdapter(getActivity(), R.layout.part3_listview_item, postcodes);
listView.setAdapter(postCodeAdapter);
postCodeAdapter.notifyDataSetChanged();
}
}
}
And here is the code for the service:
public class Part3Service extends Service {
private final String TAG = this.getClass().getSimpleName();
// TODO - we can use this as the broadcast intent to filter for in our Part3Fragment
public static final String ACTION_SERVICE_DATA_UPDATED = "com.roundarch.codetest.ACTION_SERVICE_DATA_UPDATED";
private List<Map<String, String>> data = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service has started");
new PostCodeRetriever().execute("http://gomashup.com/json.php?fds=geo/usa/zipcode/state/IL");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void updateData() {
// TODO - start the update process for our data
}
private void broadcastDataUpdated(String jsonString) {
Intent intent = new Intent();
intent.setAction("JSON_RECEIVED");
ArrayList<PostCodesResult.Result> postcodes = new ArrayList<>();
if (jsonString != null) {
Gson gson = new Gson();
PostCodesResult result = gson.fromJson(jsonString, PostCodesResult.class);
postcodes.addAll(result.getResult());
}
Bundle bundle = new Bundle();
bundle.putSerializable("postcodeArray", postcodes);
intent.putExtra("bundle", bundle);
sendBroadcast(intent);
stopSelf();
}
class PostCodeRetriever extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
Uri uri = Uri.parse(params[0]);
String jsonString = "";
try {
URL postcodesUrl = new URL(uri.toString());
InputStream inputStream = null;
HttpURLConnection connection = (HttpURLConnection) postcodesUrl.openConnection();
connection.connect();
inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
stringBuilder.deleteCharAt(0);
stringBuilder.deleteCharAt(stringBuilder.length() -1);
jsonString = stringBuilder.toString();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return jsonString;
}
@Override
protected void onPostExecute(String jsonString) {
broadcastDataUpdated(jsonString);
super.onPostExecute(jsonString);
}
}
}
I've tried so many different things: sending only the raw JSON string via the intent, defining the broadcast receiver as a separate class, converting the JSON to a serialized ArrayList and sending that through the intent, and finally trying to include it all in a bundle and sending that.
The bizarre thing is that very similar code seems to work fine for my classmates - I can't see any differences between their code and my code that might be causing the problem (and neither can they). Additionally, if I try to just send a different String through the intent then that seems to work fine - the broadcast receiver picks it up and onReceive is called as it should be. The "Cannot deliver broadcast" error only seems to occur when attempting to deliver this specific dataset, and only for me!