2017-02-13 3 views
0

LeakCanary를 사용하여 MapBox를 사용하여지도를 표시하는 활동을 종료 할 때 메모리 누수가 발생했다고합니다. 아래 힙 정보 및 코드. 나는 아무것도 보이지 않지만, 나는 무엇을 찾고 있는지 모른다. 원인을 알 수 있습니까?MapBox를 사용한 메모리 누수

com.myapp.debug : 1.0 : 1. * com.myapp.DirectionsActivity가 유출했습니다 * android.app.ActivityManager.mContext 정적 * GC의 ROOT 것은

  • 참조 키 com.myapp.DirectionsActivity 인스턴스를 누출 : b0fc445c-fe16-46e8-aff4-71acd3924d52
  • 장치 : 삼성 삼성 SM-T530NU는
  • 안드로이드 버전을 matissewifiue : 5.0.2 API : 21 LeakCanary : 1.3.1
  • 길이 : 시간 =의 5272ms, GC = 189ms, 힙 덤프 = 6599ms, 분석을보고 = 75048ms

  • 세부 사항 :

  • 클래스 android.app.ActivityManager | static $ staticOverhead = byte [] [id = 0x70bc2699; 길이 = 504; 크기 = 520] | 정적 AMS_POLICY_ENFORCING = java.lang.String [id = 0x701edfb0] | 정적 META_HOME_ALTERNATE = java.lang.String [id = 0x70584898] | 정적 TAG = java.lang.String [id = 0x704a96a0] | 정적 mContext = com.myapp.DirectionsActivity [id = 0x13061da0] | 정적 BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1 | 정적 BROADCAST_SUCCESS = 0 | 정적 COMPAT_MODE_ALWAYS = -1 | 정적 COMPAT_MODE_DISABLED = 0 | 정적 COMPAT_MODE_ENABLED = 1 | 정적 COMPAT_MODE_NEVER = -2 | 정적 COMPAT_MODE_TOGGLE = 2 | 정적 COMPAT_MODE_UNKNOWN = -3 | 정적 INTENT_SENDER_ACTIVITY = 2 |
    정적 INTENT_SENDER_ACTIVITY_RESULT = 3 | 정적 INTENT_SENDER_BROADCAST = 1 | 정적 INTENT_SENDER_SERVICE = 4 |
    정적 MOVE_TASK_NO_USER_ACTION = 2 | 정적 MOVE_TASK_WITH_HOME = 1 | 정적 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 | 정적 PROCESS_STATE_HOME = 9 | 정적 PROCESS_STATE_IMPORTANT_BACKGROUND = 4 | 정적 PROCESS_STATE_IMPORTANT_FOREGROUND = 3 | 정적 PROCESS_STATE_LAST_ACTIVITY = 10 | 정적 PROCESS_STATE_PERSISTENT = 0 | 정적 PROCESS_STATE_PERSISTENT_UI = 1 | 정적 PROCESS_STATE_RECEIVER = 8 | 정적 PROCESS_STATE_SERVICE = 7 |
    정적 PROCESS_STATE_TOP = 2 | static RECENT_IGNORE_HOME_STACK_TASKS = 8 | 정적 RECENT_IGNORE_UNAVAILABLE = 2 | 정적 RECENT_INCLUDE_PROFILES = 4 | 정적 RECENT_WITH_EXCLUDED = 1 |
    정적 REMOVE_ALL_RECENT_TASKS = 8 | 정적 REMOVE_ALL_TASKS = 2 |
    정적 REMOVE_TASK_EXCEPT_RECENTS = 16 | 정적 REMOVE_TASK_IMMEDIATELY = 4 | 정적 REMOVE_TASK_KILL_PROCESS = 1 | 정적 START_CANCELED = -6 | 정적 START_CANCELED_BY_TEMPERATURE = -8 | 정적 START_CLASS_NOT_FOUND = -2 | 정적 START_DELIVERED_TO_TOP = 3 | 정적 START_FLAG_DEBUG = 2 | static START_FLAG_ONLY_IF_NEEDED = 1 | 정적 START_FLAG_OPENGL_TRACES = 4 | 정적 START_FORWARD_AND_REQUEST_CONFLICT = -3 | static START_INTENT_NOT_RESOLVED = -1 | 정적 START_NOT_ACTIVITY = -5 |
    정적 START_NOT_VOICE_COMPATIBLE = -7 | static START_PERMISSION_DENIED = -4 | 정적 START_RETURN_INTENT_TO_CALLER = 1 | 정적 START_RETURN_LOCK_TASK_MODE_VIOLATION = 5 | 정적 START_SUCCESS = 0 | 정적 START_SWITCHES_CANCELED = 4 | static START_TASK_TO_FRONT = 2 | 정적 USER_OP_IS_CURRENT = -2 | static USER_OP_SUCCESS = 0 | 정적 USER_OP_UNKNOWN_USER = -1 | 정적 gMaxRecentTasks = -1 | 정적 localLOGV = false
  • 인스턴스 com.myapp.DirectionsActivity | TAG = java.lang.String [id = 0x1347b5c0] | client = 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.문자열 [id = 0x1347b260] | DEBUG_ELASTIC = false | isElasticEnabled = false | mCalled = true |
    mChangeCanvasToTranslucent = false | mChangingConfigurations = false | mCheckedForLoaderManager = true | mConfigChangeFlags = 0 |
    mDefaultKeyMode = 0 | mDestroyed = true | mDoReportFullyDrawn = false | mEnableDefaultActionBarUp = false | mFinished = true |
    mFlipfont = 0 | mIdent = 254734993 | mLoadersStarted = false |
    mPreventEmbeddedTabs = false | mResultCode = 0 | mResumed = false | mStackedHeight = -1 | mStartedActivity = false | mStopped = true | 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(); 
     } 
    }); 

답변

0

당신이 사용자의 위치를 ​​점점 정확히 어떻게? 응용 프로그램이 포 그라운드에 있지 않을 때 위치 요청을 제거하지 않으면 메모리 누수가 발생할 수 있습니다. 길 찾기 API의 경우 드물지만 요청이 접수되었지만 API 응답이 발생하기 전에 사용자가 앱을 종료하는 경우 요청을 취소해야합니다.

관련 문제