酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

AndroidでLocationManagerを使って位置情報を取得する

AndroidでLocationManagerを使って位置情報を取得する方法を紹介します。現在、minSdkVersionが19で開発しています。

事前情報

API 19ではFragment#requestPermissionsFragment#onRequestPermissionsResultが使えない*1ので、一旦パーミッションの許可をFragmentを持っているActivityで受け取ってから再度Fragmentで処理させます。

public class FragmentBase extends Fragment {
    public void onFragmentRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { }
}

MapFragment

public class MapFragment extends FragmentBase implements LocationListener {

    private static final int REQUEST_LOCATION_PERMISSION = 101;
  
    private LocationManager locationManager = null;
    //30秒に1度更新する
    private final int LOCATION_UPDATE_MIN_TIME = 30 * 1000;
    //10m移動する度に更新する
    private final int LOCATION_UPDATE_MIN_DISTANCE = 10;

    //(ここに後述するコードを追加していく)
}

Switchをオンオフすると位置測位を開始します。

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

  gpsSwitch.setOnCheckedChangeListener((compoundButton, b) -> {
    if (b) {
      //スイッチをオンにした!
      //GPSが許可されているかどうか確認する
      if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
          || ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        //パーミッションの許可を取得する
        ActivityCompat.requestPermissions(getActivity(),
            new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, REQUEST_LOCATION_PERMISSION);
      } else {
        //すでにGPSが許可されている場合は測位を開始する
        startLocation();
      }
    } else {
      //スイッチをオフにした!
      stopLocation();
    }
  });

  return view;
}

パーミッションの許可をもらったときの処理。

@Override
public void onFragmentRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  super.onFragmentRequestPermissionsResult(requestCode, permissions, grantResults);

  if (requestCode == REQUEST_LOCATION_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    // 使用が許可された
    startLocation();
  } else {
    //強制的にオフにする
    gpsSwitch.setChecked(false);
  }
}

位置測位を開始する。

補足すると、室内の場合に位置測位プロバイダがgpsだとほとんどonStatusChangedイベントが発生しない。networkにすると頻繁にイベントが発生するようになる。

private void startLocation() {
  if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
      && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

    locationManager = (LocationManager) getActivity().getSystemService(Activity.LOCATION_SERVICE);

    //位置測位プロバイダを決定する
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_COARSE);
    criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
    criteria.setBearingRequired(false);
    criteria.setSpeedRequired(false);
    criteria.setAltitudeRequired(false);
    String bestProvider = locationManager.getBestProvider(criteria, true);

    //測位を開始する
    locationManager.requestLocationUpdates(bestProvider,
        LOCATION_UPDATE_MIN_TIME, LOCATION_UPDATE_MIN_DISTANCE, this);
  }
}

位置測位を終了する。

private void stopLocation() {
  if (locationManager != null) {
    locationManager.removeUpdates(this);
  }
  locationManager = null;
}

LocationListenerの実装。

@Override
public void onLocationChanged(Location location) {
    final float longitude = Double.valueOf(location.getLongitude()).floatValue();
    final float latitude = Double.valueOf(location.getLatitude()).floatValue();;

    Log.e("MapFragment", String.format("lon:%f lat:%f", longitude, latitude));
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
    switch (i) {
        case LocationProvider.AVAILABLE:
            Log.e("MapFragment", "LocationProvider.AVAILABLE");
            break;
        case LocationProvider.OUT_OF_SERVICE:
            Log.e("MapFragment", "LocationProvider.OUT_OF_SERVICE");
            break;
        case LocationProvider.TEMPORARILY_UNAVAILABLE:
            Log.e("MapFragment", "LocationProvider.TEMPORARILY_UNAVAILABLE");
            break;
    }
}

@Override
public void onProviderEnabled(String s) {

}

@Override
public void onProviderDisabled(String s) {

}

*1:API 23以降でFragmentからパーミッションの許可を得ることができるようになった