使用MapBox

时间:2017-02-13 18:54:25

标签: android memory-leaks mapbox heap-memory leakcanary

使用LeakCanary,它说退出使用MapBox显示地图的活动时出现内存泄漏。 堆信息和代码如下。我什么也看不见,但是,我不知道我在寻找什么。你能看出原因吗?

  

在com.myapp.debug中:1.0:1。   * com.myapp.DirectionsActivity泄露:   * GC ROOT static android.app.ActivityManager.mContext   *泄漏com.myapp.DirectionsActivity实例

     
      
  • 参考密钥:b0fc445c-fe16-46e8-aff4-71acd3924d52
  •   
  • 设备:samsung samsung SM-T530NU matissewifiue
  •   
  • Android版本:5.0.2 API:21 LeakCanary:1.3.1
  •   
  • 持续时间:watch = 5272ms,gc = 189ms,堆转储= 6599ms,分析= 75048ms

  •   
  • 详细说明:

  •   
  • 类android.app.ActivityManager | static $ staticOverhead = byte [] [id = 0x70bc2699; length = 504; size = 520] |静态的   AMS_POLICY_ENFORCING = java.lang.String [id = 0x701edfb0] |静态的   META_HOME_ALTERNATE = java.lang.String [id = 0x70584898] |静态标签   = java.lang.String [id = 0x704a96a0] | static mContext = com.myapp.DirectionsActivity [id = 0x13061da0] |静态的   BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1 |静态的   BROADCAST_SUCCESS = 0 | static COMPAT_MODE_ALWAYS = -1 |静态的   COMPAT_MODE_DISABLED = 0 | static COMPAT_MODE_ENABLED = 1 |静态的   COMPAT_MODE_NEVER = -2 | static COMPAT_MODE_TOGGLE = 2 |静态的   COMPAT_MODE_UNKNOWN = -3 |静态INTENT_SENDER_ACTIVITY = 2 |
      static INTENT_SENDER_ACTIVITY_RESULT = 3 |静态的   INTENT_SENDER_BROADCAST = 1 |静态INTENT_SENDER_SERVICE = 4 |
      静态MOVE_TASK_NO_USER_ACTION = 2 | static MOVE_TASK_WITH_HOME = 1   | static PROCESS_STATE_BACKUP = 5 |静态的   PROCESS_STATE_CACHED_ACTIVITY = 11 |静态的   PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12 |静态的   PROCESS_STATE_CACHED_EMPTY = 13 |静态PROCESS_STATE_HEAVY_WEIGHT   = 6 | static PROCESS_STATE_HOME = 9 | static PROCESS_STATE_IMPORTANT_BACKGROUND = 4 |静态的   PROCESS_STATE_IMPORTANT_FOREGROUND = 3 |静态的   PROCESS_STATE_LAST_ACTIVITY = 10 | static PROCESS_STATE_PERSISTENT =   0 | static PROCESS_STATE_PERSISTENT_UI = 1 |静态的   PROCESS_STATE_RECEIVER = 8 | static PROCESS_STATE_SERVICE = 7 |
      static PROCESS_STATE_TOP = 2 |静态RECENT_IGNORE_HOME_STACK_TASKS   = 8 |静态RECENT_IGNORE_UNAVAILABLE = 2 |静态RECENT_INCLUDE_PROFILES = 4 |静态RECENT_WITH_EXCLUDED = 1 |
      static REMOVE_ALL_RECENT_TASKS = 8 |静态REMOVE_ALL_TASKS = 2 |
      static REMOVE_TASK_EXCEPT_RECENTS = 16 |静态的   REMOVE_TASK_IMMEDIATELY = 4 | static REMOVE_TASK_KILL_PROCESS = 1 |   静态START_CANCELED = -6 |静态START_CANCELED_BY_TEMPERATURE =   -8 | static START_CLASS_NOT_FOUND = -2 |静态START_DELIVERED_TO_TOP = 3 |静态START_FLAG_DEBUG = 2 |静态的   START_FLAG_ONLY_IF_NEEDED = 1 |静态START_FLAG_OPENGL_TRACES = 4   |静态START_FORWARD_AND_REQUEST_CONFLICT = -3 |静态的   START_INTENT_NOT_RESOLVED = -1 |静态START_NOT_ACTIVITY = -5 |
      静态START_NOT_VOICE_COMPATIBLE = -7 |静态的   START_PERMISSION_DENIED = -4 |静态START_RETURN_INTENT_TO_CALLER   = 1 |静态START_RETURN_LOCK_TASK_MODE_VIOLATION = 5 | static START_SUCCESS = 0 |静态START_SWITCHES_CANCELED = 4 |静态的   START_TASK_TO_FRONT = 2 | static USER_OP_IS_CURRENT = -2 |静态的   USER_OP_SUCCESS = 0 | static USER_OP_UNKNOWN_USER = -1 |静态的   gMaxRecentTasks = -1 | static localLOGV = false
  •   
  • com.myapp.DirectionsActivity |的实例TAG = java.lang.String [id = 0x1347b5c0] |客户=   com.mapbox.services.directions.v5.MapboxDirections [id = 0x1351f4c0] |
      currentRoute =   com.mapbox.services.directions.v5.models.DirectionsRoute   [id = 0x136c94e0] | map = com.mapbox.mapboxsdk.maps.MapboxMap   [id = 0x12c94600] | mapView = com.mapbox.mapboxsdk.maps.MapView   [id = 0x13422400] | mDelegate =   android.support.v7.app.AppCompatDelegateImplV14 [id = 0x12c30fc0] |
      mResources = null | mEatKeyUpEvent = false | mThemeId = 2131361951   | mFragments = android.support.v4.app.FragmentController   [id = 0x13473ac0] | mHandler =   android.support.v4.app.FragmentActivity $ 1 [id = 0x1347b5a0] |
      mMediaController = null | mPendingFragmentActivityResults =   android.support.v4.util.SparseArrayCompat [id = 0x13482080] | mCreated   = true | mNextCandidateRequestIndex = 0 | mOptionsMenuInvalidated = false | mReallyStopped = true | mRequestedPermissionsFromFragment = false | mResumed = false |
      mRetaining = false | mStopped = true |
      mStartedActivityFromFragment = false |
      mStartedIntentSenderFromFragment = false | mActionBar = null |
      mActivityInfo = android.content.pm.ActivityInfo [id = 0x12f34340] |
      mActivityTransitionState = android.app.ActivityTransitionState   [id = 0x1347a280] | mAllLoaderManagers = android.util.ArrayMap   [id = 0x134b4d80] | mApplication = android.app.Application   [id = 0x12c95a80] | mComponent = android.content.ComponentName   [id = 0x12c920b0] | mContainer = android.app.Activity $ 1   [id = 0x13473a90] | mCurrentConfig = android.content.res.Configuration   [id = 0x13475a20] | mDecor = null | mDefaultKeySsb = null |
      mEmbeddedID = null | mEnterTransitionListener =   android.app.SharedElementCallback $ 1 [id = 0x724871f0] |
      mExitTransitionListener = android.app.SharedElementCallback $ 1   [id = 0x724871f0] | mFeatureContextMenuListener =   android.app.Activity $ FeatureContextMenuListener [id = 0x13473a60] |
      mFragments = android.app.FragmentManagerImpl [id = 0x134757f0] |
      mHandler = android.os.Handler [id = 0x1347b580] | mInjectionManager =   null | mInstanceTracker = android.os.StrictMode $ InstanceTracker   [id = 0x13473aa0] | mInstrumentation = android.app.Instrumentation   [id = 0x12c2cd90] | mIntent = android.content.Intent [id = 0x133fb390] |   mLastNonConfigurationInstances = null | mLauncherBooster = null |
      mLoaderManager = null | mMainThread = android.app.ActivityThread   [id = 0x12c42100] | mManagedCursors = java.util.ArrayList   [id = 0x1347b540] | mManagedDialogs = null | mMenuInflater = null |   mMultiWindowStyle = com.samsung.android.multiwindow.MultiWindowStyle   [id = 0x13478790] | mParent = null | mResultData = null |
      mSearchManager = null | mSubDecor = null | mSubWindow = null |
      mSubWindowDummpy = null | mTitle = java.lang.String [id = 0x134a3dc0]   | mToken = android.os.BinderProxy [id = 0x12c82fa0] |
      mTranslucentCallback = null | mUiThread = java.lang.Thread   [id = 0x8736cfb0] | mVoiceInteractor = null | mWindow =   com.android.internal.policy.impl.PhoneWindow [id = 0x12db0110] |
      mWindowManager = android.view.WindowManagerImpl [id = 0x1347ba00] |
      myName = java.lang.String [id = 0x1347b260] | DEBUG_ELASTIC = false |   isElasticEnabled = false | mCalled = true |
      mChangeCanvasToTranslucent = false | mChangingConfigurations = false   | mCheckedForLoaderManager = true | mConfigChangeFlags = 0 |
      mDefaultKeyMode = 0 | mDestroyed = true | mDoReportFullyDrawn =   假| mEnableDefaultActionBarUp = false | mFinished = true |
      mFlipfont = 0 | mIdent = 254734993 | mLoadersStarted = false |
      mPreventEmbeddedTabs = false | mResultCode = 0 | mResumed = false   | mStackedHeight = -1 | mStartedActivity = false | mStopped =   是的| mSubWindowAdded = false | mTemporaryPause = false |
      mTitleColor = 0 | mTitleReady = true | mVisibleBehind = false |
      mVisibleFromClient = true | mVisibleFromServer = true |
      mWindowAdded = true | mInflater =   com.android.internal.policy.impl.PhoneLayoutInflater [id = 0x134789a0] |   mOverrideConfiguration = null | mResources =   android.content.res.Resources [id = 0x12c24700] | mTheme =   android.content.res.Resources $ Theme [id = 0x1347ba20] | mThemeResource   = 2131361951 | mBase = android.app.ContextImpl [id = 0x12e06b20]
  •   
public class DirectionsActivity extends AppCompatActivity {

    private final String TAG = "DirectionsActivity";

    private MapView mapView;
    private MapboxMap map;
    private DirectionsRoute currentRoute;

    private MapboxDirections client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // employee location is destination
        Intent intent = getIntent();
        double employeeLatitude = intent.getDoubleExtra("latitude", new Double(getString(R.string.state_capital_latitude)));
        double employeeLongitude = intent.getDoubleExtra("longitude", new Double(getString(R.string.state_capital_longitude)));

        // Mapbox access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        MapboxAccountManager.start(this, getString(R.string.access_token));

        // This contains the MapView in XML and needs to be called after the account manager
        setContentView(R.layout.activity_mas_directions);

        // get current location from preferences for use as origin
        Location currentLocation = PreferencesUtilities.getCurrentLocation(this.getApplicationContext());
        final Position origin = Position.fromCoordinates(currentLocation.getLongitude(), currentLocation.getLatitude());

        final Position destination = Position.fromCoordinates(employeeLongitude, employeeLatitude); // yes, long,lat - strange

        this.setTitle("Origin: (" + origin.getLatitude() + ", " + origin.getLongitude() + ")  >>  Destination: (" + destination.getLatitude() + ", " + destination.getLongitude() + ")" );

        // Create Icon objects for the marker to use
        IconFactory iconFactory = IconFactory.getInstance(this);
        Drawable iconDrawable = ContextCompat.getDrawable(this, R.drawable.green_pin); // pin png is 125x125
        final Icon greenPinIcon = iconFactory.fromDrawable(iconDrawable);
        iconDrawable = ContextCompat.getDrawable(this, R.drawable.red_pin); // pin png is 125x125
        final Icon redPinIcon = iconFactory.fromDrawable(iconDrawable);

        // Setup the MapView
        mapView = (MapView) findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(MapboxMap mapboxMap) {
                map = mapboxMap;

                // Add origin and destination to the map
                LatLng originLatLng = (new LatLng(origin.getLatitude(), origin.getLongitude()));
                mapboxMap.addMarker(new MarkerViewOptions()
                        .position(originLatLng)
                        .anchor((float)0.5, (float)1.0) // bottom, middle I think
                        //.anchor(1, (float)0.5) // (0,0) is top left
                        .title("Origin")
                        .snippet("current location: (" + origin.getLatitude() + ", " + origin.getLongitude() + ")")
                        .icon(greenPinIcon));

                LatLng destinationLatLng = (new LatLng(destination.getLatitude(), destination.getLongitude()));
                mapboxMap.addMarker(new MarkerViewOptions()
                        .position(destinationLatLng)
                        .anchor((float)0.5, (float)1.0) // bottom, middle I think
                        //.anchor(1, (float)0.5) // (0,0) is top left
                        .title("Destination")
                        .snippet("destination: (" + destination.getLatitude() + ", " + destination.getLongitude() + ")")
                        .icon(redPinIcon));

                LatLngBounds latLngBounds = new LatLngBounds.Builder()
                        .include(originLatLng) // Northeast
                        .include(destinationLatLng) // Southwest
                        .build();

                mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, 50), 5000);

                // Get route from API
                try {
                    getRoute(origin, destination);
                }
                catch (ServicesException servicesException) {
                    Log.e(TAG, servicesException.toString());
                    servicesException.printStackTrace();
                }
            }
        });
    }

    private void getRoute(Position origin, Position destination) throws ServicesException {

        client = new MapboxDirections.Builder()
                .setOrigin(origin)
                .setDestination(destination)
                .setProfile(DirectionsCriteria.PROFILE_CYCLING)
                .setAccessToken(MapboxAccountManager.getInstance().getAccessToken())
                .build();

        client.enqueueCall(new Callback<DirectionsResponse>() {
            @Override
            public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
                // You can get the generic HTTP info about the response
                //Log.d(TAG, "Response code: " + response.code());
                if (response.body() == null) {
                    Log.e(TAG, "No routes found, make sure you set the right user and access token.");
                    return;
                } else if (response.body().getRoutes().size() < 1) {
                    Log.e(TAG, "No routes found");
                    return;
                }

                // Print some info about the route
                currentRoute = response.body().getRoutes().get(0);
                //Log.d(TAG, "Distance: " + currentRoute.getDistance());
                Double km = currentRoute.getDistance() / 1000;
                // there are 4 digits to the right of the decimal, make it 2
                String kilometers = km.toString();
                int index = kilometers.lastIndexOf(".");
                kilometers = kilometers.substring(0, index + 3);
                Toast.makeText(
                        DirectionsActivity.this,
                        "Route is " + kilometers + " kilometers",
                        Toast.LENGTH_SHORT).show();

                // Draw the route on the map
                drawRoute(currentRoute);
            }

            @Override
            public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
                Log.e(TAG, "Error: " + throwable.getMessage());
                Toast.makeText(DirectionsActivity.this, "Error: " + throwable.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void drawRoute(DirectionsRoute route) {
        // Convert LineString coordinates into LatLng[]
        LineString lineString = LineString.fromPolyline(route.getGeometry(), Constants.OSRM_PRECISION_V5);
        List<Position> coordinates = lineString.getCoordinates();
        LatLng[] points = new LatLng[coordinates.size()];
        for (int i = 0; i < coordinates.size(); i++) {
            points[i] = new LatLng(
                    coordinates.get(i).getLatitude(),
                    coordinates.get(i).getLongitude());
        }

        // Draw Points on MapView
        map.addPolyline(new PolylineOptions()
                .add(points)
                .color(Color.parseColor("#009688"))
                .width(5));
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Cancel the directions API request
        if (client != null) {
            client.cancelCall();
        }
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }
}

编辑:
添加信息以响应@cammace。这就是我获取用户位置的方式。应用程序位于前台时(而且从未出现在后台)发生泄漏。

MainActivity

private Observable<Location> locationUpdateObservable;
private Subscription locationUpdateSubscription;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Location Observable
    final LocationRequest locationRequest = LocationRequest.create()
            //.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(30000); // milliseconds
    // net.kjulio.rxlocation
    locationUpdateObservable = RxLocation.locationUpdates(mActivity, locationRequest);
    locationUpdateSubscription = locationUpdateObservable.subscribe(new Action1<Location>() {
        @Override
        public void call(Location location) {
            // save current location to preferences
            PreferencesUtilities.setCurrentLocation(mActivity.getApplicationContext(), location);
        }
    }, new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            Toast.makeText(mActivity, "Can't get location Throwable: " + throwable.toString(),  Toast.LENGTH_SHORT).show();
        }
    });

1 个答案:

答案 0 :(得分:0)

您如何获得用户位置?如果您的应用程序不在前台时没有删除位置请求,这可能会导致内存泄漏。此外,对于路线API,您应该在极少数情况下取消您的请求,但是在API响应发生之前,用户会退出应用。