Android - getting “can't deliver broadcast” error on dynamically registered broadcast receiver

匿名 (未验证) 提交于 2019-12-03 02:33:02

问题:

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) 

Here is my code for the fragment:

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!

回答1:

I had this issue too. I found out the I broadcast on the intent huge amount of data (which was a big byte array..) So I solved it by decreasing the size of the byte array. Check it out!



回答2:

I know the original question was posted a while ago, but I recently experienced this problem, and I wanted to post what I did to fix it in case it helps someone else.

I was able to correct the problem by making the following 2 changes. I cannot definitively confirm the exact cause of the problem, so I don't know for sure if both changes were necessary. However, given what the app is doing, these are improvements over the old code, so I kept them both.

Definition of Intent action constants

The app uses a class that extends IntentService for performing background tasks. That class defines a number of static final String values that are used as the "action" values for the Intents that are broadcast when the service finishes a task.

I noticed that these strings were defined with values that matched the action's meaning (such as "SAVE_COMMENT"), but they did NOT include the app's package name.

From Android's Intents and IntentFilter documentation...

If you define your own actions, be sure to include your app's package name as a prefix.

https://developer.android.com/guide/components/intents-filters.html

I changed the value of these strings to include the package name.

I have not been able to prove this, but I'm wondering if one of the action names may have conflicted with an action defined in another app on those devices.

Switch to LocalBroadcastManager

The app was using Context::sendBroadcast() to broadcast an Intent whenever it finished a task. All of the logic performed by the app's service is only intended for that app, so I switched it to use LocalBroadcastManager::sendBroadcast() instead. This keeps the broadcast within the app, further preventing the possibility of a conflict with another app.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!