web-dev-qa-db-ja.com

FusedLocationProviderClient.removeLocationUpdatesは常に失敗を返します

activityと呼ばれるベースclassを拡張するLocationAwareActivityがあります。このLocationAwareActivityactivityは、ロケーションサービスクライアントを作成するだけです。

LocationServices.getFusedLocationProviderClientおよびlistensから場所の更新。

このアクティビティのソースはこちらhttps://github.com/snijsure/MultiActivity/blob/master/app/src/main/ Java/com/example/subodhnijsure/multiactivity/LocationAwareActivity.Java

そして、アクティビティが破棄されると、removeLocationUpdatesが呼び出されます。私が見つけているのは

  • removeLocationUpdateは、常に失敗を返すタスクを返します
  • さらに懸念されるのは、ロケーションアクティビティが削除されていないため、activityがガベージコレクションされていないことです。

enter image description here -したがって、LocationAwareActivityから継承するアクティビティを開始すると、そのアクティビティは常にヒープに留まります。

したがって、問題は、場所の更新の受信を停止して、activityをガベージコレクションできるようにする正しい方法は何かということです。

このプロジェクトのソース全体はここからアクセスできます- https://github.com/snijsure/MultiActivity

12
Subodh Nijsure

removeLocationUpdatesでは、locationCallbackを渡す必要がありますが、現在の実装は間違っています。

それでも、どこかでメモリリークが発生する可能性があります。アプリに Leakcanary を統合してみてください。参照ツリーが表示され、このメモリリークの原因となっているフィールドまたはリスナーがわかります。あなたは参照することができます ここに私の唯一のブログ投稿の1つ

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");
            }
        }
    }
9
Akhil

リポジトリ内のコードを見ると、Activityのリークを引き起こす可能性のある設計上の問題がいくつか見つかりました。

1)2つの異なるLocationCallbacksを使用しています。 1つはstartメソッドに、もう1つはstopメソッドにありますが、実際には同じものを使用する必要があります。したがって、一度インスタンス化するだけで十分であり、Taskを削除すると、おそらくLocationCallbackの結果も成功するでしょう。

2)LocationCallbackAnonymous Classで2回インスタンス化するため、含まれているクラスを終了しても、内部クラスの非静的参照を保持しているため、Memory Leakが発生します。あなたはこれについてもっと読むことができます ここ

3)私見では、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;
    }
}

私はあなたのコードを試していなかったので、これは単なる推測ですが、解決策はとにかくあなたを助けるはずです。私が間違っている場合は私を訂正してください;)

4

こんにちは@SubodhNijsure以下のコードを確認し、コードに貼り付けて確認してください。

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()を1回呼び出し、onDestroy()、onStop()などの他のメソッドから削除してください。一度停止するので、複数回呼び出す必要があるのはなぜですか。 。

これがお役に立てば幸いです。

4
Jyubin Patel

Taskオブジェクトがfalseを返す理由は、stopLocationUpdatesメソッドにあります。ここでも、ローカルの_**LocationCallback**_参照を作成し、この参照をlocationProviderClient.removeLocationUpdates(cL);の引数として使用しています。ローカルLocationCallBackがlocationProviderClientに存在することはありません

したがって、別のLocationCallBackオブジェクトを作成する代わりに、startLocationUpdatesメソッドでインスタンス化するのと同じグローバルオブジェクトを渡す必要があります。

コードは次のようになります

_inal Task<Void> voidTask = locationProviderClient.removeLocationUpdates(locationCallback);
_
3
Hasif Seyd