Search code examples
androidfragmentrealmrealm-list

Checking for null with RealmChangeListener


My app crashes when RealmList is null. It was working fine, until i changed my emulator from API 23 to API 19.

I already did a check but its not, but still getting crashes. I tried doing this

RealmChangeListener realmChangeListener = new RealmChangeListener() {
@Override
public void onChange(Object element) {
   if(getPostsFromDb() != null){
      mAdapter.swapData(getPostsFromDb());      
    }
  }
};

The App still crash with NPE

How can i resolve this?

public class CartFragment extends Fragment{

private Realm realm;

private RecyclerView mRecyclerView;
private BucketFragmentAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;

private ProgressBar circularProgessBar;

private RelativeLayout emptyView;

RealmChangeListener realmChangeListener = new RealmChangeListener() {
    @Override
    public void onChange(Object element) {
        mAdapter.swapData(getPostsFromDb());
    }
};

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    realm = Realm.getDefaultInstance();
    int user_id = Utils.getStoredUser(getActivity()).getId();

    BucketListAPIHelper.loadBucket(user_id);

        realm.addChangeListener(realmChangeListener);
}

public View onCreateView(LayoutInflater inflater, ViewGroup vg,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.cart_fragment, vg, false);

    emptyView = (RelativeLayout) view.findViewById(R.id.empty_view);

    circularProgessBar = (ProgressBar) view.findViewById(R.id.circular_progress_bar);

    circularProgessBar.setVisibility(View.VISIBLE);

    if(isNetworkConnected()){
        circularProgessBar.setVisibility(View.GONE);
    }else{
        new AlertDialog.Builder(getActivity())
                .setTitle("No Internet Connection")
                .setMessage("It looks like your a problem with your internet connection. Please turn it " +
                        "on and try again")
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                    }
                }).setIcon(android.R.drawable.ic_dialog_alert).show();
    }

    loadBucket(view);

    return view;
}


private void loadBucket(View view) {

    RealmResults<Bucket> realmResults = getPostsFromDb();

    mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mLayoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(mLayoutManager);

    if(!realmResults.isEmpty()){
        mRecyclerView.setVisibility(View.VISIBLE);
        emptyView.setVisibility(View.GONE);
        circularProgessBar.setVisibility(View.GONE);
        mAdapter = new BucketFragmentAdapter(realmResults, getActivity(), true);
        mRecyclerView.setAdapter(mAdapter);

    }else{
        mRecyclerView.setVisibility(View.GONE);
        emptyView.setVisibility(View.VISIBLE);
    }

}


@Subscribe
public void onPostSuccess(BucketListAPIHelper.PostsInfoSuccess postInfo) {
    //setRefreshing(false);
}

public RealmResults<Bucket> getPostsFromDb() {
    RealmResults<Bucket> realmResults = realm.where(Bucket.class).findAll();
    if(realmResults != null){
        return  realmResults;
    }
    return realmResults;
}

/* Present user with some error message when there's an issue while retrieving data */
@Subscribe
public void onPostFailure(BucketListAPIHelper.PostsInfoFailure error) {
    ///setRefreshing(false);
    //displaySimpleConfirmSnackBar(recyclerView, error.getErrorMessage());
}


private boolean isNetworkConnected() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); // 1
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); // 2
    return networkInfo != null && networkInfo.isConnected(); // 3
}

@Override
public void onPause() {
    super.onPause();
    EventBusSingleton.unregister(this);

}

@Override
public void onResume() {
    super.onResume();
    EventBusSingleton.register(this);
}

@Override
public void onDestroy() {

    if (realm != null) {
        realm.close();
    }

    super.onDestroy();
}

 @Override
 public void onConfigurationChanged(Configuration newConfig) {
     super.onConfigurationChanged(newConfig);
  }

}

Stack Trace

    02-08 14:19:58.526 23440-23440/ng.aswitch.shoppar E/dalvikvm: Could not         find class 'android.graphics.drawable.RippleDrawable', referenced from method android.support.v7.widget.AppCompatImageHelper.hasOverlappingRendering
    02-08 14:24:27.126 23440-23440/ng.aswitch.shoppar E/AndroidRuntime: FATAL EXCEPTION: main
Process: ng.aswitch.shoppar, PID: 23440
java.lang.NullPointerException
    at ng.aswitch.shoppar.fragments.CartFragment$1.onChange(CartFragment.java:66)
    at io.realm.HandlerController.notifyGlobalListeners(HandlerController.java:248)
    at io.realm.HandlerController.notifyAllListeners(HandlerController.java:323)
    at io.realm.HandlerController.realmChanged(HandlerController.java:456)
    at io.realm.HandlerController.handleMessage(HandlerController.java:122)
    at android.os.Handler.dispatchMessage(Handler.java:98)
    at android.os.Looper.loop(Looper.java:136)
    at    android.app.ActivityThread.main(ActivityThread.java:5017)
     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:779)
    at    com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
     at     dalvik.system.NativeStart.main(Native Method)

Solution

  • You should add RealmChangeListener to Realm instance in onCreateView() instead of onCreate(), and also remove it in onDestroyView().

    Because currently if onDestroyView() is called, and onDestroy() is not yet called, then if there is a write to the Realm, then RealmChangeListener will try to update mAdapter even though the view no longer exists.


    Although I think your real problem comes from the fact that

    if(!realmResults.isEmpty()){
        mRecyclerView.setVisibility(View.VISIBLE);
        emptyView.setVisibility(View.GONE);
        circularProgessBar.setVisibility(View.GONE);
        mAdapter = new BucketFragmentAdapter(realmResults, getActivity(), true);
        mRecyclerView.setAdapter(mAdapter);
    
    }else{
        mRecyclerView.setVisibility(View.GONE);
        emptyView.setVisibility(View.VISIBLE);
    }
    

    if RealmResults is empty, then mAdapter is not initialized, but if there is a write to the Realm, then RealmChangeListener will try to update the not-initialized mAdapter which will result in your crash.


    Solution: add null check to RealmChangeListener around mAdapter

    RealmChangeListener realmChangeListener = new RealmChangeListener() {
        @Override
        public void onChange(Object element) {
            if(mAdapter != null) {
                mAdapter.swapData(getPostsFromDb());
            }
        }
    };