-2

Hello I´m making a map app that is inside a drawer activity in Android Studio, everything was going well untill I started to play with fragments since I'm pretty new in android I don't really know whats going on or what is the cause of the issue.

I replaced the main layout with the map layout making it my "home" layout. Then I added a fragment where I can change the language of the app called "Configuration" and I call that fragment when the "Configuration" button is pressed at the drawer menu.

I've noticed that even though the Configuration fragment replaces the map layout some location methods from the map fragment are still active in the background, I know this because a Toast message is displayed everytime the app checks for permission or changes location I did this as a visual aid so I know its working.

There are 2 scenarios where my app crashes for the same error at the same line of code and it happens to be one of the toasts I put there as aid. It is weird for me because all the other toast are working fine since the message is being displayed but when it gets to that specific toast at the onLocationChanged method the next error its displayed:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
    at android.widget.Toast.<init>(Toast.java:114)
    at android.widget.Toast.makeText(Toast.java:277)
    at android.widget.Toast.makeText(Toast.java:267)
    at com.tesseract.psiclops.zerov2.mapFragment.onLocationChanged(mapFragment.java:172)
    at com.google.android.gms.internal.location.zzay.notifyListener(Unknown Source:4)
    at com.google.android.gms.common.api.internal.ListenerHolder.notifyListenerInternal(Unknown Source:8)
    at com.google.android.gms.common.api.internal.ListenerHolder$zza.handleMessage(Unknown Source:16)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

So the 2 things that trigger the error are:

1.- When I press the back button and there is no fragment in the backstack (I suppose) so it hides (closes? minimizes?) the app then I open it again...it crashes.

2.- When I go into my configuration fragment and change language. Note: The method I use to change language restarts the activity so the change in language is shown.

I figured that since I'm closing the app both pressing the back button or calling my languaje method it makes sense that getcontext() becomes null after this but why are the other toasts showing the message and only crashing at that specific line? or specific method? and how can I prevent this??

NOTE: When I comment out the toast everything works fine, no crashing or anything :S, I really need to understand whats going on so I can prevent any issues later.

MapFragment:

    public class mapFragment extends Fragment implements OnMapReadyCallback, LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

private  GoogleMap mMap;
private GoogleApiClient mApiClient;
private Context mContext;

private OnFragmentInteractionListener mListener;

public mapFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_map, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);


 // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    SupportMapFragment mapFragment = (SupportMapFragment)getChildFragmentManager()
            .findFragmentById(R.id.map1);
    mapFragment.getMapAsync(this);

}

private void SetSancrisM(final GoogleMap mMap) {

    // Add a marker in Sancris and move the camera

   // LatLng sancris = new LatLng(16.736380, -92.638795);
    LatLngBounds SanCris = new LatLngBounds(new LatLng(16.720215, -92.684189), new LatLng(16.749950, -92.596649));


    //mMap.addMarker(new MarkerOptions().position(sancris).title("Marker in Sancris"));
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SanCris.getCenter(), 16));
    //mMap.setLatLngBoundsForCameraTarget(SanCris);


}



@Override
public void onMapReady(GoogleMap googleMap) {

    mMap = googleMap;
    mMap.getUiSettings().setZoomControlsEnabled(true);

    try {
        // Customise the styling of the base map using a JSON object defined
        // in a raw resource file.
        boolean success = googleMap.setMapStyle(
                MapStyleOptions.loadRawResourceStyle(getContext(), R.raw.style_json));

        if (!success) {
            Toast.makeText(mContext, "Chido3", Toast.LENGTH_SHORT).show();
        }
    } catch (Resources.NotFoundException e) {
        Toast.makeText(mContext, "No cargó el styla", Toast.LENGTH_SHORT).show();
    }

    //Calls the function that moves the cam to Sancris
    SetSancrisM(mMap);


    //Location permission conditions

    if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

        Toast.makeText(mContext, "Failed to get location permission", Toast.LENGTH_SHORT).show();
        ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 200);
        return;

    } else {

        if (!mMap.isMyLocationEnabled()) {
            mMap.setMyLocationEnabled(true);
            Toast.makeText(mContext, "Chido", Toast.LENGTH_SHORT).show();

        }

    }


    mApiClient = new GoogleApiClient.Builder(getContext())
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mApiClient.connect();


}


@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case 200: {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(mContext, "Failed to get permission 2", Toast.LENGTH_SHORT).show();

                } else {
                    mMap.setMyLocationEnabled(true);
                    Toast.makeText(mContext, "Chido2", Toast.LENGTH_SHORT).show();

                }
            }
        }
    }
}



@Override
public void onLocationChanged(Location location) {
    if(location==null){
        Toast.makeText(mContext, "No Location 4", Toast.LENGTH_SHORT).show();
    }else{
        LatLng ll= new LatLng(location.getLatitude(), location.getLongitude());
        CameraUpdate update= CameraUpdateFactory.newLatLngZoom(ll,mMap.getCameraPosition().zoom);
        mMap.animateCamera(update);

        Toast.makeText(mContext, "Chido4", Toast.LENGTH_SHORT).show(); //This one right here is the one not working
        }

}

LocationRequest mLocReq;

@Override
public void onConnected(@Nullable Bundle bundle) {

    mLocReq = LocationRequest.create();
    mLocReq.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocReq.setInterval(1000);

    if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 200);
        return;
    }
    LocationServices.FusedLocationApi.requestLocationUpdates(mApiClient, mLocReq, this);

    Toast.makeText(mContext, "Chido5", Toast.LENGTH_SHORT).show();

}







@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

}







//Este código tiene que ver con la comunicación entre fragmentos y actividades, osea déjalo para después


// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
    if (mListener != null) {
        mListener.onFragmentInteraction(uri);
    }
}


@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) {
        mListener = (OnFragmentInteractionListener) context;
    } else {
        throw new RuntimeException(context.toString()
                + " must implement OnFragmentInteractionListener");
    }
    mContext=context;
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}




/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p>
 * See the Android Training lesson <a href=
 * "http://developer.android.com/training/basics/fragments/communicating.html"
 * >Communicating with Other Fragments</a> for more information.
 */
public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
}
}

The toast that's not working its inside the onLocationChanged method

@Override
public void onLocationChanged(Location location) {
    if(location==null){
        Toast.makeText(getContext(), "No Location 4", Toast.LENGTH_SHORT).show();
    }else{
        LatLng ll= new LatLng(location.getLatitude(), location.getLongitude());
        CameraUpdate update= CameraUpdateFactory.newLatLngZoom(ll,mMap.getCameraPosition().zoom);
        mMap.animateCamera(update);

        Toast.makeText(getContext(), "Good4", Toast.LENGTH_SHORT).show(); //This one right here is the one not working
        }

}

All other toasts inside the mapfragment methods are working fine.

If you need more code please ask me here and I'll add it for you. Thank you very much in advance!!

Daniel
  • 21
  • 1
  • 6
  • Is all that code really necessary to reproduce the issue? Please read [MCVE], with stress on the minimal. – Max Vollmer Jun 05 '18 at 01:01
  • Possible duplicate of [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – Max Vollmer Jun 05 '18 at 01:03
  • Oh sorry I just wanted to have all the info here I'll edit and then add more code if people ask me to, thanks! – Daniel Jun 05 '18 at 01:05
  • I think you misunderstood my comment. Your code still needs to be **complete**. This edit actually made it worse, because now there is absolutely no information anymore to what's going on. Please read [MCVE] thoroughly, and create an example that actually is minimal **and** complete. – Max Vollmer Jun 05 '18 at 01:16
  • Basically we want you to do the groundwork of debugging your code and narrowing down the issue to the absolute minimum required to reproduce it. My suggestion is to create a new project and add/remove parts of your code until you can reproduce the issue with the least amount of code necessary. This has two reasons: 1. There is a great chance you find the cause and a solution yourself by doing so. 2. When posting a question with such a narrowed down example, other users don't all have to do the groundwork as well, making it possible to help you much quicker and more effectively. – Max Vollmer Jun 05 '18 at 01:24

2 Answers2

2

Declare Context variable in Fragment

private Context mContext;

Initialise it from onAttach()

 @Override
 public void onAttach(Context context) {
 super.onAttach(context);
 mContext = context;

}

Your Toast will be

  Toast.makeText(mContext,getString(R.string.toast) , Toast.LENGTH_LONG).show();
Mbuodile Obiosio
  • 1,463
  • 1
  • 15
  • 19
  • Wow man you did it! Its working now!! Would you care to explain why this happened? should I change all my "getcontext()" to a variable instead? – Daniel Jun 05 '18 at 01:27
  • The life cycle of fragments work differently and to get the contexts you need to do this or call getApplicationContext() You can accept my answer if it worked for you. – Mbuodile Obiosio Jun 05 '18 at 01:46
  • If this works for you, then you're absolutely just leaking your Context and keeping your LocationListener longer than your Fragment is alive. – ianhanniballake Jun 05 '18 at 01:51
  • I'll accept it altough I still have doubt about why was that specific toast crashing and not the others.... – Daniel Jun 05 '18 at 02:02
  • @ianhanniballake I guess I totally am doing that, could you point me out to some documentation I can read to avoid this? Or Would you care to explain? Total noob here, thanks! – Daniel Jun 05 '18 at 02:12
0

getContext() only returns a non-null value when the Fragment is attached to its containing Activity - between onAttach() and onDetach(). If you have a callback that is happening outside of those events, then you are not cleaning up your listeners appropriately.

The earliest you should create your listener (i.e., call requestLocationUpdates()) is in onAttach().

You must then unregister your listener (i.e., call removeUpdates()) in onDetach() to prevent leaking your LocationListener and the whole Fragment.

Generally, you should only request updates when your Fragment is visible on the screen. In that case, you would register in onStart() and unregister in onStop().

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Thank you so much! This gives me more perspective, I think I know how to look for the info I need now, appreciate it! – Daniel Jun 05 '18 at 03:04