Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird "Could not dispatch event" and "No subscribers registered for event" issue

I've been trying to debug this thing for hours and I really can't see the issue here.

This is my MainActivity. The main thing to look for here is the ArrayList<Servico>, being Servico a custom object. I've created a simple "event" class - ServicoActual - that has just a Servico object reference (and a constructor/getter):

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.SearchManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import net.pedromoreira.billper.events.ServicoActual;

import java.util.ArrayList;
import java.util.Locale;

import de.greenrobot.event.EventBus;


public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;

    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    private ArrayList<Servico> servicos;
    //private String[] mPlanetTitles;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        servicos = new ArrayList<Servico>();
        servicos.add(new Servico("Luz"));
        servicos.add(new Servico("Água"));
        servicos.add(new Servico("Gás"));

        //Log.i("onCreate", "Servicos: " + servicos.size());

        setContentView(R.layout.activity_main);

        mTitle = mDrawerTitle = getTitle();
        //mPlanetTitles = getResources().getStringArray(R.array.planets_array);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // set a custom shadow that overlays the main content when the drawer opens
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
        // set up the drawer's list view with items and click listener
        mDrawerList.setAdapter(new ArrayAdapter<Servico>(this,
                R.layout.drawer_list_item, servicos));
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        // enable ActionBar app icon to behave as action to toggle nav drawer
        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);

        // ActionBarDrawerToggle ties together the the proper interactions
        // between the sliding drawer and the action bar app icon
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.drawable.ic_drawer,  /* nav drawer image to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description for accessibility */
                R.string.drawer_close  /* "close drawer" description for accessibility */
        ) {
            public void onDrawerClosed(View view) {
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            public void onDrawerOpened(View drawerView) {
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        if (savedInstanceState == null) {
            selectItem(0);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    /* Called whenever we call invalidateOptionsMenu() */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // The action bar home/up action should open or close the drawer.
        // ActionBarDrawerToggle will take care of this.
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle action buttons
        switch(item.getItemId()) {
            case R.id.action_websearch:
                // create intent to perform web search for this planet
                Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
                intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
                // catch event that there's no activity to handle intent
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                } else {
                    Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
                }
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /* The click listener for ListView in the navigation drawer */
    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
            Log.i("DrawerItemClickListener", "Position: " + position);
        }
    }

    private void selectItem(int position) {
        // update the main content by replacing fragments
        Fragment fragment = new ServicoFragment();
        //Bundle args = new Bundle();
        //args.putInt(ServicoFragment.ARG_PLANET_NUMBER, position);
        //fragment.setArguments(args);

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
        Log.i("selectItem", "Servico: " + servicos.get(position).getNome());
        EventBus.getDefault().postSticky(new ServicoActual(servicos.get(position)));

        // update selected item and title, then close the drawer
        mDrawerList.setItemChecked(position, true);
        setTitle(servicos.get(position).getNome());
        mDrawerLayout.closeDrawer(mDrawerList);
    }

    @Override
    public void setTitle(CharSequence title) {
        mTitle = title;
        getActionBar().setTitle(mTitle);
    }

    /**
     * When using the ActionBarDrawerToggle, you must call it during
     * onPostCreate() and onConfigurationChanged()...
     */

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggls
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    /**
     * Fragment that appears in the "content_frame"
     */
    public static class ServicoFragment extends Fragment {
        //public static final String ARG_PLANET_NUMBER = "planet_number";

        private TextView mTestText;

        public ServicoFragment() {
            // Empty constructor required for fragment subclasses
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EventBus.getDefault().registerSticky(this);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_servico, container, false);
            //int i = getArguments().getInt(ARG_PLANET_NUMBER);
            //String planet = getResources().getStringArray(R.array.planets_array)[i];

            //int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
            //        "drawable", getActivity().getPackageName());
            //((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);

            //getActivity().setTitle(planet);

            mTestText = ((TextView) rootView.findViewById(R.id.test_text));
            Log.i("onCreateView", "mTestText: " + mTestText.toString());
            mTestText.setText("xpto");

            return rootView;
        }

        public void onEvent(ServicoActual e){
            Servico servico = e.getServico();
            Log.i("onEvent", "Servico: " + servico.getNome());
            getActivity().setTitle(servico.getNome());
            mTestText.setText(servico.getNome());
        }
    }
}

So, when a Drawer list item is clicked, I'm trying to pass the corresponding Servico (inside the ServicoActual "event") to the ServicoFragment, which should write the Servico's name to its TextView.

This is what happens with a single click on the first item (0):

07-13 00:50:35.388  26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch Down
07-13 00:50:35.628  26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch UP
07-13 00:50:36.308  26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch Down
07-13 00:50:36.358  26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch UP
07-13 00:50:36.428  26244-26244/net.pedromoreira.billper I/selectItem﹕ Servico: Luz
07-13 00:50:36.428  26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.458  26244-26244/net.pedromoreira.billper E/Event﹕ Could not dispatch event: class net.pedromoreira.billper.events.ServicoActual to subscribing class class net.pedromoreira.billper.MainActivity$ServicoFragment
    java.lang.NullPointerException
            at net.pedromoreira.billper.MainActivity$ServicoFragment.onEvent(MainActivity.java:235)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at de.greenrobot.event.EventBus.invokeSubscriber(EventBus.java:569)
            at de.greenrobot.event.EventBus.postToSubscription(EventBus.java:500)
            at de.greenrobot.event.EventBus.postSingleEvent(EventBus.java:475)
            at de.greenrobot.event.EventBus.post(EventBus.java:365)
            at de.greenrobot.event.EventBus.postSticky(EventBus.java:406)
            at net.pedromoreira.billper.MainActivity.selectItem(MainActivity.java:161)
            at net.pedromoreira.billper.MainActivity.access$300(MainActivity.java:35)
            at net.pedromoreira.billper.MainActivity$DrawerItemClickListener.onItemClick(MainActivity.java:146)
            at android.widget.AdapterView.performItemClick(AdapterView.java:299)
            at android.widget.AbsListView.performItemClick(AbsListView.java:1158)
            at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957)
            at android.widget.AbsListView$3.run(AbsListView.java:3849)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5105)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
            at dalvik.system.NativeStart.main(Native Method)
07-13 00:50:36.458  26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.468  26244-26244/net.pedromoreira.billper D/Event﹕ No subscribers registered for event class de.greenrobot.event.SubscriberExceptionEvent
07-13 00:50:36.468  26244-26244/net.pedromoreira.billper I/DrawerItemClickListener﹕ Position: 0
07-13 00:50:36.498  26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.508  26244-26244/net.pedromoreira.billper E/Event﹕ Could not dispatch event: class net.pedromoreira.billper.events.ServicoActual to subscribing class class net.pedromoreira.billper.MainActivity$ServicoFragment
    java.lang.NullPointerException
            at net.pedromoreira.billper.MainActivity$ServicoFragment.onEvent(MainActivity.java:236)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at de.greenrobot.event.EventBus.invokeSubscriber(EventBus.java:569)
            at de.greenrobot.event.EventBus.postToSubscription(EventBus.java:500)
            at de.greenrobot.event.EventBus.subscribe(EventBus.java:288)
            at de.greenrobot.event.EventBus.register(EventBus.java:189)
            at de.greenrobot.event.EventBus.registerSticky(EventBus.java:166)
            at net.pedromoreira.billper.MainActivity$ServicoFragment.onCreate(MainActivity.java:209)
            at android.app.Fragment.performCreate(Fragment.java:1688)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:860)
            at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1063)
            at android.app.BackStackRecord.run(BackStackRecord.java:684)
            at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1450)
            at android.app.FragmentManagerImpl$1.run(FragmentManager.java:444)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5105)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
            at dalvik.system.NativeStart.main(Native Method)
07-13 00:50:36.508  26244-26244/net.pedromoreira.billper D/Event﹕ No subscribers registered for event class de.greenrobot.event.SubscriberExceptionEvent
07-13 00:50:36.508  26244-26244/net.pedromoreira.billper I/onCreateView﹕ mTestText: android.widget.TextView{429216d0 V.ED.... ......ID 0,0-0,0 #7f090003 app:id/test_text}

What can I be doing wrong?

like image 793
Pedro Moreira Avatar asked Jul 13 '14 00:07

Pedro Moreira


1 Answers

I was wrong. The warning is about nobody being registered at the time of posting. The actual exception probably has to do with the Activity not being attached when the event runs. You probably want onAttach().

http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Still feels like kind of a weird architecture, but it would be false to say none of my apps have weird architectures.

Update:

Let me start over. My original answer said EventBus was crashing because nobody was registered for your event. However, that log statement isn't a crash. It's just a debug. I have found EventBus to be a bit aggressive with that kind of thing, but on the balance it probably helps.

The NullPointerException is different. When onCreate() is called on your fragment, it registers to your sticky event. This IMMEDIATELY calls your event. It's right there in the stack. At that point, your fragment is probably attached to your activity, but onCreateView() has (probably) not been called, so your TextView doesn't exist. I can't see which line is throwing the exception exactly. See the life cycle:

If you want to use a sticky event, you'll need to register later in the Fragment life cycle. I would guess you want onCreateView()/onDestroyView(). I've never seen that kind of register/unregister pairing, but it should work.

Generally I think we'd do this with arguments to the fragment, or possibly getting the data from the activity directly, but all methods are fairly clumsy and/or verbose, which is why I'm still an "only when needed" kind of person with the fragments.

An alternative would be to create a Handler in the UI thread and post a Runnable that does the register, which should schedule that after the pending life cycle events, but if you don't need that, don't do it.

Also, you could probably just post/register the Servico rather than the ServicoActual, but that shouldn't make any difference to your issue.

like image 129
Kevin Galligan Avatar answered Sep 20 '22 00:09

Kevin Galligan