Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Android: crash when using library that also uses Lost for location #8639

@chenguo

Description

@chenguo

I'm writing an application that uses MapView. I'm using a library that also uses the Lost API under the hood, and the location updates being requested of that library is crashing MyLocationView:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6902)
    at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1087)
    at android.view.ViewGroup.invalidateChild(ViewGroup.java:5246)
    at android.view.View.invalidateInternal(View.java:13573)
    at android.view.View.invalidate(View.java:13537)
    at android.view.View.invalidate(View.java:13521)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView$MyLocationShowBehavior.invalidate(MyLocationView.java:851)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView.update(MyLocationView.java:379)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView$MarkerCoordinateAnimatorListener.onAnimationUpdate(MyLocationView.java:700)
    at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1346)
    at android.animation.ValueAnimator.end(ValueAnimator.java:1055)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView$MyLocationShowBehavior.updateLatLng(MyLocationView.java:833)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView.setLocation(MyLocationView.java:458)
    at com.mapbox.mapboxsdk.maps.widgets.MyLocationView$GpsLocationListener.onLocationChanged(MyLocationView.java:592)
    at com.mapbox.mapboxsdk.location.LocationSource.onLocationChanged(LocationSource.java:136)
    at com.mapzen.android.lost.internal.LostClientManager.reportLocationChanged(LostClientManager.java:173)
    at com.mapzen.android.lost.internal.FusedLocationProviderServiceImpl.reportLocation(FusedLocationProviderServiceImpl.java:126)
    at com.mapzen.android.lost.internal.FusionEngine.onLocationChanged(FusionEngine.java:191)
    at com.mapzen.android.lost.internal.FusionEngine.checkLastKnownAndNotify(FusionEngine.java:183)
    at com.mapzen.android.lost.internal.FusionEngine.checkLastKnownGps(FusionEngine.java:169)
    at com.mapzen.android.lost.internal.FusionEngine.enable(FusionEngine.java:119)
    at com.mapzen.android.lost.internal.LocationEngine.setRequest(LocationEngine.java:45)
    at com.mapzen.android.lost.internal.FusedLocationProviderServiceImpl.requestLocationUpdates(FusedLocationProviderServiceImpl.java:60)
    at com.mapzen.android.lost.internal.FusedLocationProviderService.requestLocationUpdates(FusedLocationProviderService.java:69)
    at com.mapzen.android.lost.internal.FusedLocationProviderApiImpl

This can be replicated by starting a separate Lost listener on another thread. Here's snippets from a minimal reproducing example (sorry for stuffing all the Lost stuff in one function :-/), based largely off of the example app. The initializeLocation() and initializeMap() calls are basically code from the example app that sets up LocationEngineListener and MapView.

I spin up a new thread and wait a few seconds to let MapView get set up, then I go ahead and register a second Lost listener independent of anything in the Mapbox SDK. When LocationSource.onLocationChanged picks up the updates from this separate thread you should crash and see the stacktrace above.

  @Override
  public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Mapbox.getInstance(this, getString(R.string.mapbox_token));
    setContentView(R.layout.activity_mapview);

    initializeLocation();
    initializeMap(savedInstanceState);

    Thread lostThread = new Thread() {
      public void run() {
        Looper.prepare();
        try {
          // Give main thread some time to get set up
          Thread.sleep(5000);
        } catch (InterruptedException ex) {}
        startLostRequest();
      }
    };
    lostThread.start();
  }

  private void startLostRequest() {
    Log.d("crash-example", "setting up lost");
    final LocationListener listener = new LocationListener() {
      public void onLocationChanged(Location location) {
        Log.d("crash-example", "lost location update on thread "  + Thread.currentThread().getId());
      }
      public void onProviderDisabled(String provider) { /* no-op */ }
      public void onProviderEnabled(String provider) { /* no-op */ }
    };
    final Object lock = new Object();
    Log.d("crash-example", "creating client");
    LostApiClient client = new LostApiClient.Builder(this)
      .addConnectionCallbacks(new LostApiClient.ConnectionCallbacks () {
        public void onConnected() {
          Log.d("crash-example", "lost client connected");
          synchronized(lock) {
            lock.notify();
          }
        }
        public void onConnectionSuspended() { /* no-op */ }
      })
      .build();
    client.connect();
    synchronized(lock) {
      if (!client.isConnected()) {
        try {
          lock.wait();
        } catch (InterruptedException ex) {}
      }
    }
    LocationRequest request = LocationRequest.create()
      .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
      .setInterval(1)
      .setSmallestDisplacement(0);
    LocationServices.FusedLocationApi.requestLocationUpdates(client, request, listener);
    Log.d("crash-example", "Lost updates requested");
  }

Metadata

Metadata

Assignees

Labels

AndroidMapbox Maps SDK for Androidrelease blockerBlocks the next final release

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions