Mapbox Реагирует поочередно Навигационная проблема ANR - PullRequest
1 голос
/ 23 октября 2019

React Native версия: 59.5

Мы используем https://github.com/SRHealth/react-native-mapbox-navigation, но в этом примере отображается mapboxnavigationview. Мы не хотим так работать.

Мы используем Mapbox Navigation в нашем приложении. iOS работает нормально, но у Android есть проблема ANR.

Mapbox iOS не имеет никаких проблем. Это работает как шарм.

Android зависает, но не всегда.

Logcat;

10-23 12:03:40.907  2104  2239 E ActivityManager: ANR in com.mybundle (com.mybunle/.MainActivity)
10-23 12:03:40.907  2104  2239 E ActivityManager: PID: 25847
10-23 12:03:40.907  2104  2239 E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 2.  Wait queue head age: 48808.3ms.)
10-23 12:03:40.907  2104  2239 E ActivityManager: Load: 9.4 / 9.37 / 8.86
10-23 12:03:40.907  2104  2239 E ActivityManager: CPU usage from 0ms to 8134ms later (2019-10-23 12:03:32.741 to 2019-10-23 12:03:40.876):

Мой родной компонент, который является mapboxnavigationview;

package com.mybundle.Mapbox;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.location.Location;
// import android.support.annotation.NonNull;
// import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import android.view.View;
import android.widget.LinearLayout;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.android.core.location.LocationEngineCallback;
import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.android.core.location.LocationEngineRequest;
import com.mapbox.android.core.location.LocationEngineResult;
import com.mapbox.api.directions.v5.models.DirectionsResponse;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;

import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.services.android.navigation.ui.v5.camera.DynamicCamera;
import com.mapbox.services.android.navigation.ui.v5.camera.NavigationCamera;
import com.mapbox.services.android.navigation.ui.v5.camera.NavigationCameraUpdate;
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView;
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMap;
import com.mapbox.services.android.navigation.ui.v5.voice.NavigationSpeechPlayer;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechPlayerProvider;
import com.mapbox.services.android.navigation.ui.v5.voice.VoiceInstructionLoader;
import com.mapbox.services.android.navigation.v5.milestone.Milestone;
import com.mapbox.services.android.navigation.v5.milestone.MilestoneEventListener;
import com.mapbox.services.android.navigation.v5.milestone.VoiceInstructionMilestone;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation;
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute;
import com.mapbox.services.android.navigation.v5.offroute.OffRouteListener;
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mybundle.R;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Locale;

import okhttp3.Cache;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;

public class MapboxNavigationView extends LinearLayout implements OnMapReadyCallback,
  MapboxMap.OnMapLongClickListener, ProgressChangeListener, MilestoneEventListener, OffRouteListener {


    private static final int FIRST = 0;
    private static final int ONE_HUNDRED_MILLISECONDS = 100;
    private static final int BOTTOMSHEET_PADDING_MULTIPLIER = 4;
    private static final int TWO_SECONDS_IN_MILLISECONDS = 2000;
    private static final double BEARING_TOLERANCE = 90d;
    private static final String LONG_PRESS_MAP_MESSAGE = "Long press the map to select a destination.";
    private static final String SEARCHING_FOR_GPS_MESSAGE = "Searching for GPS...";
    private static final String COMPONENT_NAVIGATION_INSTRUCTION_CACHE = "component-navigation-instruction-cache";
    private static final long TEN_MEGABYTE_CACHE_SIZE = 10 * 1024 * 1024;
    private static final int ZERO_PADDING = 0;
    private static final double DEFAULT_ZOOM = 12.0;
    private static final double DEFAULT_TILT = 0d;
    private static final double DEFAULT_BEARING = 0d;
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000;
    private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 500;


    private Context context;
    private final MapboxNavigationViewLocationCallback callback = new MapboxNavigationViewLocationCallback(this);
    private LocationEngine locationEngine;
    private MapboxNavigation navigation;
    private NavigationSpeechPlayer speechPlayer;
    private NavigationMapboxMap navigationMap;
    private Location lastLocation;
    private DirectionsRoute route;
    private Point destination = null;

    private InstructionView instructionView;
    private MapView mapView;

    private boolean mapLoaded = false;

    public MapboxNavigationView(ReactApplicationContext reactContext) {
        super(reactContext);

        this.context = reactContext;
        context.setTheme(R.style.CustomInstructionView);

        inflate(context, R.layout.navigation_view, this);

        mapView = findViewById(R.id.mapView);
        instructionView = findViewById(R.id.instructionView);
        mapView.onCreate(null);
        mapView.getMapAsync(this);

    }

    @Override
    public void onMapReady(@NonNull MapboxMap mapboxMap) {
        mapboxMap.setStyle(new Style.Builder().fromUri(context.getString(R.string.navigation_guidance_day)), style -> {
            navigationMap = new NavigationMapboxMap(mapView, mapboxMap);

            // For Location updates
            initializeLocationEngine();

            // For navigation logic / processing
            initializeNavigation(mapboxMap);
            navigationMap.updateCameraTrackingMode(NavigationCamera.NAVIGATION_TRACKING_MODE_NONE);
            navigationMap.updateLocationLayerRenderMode(RenderMode.GPS);

            // For voice instructions
            initializeSpeechPlayer();
            mapLoaded = true;
            if(destination != null)
            {
                navigateToDestination(destination);
            }
        });
    }

    @Override
    public boolean onMapLongClick(@NonNull LatLng point) {
        // Only reverse geocode while we are not in navigation
        return false;
    }
    /*
     * Navigation listeners
     */

    @Override
    protected void onDetachedFromWindow() {
        navigation.onDestroy();
        super.onDetachedFromWindow();
    }


    @Override
    public void onProgressChange(Location location, RouteProgress routeProgress) {
        // Cache "snapped" Locations for re-route Directions API requests
        updateLocation(location);

        // Update InstructionView data from RouteProgress
        instructionView.updateDistanceWith(routeProgress);
    }

    @Override
    public void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {
        playAnnouncement(milestone);

        // Update InstructionView banner instructions
        instructionView.updateBannerInstructionsWith(milestone);
    }

    @Override
    public void userOffRoute(Location location) {
        calculateRouteWith(destination, true);
    }

    /*
     * Activity lifecycle methods
     */


    void checkFirstUpdate(Location location) {
        if (lastLocation == null) {
            lastLocation = location;
            moveCameraTo(location);
            if(destination != null ) navigateToDestination(destination);
            // Allow navigationMap clicks now that we have the current Location
        }
    }

    void updateLocation(Location location) {
        lastLocation = location;
        navigationMap.updateLocation(location);
    }

    private void initializeSpeechPlayer() {
        String english = "en";
        String lng = Locale.getDefault().getDisplayLanguage();
        if(lng.equals("Deutsch")){
            english = "de";
        }
        Cache cache = new Cache(new File(context.getCacheDir(), COMPONENT_NAVIGATION_INSTRUCTION_CACHE),
                TEN_MEGABYTE_CACHE_SIZE);
        VoiceInstructionLoader voiceInstructionLoader = new VoiceInstructionLoader(context,
                Mapbox.getAccessToken(), cache);
        SpeechPlayerProvider speechPlayerProvider = new SpeechPlayerProvider(context, english, true,
                voiceInstructionLoader);
        speechPlayer = new NavigationSpeechPlayer(speechPlayerProvider);
    }

    @SuppressLint("MissingPermission")
    private void initializeLocationEngine() {
        locationEngine = LocationEngineProvider.getBestLocationEngine(context);
        LocationEngineRequest request = buildEngineRequest();
        locationEngine.requestLocationUpdates(request, callback, null);

    }

    private void initializeNavigation(MapboxMap mapboxMap) {
        navigation = new MapboxNavigation(context, Mapbox.getAccessToken());
        navigation.setLocationEngine(locationEngine);
        navigation.setCameraEngine(new DynamicCamera(mapboxMap));
        navigation.addProgressChangeListener(this);
        navigation.addMilestoneEventListener(this);
        navigation.addOffRouteListener(this);
        navigationMap.addProgressChangeListener(navigation);
    }


    private void playAnnouncement(Milestone milestone) {
        if (milestone instanceof VoiceInstructionMilestone) {
            SpeechAnnouncement announcement = SpeechAnnouncement.builder()
                    .voiceInstructionMilestone((VoiceInstructionMilestone) milestone)
                    .build();
            speechPlayer.play(announcement);
        }
    }

    private void moveCameraTo(Location location) {
        CameraPosition cameraPosition = buildCameraPositionFrom(location, location.getBearing());
        navigationMap.retrieveMap().animateCamera(
                CameraUpdateFactory.newCameraPosition(cameraPosition), TWO_SECONDS_IN_MILLISECONDS
        );
    }

    private void moveCameraToInclude(Point destination) {
        LatLng origin = new LatLng(lastLocation);
        LatLngBounds bounds = new LatLngBounds.Builder()
                .include(origin)
                .include(new LatLng(destination.latitude(), destination.longitude()))
                .build();
        Resources resources = getResources();
        int routeCameraPadding = (int) resources.getDimension(R.dimen.component_navigation_route_camera_padding);
        int[] padding = {routeCameraPadding, routeCameraPadding, routeCameraPadding, routeCameraPadding};
        CameraPosition cameraPosition = navigationMap.retrieveMap().getCameraForLatLngBounds(bounds, padding);
        navigationMap.retrieveMap().animateCamera(
                CameraUpdateFactory.newCameraPosition(cameraPosition), TWO_SECONDS_IN_MILLISECONDS
        );
    }

    private void moveCameraOverhead() {
        if (lastLocation == null) {
            return;
        }
        CameraPosition cameraPosition = buildCameraPositionFrom(lastLocation, DEFAULT_BEARING);
        navigationMap.retrieveMap().animateCamera(
                CameraUpdateFactory.newCameraPosition(cameraPosition), TWO_SECONDS_IN_MILLISECONDS
        );
    }

    @Nullable
    private CameraUpdate cameraOverheadUpdate() {
        if (lastLocation == null) {
            return null;
        }
        CameraPosition cameraPosition = buildCameraPositionFrom(lastLocation, DEFAULT_BEARING);
        return CameraUpdateFactory.newCameraPosition(cameraPosition);
    }

    @NonNull
    private CameraPosition buildCameraPositionFrom(Location location, double bearing) {
        return new CameraPosition.Builder()
                .zoom(DEFAULT_ZOOM)
                .target(new LatLng(location.getLatitude(), location.getLongitude()))
                .bearing(bearing)
                .tilt(DEFAULT_TILT)
                .build();
    }

    private void adjustMapPaddingForNavigation() {
        Resources resources = getResources();
        int mapViewHeight = mapView.getHeight();
        int bottomSheetHeight = (int) resources.getDimension(R.dimen.component_navigation_bottomsheet_height);
        int topPadding = mapViewHeight - (bottomSheetHeight * BOTTOMSHEET_PADDING_MULTIPLIER);
        navigationMap.retrieveMap().setPadding(ZERO_PADDING, topPadding, ZERO_PADDING, ZERO_PADDING);
    }

    private void resetMapAfterNavigation() {
        navigationMap.removeRoute();
        navigationMap.clearMarkers();
        navigation.stopNavigation();
        moveCameraOverhead();
    }

    private void removeLocationEngineListener() {
        if (locationEngine != null) {
            locationEngine.removeLocationUpdates(callback);
        }
    }

    @SuppressLint("MissingPermission")
    private void addLocationEngineListener() {
        if (locationEngine != null) {
            LocationEngineRequest request = buildEngineRequest();
            locationEngine.requestLocationUpdates(request, callback, null);
        }
    }

    private void calculateRouteWith(Point destination, boolean isOffRoute) {
        Point origin = Point.fromLngLat(lastLocation.getLongitude(), lastLocation.getLatitude());
        Double bearing = Float.valueOf(lastLocation.getBearing()).doubleValue();
        NavigationRoute.builder(context)
                .accessToken(Mapbox.getAccessToken())
                .origin(origin, bearing, BEARING_TOLERANCE)
                .destination(destination)
                .build()
                .getRoute(new Callback<DirectionsResponse>() {
                    @Override
                    public void onResponse(@NonNull Call<DirectionsResponse> call, @NonNull Response<DirectionsResponse> response) {
                        handleRoute(response, isOffRoute);
                    }

                    @Override
                    public void onFailure(@NonNull Call<DirectionsResponse> call, @NonNull Throwable throwable) {
                        Timber.e("fuck;");
                    }
                });
    }


    public void navigateToDestination(Point destination)
    {
        this.destination = destination;
        if(mapLoaded && lastLocation != null) calculateRouteWith(destination, false);
    }

    private void quickStartNavigation() {
        // Transition to navigation state


        // Show the InstructionView
        instructionView.setVisibility(View.VISIBLE);

        adjustMapPaddingForNavigation();
        // Updates camera with last location before starting navigating,
        // making sure the route information is updated
        // by the time the initial camera tracking animation is fired off
        // Alternatively, NavigationMapboxMap#startCamera could be used here,
        // centering the map camera to the beginning of the provided route
        navigationMap.resumeCamera(lastLocation);
        navigation.startNavigation(route);

        // Location updates will be received from ProgressChangeListener
        removeLocationEngineListener();

        // TODO remove example usage
        navigationMap.resetCameraPositionWith(NavigationCamera.NAVIGATION_TRACKING_MODE_GPS);
        CameraUpdate cameraUpdate = cameraOverheadUpdate();
        if (cameraUpdate != null) {
            NavigationCameraUpdate navUpdate = new NavigationCameraUpdate(cameraUpdate);
            navigationMap.retrieveCamera().update(navUpdate);
        }
    }

    private void handleRoute(Response<DirectionsResponse> response, boolean isOffRoute) {
        List<DirectionsRoute> routes = response.body().routes();
        if (!routes.isEmpty()) {
            route = routes.get(FIRST);
            navigationMap.drawRoute(route);
            if (isOffRoute) {
                navigation.startNavigation(route);
            }else quickStartNavigation();
        }
    }


    @NonNull
    private LocationEngineRequest buildEngineRequest() {
        return new LocationEngineRequest.Builder(UPDATE_INTERVAL_IN_MILLISECONDS)
                .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
                .setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS)
                .build();
    }


    private static class MapboxNavigationViewLocationCallback implements LocationEngineCallback<LocationEngineResult> {

        private final WeakReference<MapboxNavigationView> activityWeakReference;

        MapboxNavigationViewLocationCallback(MapboxNavigationView activity) {
            this.activityWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void onSuccess(LocationEngineResult result) {
            MapboxNavigationView activity = activityWeakReference.get();
            if (activity != null) {
                Location location = result.getLastLocation();
                if (location == null) {
                    return;
                }
                activity.checkFirstUpdate(location);
                activity.updateLocation(location);
            }
        }

        @Override
        public void onFailure(@NonNull Exception exception) {

        }
    }
}

Я не нашел никакого решения.

...