0

In my App, I have two Fragments: FragmentA and FragmentB which are loaded from my MainActivity by the help of an SlidingMenu.

Thanks to Crashlytics I get notified when and where my app crashes. In this case, from time to time the DatabaseHandler in FragmentA is null (for some users), even though it has been initzialized.

This is my Code:

MainActivity

public class MainActivity extends AppCompatActivity implements MyFragment.OnListFragmentInteractionListener, AsyncResponse {


    private FragmentA fragmentA = new FragmentA();

    private DatabaseHandler databaseHandler = new DatabaseHandler(this);

    private NavigationView navigationView;
    private DrawerLayout drawer;
    private Toolbar toolbar;

    // index to identify current nav menu item
    private static int navItemIndex = 0;

    public static String CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;

    // toolbar titles respected to selected nav menu item
    private String[] activityTitles;

    // flag to load home fragment when user presses back key
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        fragmentA.setDatabaseHandler(this.databaseHandler);

        // Init UI
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mHandler = new Handler();

        drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        navigationView = (NavigationView) findViewById(R.id.nav_view);

        fabSendButton = (FloatingActionButton) findViewById(R.id.fab);

        // Navigation view header
        navHeader = navigationView.getHeaderView(0);


        // load toolbar titles from string resources
        activityTitles = getResources().getStringArray(R.array.sliding_menu_item_activity_titles);


        // initializing navigation menu
        setUpNavigationView();

        if (savedInstanceState == null) {
            navItemIndex = 0;
            CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;
            loadHomeFragment();
        }
    }

    /***
     * Returns respected fragment that user
     * selected from navigation menu
     */
    private void loadHomeFragment() {

        // set toolbar title
        setToolbarTitle();

        // if user select the current navigation menu again, don't do anything
        // just close the navigation drawer
        if (getSupportFragmentManager().findFragmentByTag(CURRENT_TAG) != null) {
            drawer.closeDrawers();
            return;
        }

        // Sometimes, when fragment has huge data, screen seems hanging
        // when switching between navigation menus
        // So using runnable, the fragment is loaded with cross fade effect
        // This effect can be seen in GMail app
        Runnable mPendingRunnable = new Runnable() {
            @Override
            public void run() {
                // update the activity_main_header_with_item content by replacing fragments
                Fragment fragment = getFragment();
                FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
                fragmentTransaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
                fragmentTransaction.replace(R.id.frame, fragment, CURRENT_TAG);
                fragmentTransaction.commit();
            }
        };

        // If mPendingRunnable is not null, then add to the message queue
        if (mPendingRunnable != null) {
            mHandler.post(mPendingRunnable);
        }

        //Closing drawer on item click
        drawer.closeDrawers();

        // refresh toolbar menu
        invalidateOptionsMenu();
    }

    private Fragment getFragment() {
        switch (navItemIndex) {
            case 0:
                return this.fragmentA;
            case 1:
                Fragment B fragmentB = new FragmentB();
                fragmentB.setDatabaseHandler(this.databaseHandler);
                return fragmentB;
            default:
                return this.fragmentA;
        }
    }

    private void setToolbarTitle() {
        getSupportActionBar().setTitle(activityTitles[navItemIndex]);
    }

    private void setUpNavigationView() {
        //Setting Navigation View Item Selected Listener to handle the item click of the navigation menu
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {

            // This method will trigger on item Click of navigation menu
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {

                //Check to see which item was being clicked and perform appropriate action
                switch (menuItem.getItemId()) {
                    //Replacing the activity_main_header_with_item content with ContentFragment Which is our Inbox View;
                    case R.id.nav_A:
                        navItemIndex = 0;
                        CURRENT_TAG = MyConstants.TAG_FRAGMENT_A;
                        break;
                    case R.id.nav_B:
                        navItemIndex = 1;
                        CURRENT_TAG = MyConstants.TAG_FRAGMENT_B;
                        break;
                    default:
                        navItemIndex = 0;
                }

                loadHomeFragment();

                return true;
            }
        });

        ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.openDrawer, R.string.closeDrawer) {

            @Override
            public void onDrawerClosed(View drawerView) {
                // Code here will be triggered once the drawer closes as we dont want anything to happen so we leave this blank
                super.onDrawerClosed(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                // Code here will be triggered once the drawer open as we dont want anything to happen so we leave this blank
                super.onDrawerOpened(drawerView);
            }
        };

        //Setting the actionbarToggle to drawer layout
        drawer.setDrawerListener(actionBarDrawerToggle);

        //calling sync state is necessary or else your hamburger icon wont show up
        actionBarDrawerToggle.syncState();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main_header_with_item, menu);
        return true;
    }
}

FragmentA:

public class FragmentA extends MyFragment implements AsyncResponse {

    private View view;
    private RecyclerView recyclerView;

    public FragmentA() {
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        List<Song> songsList = databaseHandler.getAllSongs();

        view = inflater.inflate(R.layout.home_list, container, false);

        // Set the adapter
        Context context = view.getContext();
        recyclerView = (RecyclerView) view.findViewById(R.id.listinclude);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));

        recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), R.drawable.divider));

        setVisibilities(songsList);

        this.recyclerViewAdapter = new RecyclerViewAdapter(songsList, mListener, false, true);

        recyclerView.setAdapter(this.recyclerViewAdapter);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }


    private void setVisibilities(List<Song> songsList) {
        ViewFlipper viewFlipper = (ViewFlipper) view.findViewById(R.id.viewFlipper);
        if (songsList.isEmpty() && viewFlipper.getDisplayedChild() == 0) {
            viewFlipper.setDisplayedChild(1);
        } else if (!songsList.isEmpty() && viewFlipper.getDisplayedChild() == 1) {
            viewFlipper.setDisplayedChild(0);
        }
    }


    @Override
    public void processFinish(String output) {
        // does something
    }
}

MyFragment

public class MyFragment extends Fragment {
    protected DatabaseHandler databaseHandler;
    protected static final String ARG_COLUMN_COUNT = "column-count";
    protected int mColumnCount = 1;
    protected MyFragment.OnListFragmentInteractionListener mListener;
    protected RecyclerViewAdapter recyclerViewAdapter;
    public void setDatabaseHandler(DatabaseHandler databaseHandler) {
        this.databaseHandler = databaseHandler;
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Timber.i( "onCreate");
        if (getArguments() != null) {
            mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
        }
    }
    @Override
    public void onPause() {
        Timber.i( "onPause");
        super.onPause();
    }
    @Override
    public void onAttach(Context context) {
        Timber.i( "onAttach");
        super.onAttach(context);
        if (context instanceof OnListFragmentInteractionListener) {
            mListener = (FragmentA.OnListFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener");
        }
    }
    @Override
    public void onDetach() {
        Timber.i( "onDetach");
        super.onDetach();
        mListener = null;
    }

    public interface OnListFragmentInteractionListener {
        // TODO: Update argument type and name
        void onListFragmentInteraction(Song item);
    }
}

DatabaseHandler:

public class DatabaseHandler extends SQLiteOpenHelper {

    // All Static variables
    // Database Version
    private static final int DATABASE_VERSION = 1;

    // Database Name
    private static final String DATABASE_NAME = "appName";

    // Songs table name
    private static final String TABLE_SONGS = "songs";


    @Override
    public void onCreate(SQLiteDatabase db) {
        // create Tables
    }


    // Upgrading database
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch(oldVersion) {
            case 1:
                //upgrade logic from version 1 to 2
            case 2:
                //upgrade logic from version 2 to 3
            case 3:
                //upgrade logic from version 3 to 4
                break;
            default:
                throw new IllegalStateException(
                        "onUpgrade() with unknown oldVersion " + oldVersion);
        }
    }


    // Getting All Songs
    public List<Song> getAllSongs() {

        // Select All Query
        String selectQuery = "SELECT  * FROM " + TABLE_SONGS + " ORDER BY ID DESC";

        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery(selectQuery, null);

        List<Song> songList = new ArrayList<>();

        songList.addAll(getSongsFromCursor(cursor));

        cursor.close();
        // return title list
        return songList;
    }
}

Problem:

Fatal Exception: java.lang.RuntimeException
Unable to start activity ComponentInfo{com.myapp.myappname/com.myapp.myappname.ui.activity.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List com.myapp.myappname.database.DatabaseHandler.getAllSongs()' on a null object reference

Details:

Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.myappname/com.myapp.myappname.ui.activity.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List com.myapp.myappname.database.DatabaseHandler.getAllSongs()' on a null object reference
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2984)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6776)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List com.myapp.myappname.database.DatabaseHandler.getAllSongs()' on a null object reference
       at com.myapp.myappname.ui.fragment.FragmentA.onCreateView(FragmentA.java:51)
       at android.support.v4.app.Fragment.performCreateView(Fragment.java:2192)
       at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1299)
       at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1528)
       at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1595)
       at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:2900)
       at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
       at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:603)
       at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
       at com.myapp.myappname.ui.activity.MainActivity.onStart(MainActivity.java:590)
       at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1256)
       at android.app.Activity.performStart(Activity.java:6972)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2937)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
       at android.app.ActivityThread.-wrap14(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6776)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
joshi737
  • 869
  • 3
  • 12
  • 26
  • 2
    `fragmentA.setDatabaseHandler(this.databaseHandler);` - this is the wrong way to interact with a fragment. See https://stackoverflow.com/questions/17436298/how-to-pass-a-variable-from-activity-to-fragment-and-pass-it-back – Serg Aug 04 '17 at 16:42

3 Answers3

0

try this one

databaseHandler=new DataBaseHandler(getActivity());
List<Song> songsList = databaseHandler.getAllSongs();

And add constructor in the DataBaseHandler class

public DataBaseHandler(Context context){
super(context, DB_NAME, null, 1);
}
hareesh J
  • 520
  • 1
  • 6
  • 21
0

try this on FragmentA.

databaseHandler=new DataBaseHandler(getContext());

you are taking context of mainActivity to initialize databaseHandler but using in fragment.

0

Found a solution based on Serg's answer:

FragmentA:

    public class FragmentA extends MyFragment implements AsyncResponse {

        private View view;
        private RecyclerView recyclerView;

        public FragmentA() {
        }

  public static FragmentA newInstance(DatabaseHandler DatabaseHandler) {
        FragmentA fragment = new FragmentA();
        Bundle bundle = new Bundle();
        bundle.putSerializable(MyConstants.FRAGMENT_ARGUMENT_KEY, databaseHandler);
        fragment.setArguments(bundle);
        return fragment;
    }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            databaseHandler = (DatabaseHandler) getArguments().getSerializable(MyConstants.FRAGMENT_ARGUMENT_KEY);
            List<Song> songsList = databaseHandler.getAllSongs();

            view = inflater.inflate(R.layout.home_list, container, false);

            // Set the adapter
            Context context = view.getContext();
            recyclerView = (RecyclerView) view.findViewById(R.id.listinclude);
            recyclerView.setLayoutManager(new LinearLayoutManager(context));

            recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), R.drawable.divider));

            setVisibilities(songsList);

            this.recyclerViewAdapter = new RecyclerViewAdapter(songsList, mListener, false, true);

            recyclerView.setAdapter(this.recyclerViewAdapter);
            return view;
        }

        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
        }


        private void setVisibilities(List<Song> songsList) {
            ViewFlipper viewFlipper = (ViewFlipper) view.findViewById(R.id.viewFlipper);
            if (songsList.isEmpty() && viewFlipper.getDisplayedChild() == 0) {
                viewFlipper.setDisplayedChild(1);
            } else if (!songsList.isEmpty() && viewFlipper.getDisplayedChild() == 1) {
                viewFlipper.setDisplayedChild(0);
            }
        }


        @Override
        public void processFinish(String output) {
            // does something
        }
    }

MainActivity:

    fragmentA = FragmentA.newInstance(this.databaseHandler);
joshi737
  • 869
  • 3
  • 12
  • 26
  • Issue still not solved, I get now a Caused by java.lang.RuntimeException: Parcelable encountered IOException reading a Serializable object (name = com.myapp.appname.database.DatabaseHandler). Any ideas? – joshi737 Aug 09 '17 at 16:15