Android-获取“无法传送广播"消息动态注册的广播接收器上的错误 [英] Android - getting "can't deliver broadcast" error on dynamically registered broadcast receiver

查看:163
本文介绍了Android-获取“无法传送广播"消息动态注册的广播接收器上的错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试执行一个相对简单的任务:我有一个片段,用于启动服务以从远程服务器提取一些JSON.然后,我想使用广播将JSON传递回原始调用片段,并将BroadcastReceiver定义为片段中的匿名类.

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)

代码:

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();
        }
    }
}

这是服务的代码:

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);
        }
    }
}

我尝试了很多不同的事情:通过意图仅发送原始JSON字符串,将广播接收器定义为单独的类,将JSON转换为序列化的ArrayList并通过意图发送,最后尝试包括全部打包并发送.

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.

奇怪的是,非常相似的代码对我的同学似乎很好用-我看不到他们的代码与我的代码之间可能引起问题的任何差异(它们也不能).另外,如果我尝试通过该意图发送其他字符串,那么这似乎很好用-广播接收器将其拾取,并按原样调用onReceive.仅在尝试传递此特定数据集时,似乎仅发生无法传递广播"错误!

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!

推荐答案

我知道最初的问题是在不久前发布的,但是我最近遇到了这个问题,我想发表我为解决该问题所做的工作,以防它有所帮助别人.

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.

通过进行以下2项更改,我得以解决此问题.我无法确切确定问题的确切原因,因此我不确定是否同时需要进行两次更改.但是,鉴于应用程序正在做什么,这些都是对旧代码的改进,因此我将两者都保留了.

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.

意图动作常量的定义

该应用程序使用扩展IntentService的类来执行后台任务.该类定义了一些静态最终String值,这些值用作服务完成任务时广播的Intent的操作"值.

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.

我注意到这些字符串是用符合操作含义的值定义的(例如"SAVE_COMMENT"),但它们不包含应用程序的程序包名称.

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.

来自Android的Intent和IntentFilter文档...

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.

切换到LocalBroadcastManager

应用程序在完成任务时使用Context :: sendBroadcast()广播Intent.该应用程序的服务执行的所有逻辑仅适用于该应用程序,因此我将其切换为使用LocalBroadcastManager :: sendBroadcast().这样可以在应用程序内保持广播,从而进一步防止与其他应用程序发生冲突的可能性.

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.

这篇关于Android-获取“无法传送广播"消息动态注册的广播接收器上的错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆