У меня есть activity
, который расширяет базу class
, называемую LocationAwareActivity
, все это LocationAwareActivity
activity
делает, создает клиент службы местоположения
LocationServices.getFusedLocationProviderClient
и listens
до
обновления местоположения.
Источник для этого действия здесь https://github.com/snijsure/MultiActivity/blob/master/app/src/main/java/com/example/subodhnijsure/multiactivity/LocationAwareActivity.java
И когда действие уничтожается, он вызывает removeLocationUpdates
. Я нахожусь
removeLocationUpdate
возвращает задачу, которая всегда возвращает не-успешноactivity
не получает сбор мусора.
- Поэтому, если я начинаю любую деятельность, которая наследует от LocationAwareActivity
, что активность всегда остается на куче.
Итак, вопрос в том, что является правильным способом прекратить получение обновлений местоположения, тем самым позволяя activity
собирать мусор.
Здесь можно получить доступ ко всему источнику для этого проекта - https://github.com/snijsure/MultiActivity
Изучив код в репозитории, я обнаружил некоторые проблемы в вашем дизайне, которые могут привести к утечке вашего Activity
.
1) Вы используете два разных LocationCallbacks
. Один в начале и один в методе остановки, но вы должны фактически использовать то же самое. Таким образом, один экземпляр экземпляра будет достаточным и приведет, вероятно, к успешному результату вашего Task
при удалении LocationCallback
.
2) Поскольку вы создаете экземпляр LocationCallback
дважды с помощью Anonymous Class
, вы сохраняете нестатические ссылки для внутреннего класса, даже если вы закончите класс, который вызывает ваш Memory Leak
. Вы можете прочитать об этом здесь.
3) IMHO лучше использовать отдельный класс менеджера для обработки ваших запросов местоположения, чем абстрагировать Activity
.
Здесь сказано, что я...
GpsManager.java
public class GpsManager extends LocationCallback {
private FusedLocationProviderClient client;
private Callback callback;
public interface Callback {
void onLocationResult(LocationResult locationResult);
}
public boolean start(Context context, Callback callback) {
this.callback = callback;
client = LocationServices.getFusedLocationProviderClient(context);
if (!checkLocationPermission(context)) return false;
client.requestLocationUpdates(getLocationRequest(), this, null);
return true;
}
public void stop() {
client.removeLocationUpdates(this);
}
@Override
public void onLocationResult(LocationResult locationResult) {
callback.onLocationResult(locationResult);
}
private boolean checkLocationPermission(Context context) {
int permissionCheck = ContextCompat.checkSelfPermission(
context, android.Manifest.permission.ACCESS_FINE_LOCATION);
return permissionCheck == PackageManager.PERMISSION_GRANTED;
}
private LocationRequest getLocationRequest() {
return LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(30_000L)
.setFastestInterval(20_000L);
}
}
и назвав это из вашего Activity
следующим образом
YourActivity.java
public class MapsActivity extends AppCompatActivity implements GpsManager.Callback {
private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
private GpsManager mGpsManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mGpsManager = new GpsManager(getApplicationContext(), this);
// check if user gave permissions, otherwise ask via dialog
if (!checkPermission()) {
getLocationPermissions();
return;
}
mGpsManager.start();
...
}
@Override
protected void onStop() {
super.onStop();
mGpsManager.stop();
}
@Override
public void onLocationResult(LocationResult locationResult) {
// do something with the locationResult
}
// CHECK PERMISSIONS PART
private boolean checkPermission() {
return isGranted(ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)) &&
isGranted(ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION));
}
@TargetApi(Build.VERSION_CODES.M)
private void getLocationPermissions() {
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_FINE_LOCATION);
}
@Override
public void onRequestPermissionsResult(int code, @Nullable String permissions[], @Nullable int[] results) {
switch (code) {
case PERMISSION_REQUEST_FINE_LOCATION:
if (isPermissionGranted(results)) {
getLocationRequest();
}
}
}
private boolean isPermissionGranted(int[] results) {
return results != null && results.length > 0 && isGranted(results[0]);
}
private boolean isGranted(int permission) {
return permission == PackageManager.PERMISSION_GRANTED;
}
}
Это просто предположение, потому что я не пробовал ваш код, но решение должно помочь вам в любом случае. Пожалуйста, поправьте меня, если я ошибаюсь;)
В removeLocationUpdates вам следует передать locationCallback, текущая реализация неверна.
Тем не менее, есть вероятность утечки памяти где-то в другом месте. Вы должны попробовать интегрировать Leakcanary в свое приложение, и оно может дать вам дерево ссылок и сообщит вам, какое поле или слушатель вызывает утечку памяти, Вы можете сослаться на один из моих сообщений только в блоге
public void stopLocationUpdates() {
if (locationProviderClient != null) {
try {
final Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
if (voidTask.isSuccessful()) {
Log.d(TAG,"StopLocation updates successful! ");
} else {
Log.d(TAG,"StopLocation updates unsuccessful! " + voidTask.toString());
}
}
catch (SecurityException exp) {
Log.d(TAG, " Security exception while removeLocationUpdates");
}
}
}
Причина, по которой объект Task возвращает false, находится в вашем методе stopLocationUpdates
, вы снова создаете локальную ссылку **LocationCallback**
, а затем используя эту ссылку в качестве аргумента в locationProviderClient.removeLocationUpdates(cL);
где локальная LocationCallBack никогда не присутствует в locationProviderClient
Так что вам нужно сделать, вместо того, чтобы создавать другой объект LocationCallBack, вы должны передать тот же глобальный объект, который вы создаете в своем методе startLocationUpdates
ваш код должен выглядеть следующим образом
inal Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
Привет @Subodh Nijsure Пожалуйста, проверьте код ниже и вставьте его в свой код и после его проверки:
final Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
voidTask.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
Log.e(TAG, "addOnCompleteListener: "+task.isComplete());
}
});
voidTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.e(TAG, "addOnSuccessListener: " );
}
});
voidTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "addOnFailureListener: ");
}
});
Я думаю, что voidTask.isSuccessful() этот метод не работает, когда вы помещаете этот слушатель в то время, когда он работает нормально, и я также вижу в памяти, что он освобождает всю память, когда приходит к предыдущему действию.
И когда вы перенаправляетесь на какое-либо действие, пожалуйста, stopLocationUpdates() вызывается один раз в onPause() и удаляется из другого метода, например onDestroy(), onStop(), потому что он останавливается один раз, поэтому почему мы должны вызовите несколько раз.
Надеюсь, это поможет вам.
locationProviderClient.removeLocationUpdates(locationCallback);
removeLocationUpdates продолжает возвращать ошибку, а действия продолжают протекать.