You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Location Manager内存泄漏及启动页权限问题求助

Hey there! Let's tackle your two issues one by one—first the LocationManager memory leak detected by LeakCanary, then the permission request getting stuck in the background because your splash screen progresses too fast.

Fixing the LocationManager Memory Leak

The root cause here is that your anonymous LocationListener classes hold a strong reference to your Splash Activity. Since the LocationManager is a system-level service, it can outlive your Activity's lifecycle. Even if you call removeUpdates, if the listener still has that strong reference, the Activity can't be garbage collected, leading to a leak.

Solution Steps:

  1. Convert LocationListeners to static inner classes with WeakReference
    Static inner classes don't hold an implicit reference to the outer Activity. We'll use a WeakReference to access the Activity only when it's still alive, so the GC can collect the Activity once it's destroyed.

  2. Double-check listener cleanup timing
    Your existing stopLocationListener logic is solid, but the static listener+WeakRef combo adds an extra safety net even if cleanup is slightly delayed.

Modified Code Example:

First, rewrite your listeners as static inner classes:

private static class NetworkLocationListener implements LocationListener {
    private final WeakReference<SplashActivity> activityRef;

    public NetworkLocationListener(SplashActivity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void onLocationChanged(Location location) {
        SplashActivity activity = activityRef.get();
        if (activity != null) {
            activity.newLocationReceived(location);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    @Override
    public void onProviderEnabled(String provider) {}

    @Override
    public void onProviderDisabled(String provider) {}
}

private static class GPSLocationListener implements LocationListener {
    private final WeakReference<SplashActivity> activityRef;

    public GPSLocationListener(SplashActivity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void onLocationChanged(Location location) {
        SplashActivity activity = activityRef.get();
        if (activity != null) {
            activity.newLocationReceived(location);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    @Override
    public void onProviderEnabled(String provider) {}

    @Override
    public void onProviderDisabled(String provider) {}
}

Then initialize them in your Activity's onCreate:

private LocationListener locationListenerNetwork;
private LocationListener locationListenerGPS;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ... other initialization code
    locationListenerNetwork = new NetworkLocationListener(this);
    locationListenerGPS = new GPSLocationListener(this);
    getLocation();
}
Fixing Permission Request Getting Stuck in the Background

Your splash screen is probably navigating to the second Activity before the RxPermission request completes. When the navigation happens, the splash Activity moves to the background, so the permission dialog (tied to the splash Activity) also goes invisible to the user.

Solution Steps:

  1. Delay navigation until permission handling and location retrieval are done
    Don't start the second Activity until you've either successfully gotten the location, handled permission denial, or hit a reasonable timeout (to avoid hanging the splash screen forever).

  2. Add a location retrieval timeout
    If GPS/network location takes too long (e.g., poor signal), you'll still proceed to the next Activity instead of waiting indefinitely.

Modified Code Example:

First, add a handler for the timeout:

private Handler locationTimeoutHandler;
private static final long LOCATION_TIMEOUT_MS = 5000; // 5 seconds, adjust as needed

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ... other initialization
    locationTimeoutHandler = new Handler(Looper.getMainLooper());
    locationListenerNetwork = new NetworkLocationListener(this);
    locationListenerGPS = new GPSLocationListener(this);
    getLocation();
}

Update getLocation to trigger the timeout and only navigate after permission processing:

@SuppressLint("MissingPermission")
private void getLocation() {
    rxPermissions
        .request(Manifest.permission.ACCESS_FINE_LOCATION)
        .subscribe(granted -> {
            if (granted) {
                boolean gpsEnabled, networkEnabled;
                locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                if (locationManager != null) {
                    gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                    networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
                    if (gpsEnabled && locationListenerGPS != null) {
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f, locationListenerGPS);
                    }
                    if (networkEnabled && locationListenerNetwork != null) {
                        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0L, 0f, locationListenerNetwork);
                    }
                    // Start timeout to proceed even if location isn't received
                    locationTimeoutHandler.postDelayed(this::navigateToNextActivity, LOCATION_TIMEOUT_MS);
                }
            } else {
                Timber.e("LOCATION PERMISSION DENIED");
                // Handle denial: show a toast then navigate, or prompt user to enable permissions
                navigateToNextActivity();
            }
        });
}

Update newLocationReceived to cancel the timeout and navigate once location is found:

private void newLocationReceived(Location location) {
    // Cancel the timeout since we got a valid location
    locationTimeoutHandler.removeCallbacksAndMessages(null);
    
    if (locationManager != null) {
        locationManager.removeUpdates(locationListenerNetwork);
        locationManager.removeUpdates(locationListenerGPS);
    }
    locationManager = null;
    this.location = location;
    
    // Navigate to next activity now that we have the location
    navigateToNextActivity();
}

Add a dedicated navigation method to avoid duplicate transitions:

private void navigateToNextActivity() {
    // Ensure we don't navigate if the Activity is already finishing/destroyed
    if (!isFinishing() && !isDestroyed()) {
        Intent intent = new Intent(this, SecondActivity.class);
        // Pass location data to the next Activity if needed
        if (location != null) {
            intent.putExtra("LOCATION", location);
        }
        startActivity(intent);
        finish();
    }
}

Finally, clean up the timeout handler in onDestroy to prevent extra leaks:

@Override
protected void onDestroy() {
    super.onDestroy();
    stopLocationListener();
    locationTimeoutHandler.removeCallbacksAndMessages(null);
}

内容的提问来源于stack exchange,提问作者user6683139

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:30:09