Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPE in moveCamera #294

Closed
RobinIsTheBird opened this issue Dec 1, 2016 · 3 comments
Closed

NPE in moveCamera #294

RobinIsTheBird opened this issue Dec 1, 2016 · 3 comments
Assignees
Labels

Comments

@RobinIsTheBird
Copy link
Contributor

RobinIsTheBird commented Dec 1, 2016

This issue relates to Rollbar #1880,

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.GoogleMap.moveCamera(com.google.android.gms.maps.CameraUpdate)' on a null object reference
at org.azavea.otm.ui.MainMapFragment.lambda$null$3 (MainMapFragment.java:488)

The Rollbar traceback isn't entirely clear whether the null was the mMap or the return value of CameraUpdateFactory, but it looks to me like it's probably the former.

mMap would be null here as a result of a race condition between onCreateView, where the mapView that creates the mMap is setup, and the fragment onConnected, where the fragment attempts to zoom to the current location. Being a race condition would explain why I am unable to reproduce it.

@RobinIsTheBird
Copy link
Contributor Author

In addition to the lifecycle fixes above, I intend to add a promise to join deferreds from the mapView.getMapAsync and onConnected together, and call moveToCurrentLocation only when the joint promise completes.

@RobinIsTheBird RobinIsTheBird removed the + label Dec 1, 2016
@RobinIsTheBird
Copy link
Contributor Author

Here is a gliffy diagram showing dependencies between asynchronously performed procedures.

mainmapfragment_dependencies

@RobinIsTheBird
Copy link
Contributor Author

From a slack with @maurizi on 12/05/16:

mmaurizi [2:44 PM]
it seems like the 3 events we need to wrap into promises are: the callback of getMapAsync, the location services being ready, and the instance having loaded

rschaufler [2:45 PM]
yes.

[2:46]
Not everything requires all three, but the simplest code structure might be to just have one method that runs when all 3 are done, and not worry about excess synchronization.

mmaurizi [2:46 PM]
we can't do that - there are legitimate situations where location will never be ready

rschaufler [2:46 PM]
true.

mmaurizi [2:47 PM]
but we can combine map ready & instance loaded

rschaufler [2:47 PM]
we'd have to have a timeout that would reject location, and a variant of the DONE-DONE-DONE to deal w DONE-DONE-FAIL.

[2:48]
So maybe we want 3 methods.

mmaurizi [2:48 PM]
to get the best user experience, we likely want DONE-DONE (do reasonable default) - (DONE - move to GPS location / FAIL do nothing)

rschaufler [2:49 PM]
async-map && instance-loaded both DONE, async-map && instance-loaded DONE and location DONE, and async-map && instance-loaded DONE and location FAIL.

mmaurizi [2:49 PM]
yeah

[2:50]
I don't think this planned refactor is a 1-pointer though

rschaufler [2:50 PM]
agreed

mmaurizi [2:50 PM]
Probably should happen next sprint?

rschaufler [2:51 PM]
agreed. I feel I've already put in the 1-point of work to figure out what's going on.

another piece of refactor - moving it all into onActivityStarted (or whatever it's called)

mmaurizi [2:52 PM]
why would we move everything there?

[2:52]
I guess its as good a place as any?

rschaufler [2:53 PM]
getting the MapView requires the activity. It just happens to have worked in onCreateView, because it just happens to already have its activity, but the onActivityWhatever is more correct.

[2:53]
onActivityConnected ? (edited)

mmaurizi [2:54 PM]
onActivityCreate?onActivityCreated?

rschaufler [2:54 PM]
something like that. Whatever it is, it comes after onCreateView.

RobinIsTheBird added a commit to RobinIsTheBird/otm-android that referenced this issue Dec 23, 2016
Changes:

- Move most initialization from onCreateView to onActivityCreated
- Move getMapAsync to onResume, since it has to be there anyway,
  and shouldn't be done twice during initialization
- Complete initialization using jDeferred promises

Feats:

- On first run, shows nearby locations, and goes there when you
  pick one
- On subsequent runs, goes to most recently chosen treeMap
- On entering a treeMap that covers current location, with gps on,
  zooms correctly to current location
- Scales and pans
- Selecting a plot or tree works
- Getting more details for that plot or tree works

TODO:

- Sometimes after you select a plot/tree, you can then select
  another one, and sometimes it just ignores your attempts
- It always ignores attempts to select a third one
- Need to test filters, changing maps, logging in and out,
  and turning off the gps

--

Connects to OpenTreeMap#294
RobinIsTheBird added a commit to RobinIsTheBird/otm-android that referenced this issue Jan 4, 2017
Rollbar registered the following error:
```
java.lang.NullPointerException: Attempt to invoke virtual method
'void com.google.android.gms.maps.GoogleMap.moveCamera(
    com.google.android.gms.maps.CameraUpdate)'
on a null object reference

at org.azavea.otm.ui.MainMapFragment.lambda$null$3
    (MainMapFragment.java:488)
```

It's probably a race condition.
Tame the asynchronous control flow to eliminate race conditions.

AFAIK, this fixes all race conditions except open question OpenTreeMap#1 below.
Unknown how many rollbar errors it will eliminate, but should
eliminate the original npe.

Refer to
* [MapView](https://developers.google.com/android/reference/com/google/android/gms/maps/MapView)
* [GoogleMap](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap)
* [MapsInitializer](https://developers.google.com/android/reference/com/google/android/gms/maps/MapsInitializer)
* [GoogleApiAvailability](https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability)
* [Fragment Life Cycle](https://developer.android.com/guide/components/fragments.html)

Changes:

- Use jDeferred to synchronize between asynchronous flows
- Move most initialization from onCreateView to `onActivityCreated`
- Move google api connect, map fetch, and add mode cancel to `onStart`
- Update `MapHelper` to use current google availability api

Open questions:

1. Need a way to manage the `MapHelper` google api dialog to determine
   whether the play store services problem gets resolved,
   and fail catastrophically if it doesn't.
2. Need a way to test missing play store services without wrecking my phone.
3. Fails to fix OpenTreeMap#297, clearing the filter repositions the camera
4. Probably shouldn't create new `Deferred` objects if they
   already exist, but haven't noticed any negative effects of doing so
5. I don't know enough about the tile cache to be sure when it needs
   to be cleared.

--

Connects to OpenTreeMap#294
RobinIsTheBird added a commit to RobinIsTheBird/otm-android that referenced this issue Jan 10, 2017
The motivation for this commit is when
Rollbar registered the following error:
```
java.lang.NullPointerException: Attempt to invoke virtual method
'void com.google.android.gms.maps.GoogleMap.moveCamera(
    com.google.android.gms.maps.CameraUpdate)'
on a null object reference

at org.azavea.otm.ui.MainMapFragment.lambda$null$3
    (MainMapFragment.java:488)
```

It's undoubtedly a race condition, one of several.
Tame the asynchronous control flow to eliminate race conditions.

AFAIK, this fixes all race conditions except open question OpenTreeMap#1 below.
Unknown how many rollbar errors it will eliminate, but should
eliminate the original npe.

Refer to
* [MapView](https://developers.google.com/android/reference/com/google/android/gms/maps/MapView)
* [GoogleMap](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap)
* [MapsInitializer](https://developers.google.com/android/reference/com/google/android/gms/maps/MapsInitializer)
* [GoogleApiAvailability](https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability)
* [Fragment Life Cycle](https://developer.android.com/guide/components/fragments.html)

Changes:

- Follow instructions on the Android Fragment life cycle
- Follow instructions on the Google API state machine
- Consolidate most initialization in `onActivityCreated`
  from `onCreateView` and `onResume`
- Use jDeferred to synchronize between asynchronous flows
- Eliminate mMap member variable in favor of the deferred.
- Thanks to all of the above, eliminate checks for whether
  the map, the instance, and the location have been initialized
- Replace `clearTileCache`, which no longer worked (OpenTreeMap#291),
  with `reloadTiles`
- Eliminate `MainMapFragment.onHiddenChanged`
- Fix pre-existing bug on Back Stack pop out of step 3 of add tree
- Move some string literals used in toasts into `strings.xml`
- Remove unnecessary `public` declarations
- Better `Logger` messages
- Use `lambda`s more extensively
- Fix pre-existing bug in `TreeEditDisplay` to update the rev
  on the correct plot variable
- Update `MapHelper` to use current google availability api

--

Connects to OpenTreeMap#294
@maurizi maurizi closed this as completed Jan 12, 2017
@maurizi maurizi removed the in review label Jan 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants