Android位置情報の取得

# Androidでの簡単な位置情報取得（GPS）
これから勉強のためAndroidアプリを作ることになりました。
ライフログを活用する機能があるため、簡単に位置情報を取得する方法を調べてみました。
実際作るアプリでは長時間位置情報を取得するため、Serviceを立てて実行する必要があります。
Serviceに関しては次回に調査します。

## 権限設定
権限は2種類の設定があり、GPSからの取得とNetworkからの取得に分けられます。

```xml:AndroidManifest.xml
<!-- GPSから取得 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Networkから取得 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
```
GPS：高精度な位置情報を取得できるが、電力消費が激しい。屋内では使えない。
Network：位置情報の精度は落ちるが、電力消費が少ない。屋内でも取得できる

大体な特徴は上記のようです。
アプリの目的に応じて使い分ける必要があります。

```java:MainActivity.java
package yong.testproject;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity implements LocationListener {

    private LocationManager mLocationManager;
    private String bestProvider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initLocationManager();
    }

    @Override
    protected void onStart() {
        super.onStart();

        locationStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        locationStop();
    }

    private void initLocationManager() {
        // インスタンス生成
        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        // 詳細設定
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        criteria.setPowerRequirement(Criteria.POWER_HIGH);
        criteria.setSpeedRequired(false);
        criteria.setAltitudeRequired(false);
        criteria.setBearingRequired(false);
        criteria.setCostAllowed(true);
        criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
        criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
        bestProvider = mLocationManager.getBestProvider(criteria, true);
    }

    private void checkPermission() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // パーミッションの許可を取得する
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1000);
        }
    }

    private void locationStart() {
        checkPermission();
        mLocationManager.requestLocationUpdates(bestProvider, 60000, 3, this);
    }

    private void locationStop() {
        mLocationManager.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(Location location) {
        Log.d("DEBUG", "called onLocationChanged");
        Log.d("DEBUG", "lat : " + location.getLatitude());
        Log.d("DEBUG", "lon : " + location.getLongitude());
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.d("DEBUG", "called onStatusChanged");
        switch (status) {
            case LocationProvider.AVAILABLE:
                Log.d("DEBUG", "AVAILABLE");
                break;
            case LocationProvider.OUT_OF_SERVICE:
                Log.d("DEBUG", "OUT_OF_SERVICE");
                break;
            case LocationProvider.TEMPORARILY_UNAVAILABLE:
                Log.d("DEBUG", "TEMPORARILY_UNAVAILABLE");
                break;
            default:
                Log.d("DEBUG", "DEFAULT");
                break;
        }
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.d("DEBUG", "called onProviderDisabled");
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.d("DEBUG", "called onProviderEnabled");
    }
}
```

## 実装ポイント
### LocationManager
LocationManagerクラスのインスタンスを生成、プロバイダを設定し位置情報を取得します。
取得開始はLocationManager.requestLocationUpdates、
取得停止はLocationManager.removeUpdatesで行います。

### Criteria
プロバイダの詳細設定を行います。
・criteria.setSpeedRequired(false)
→LocationManagerのスピード算出機能。使用しない。
・criteria.setAltitudeRequired(false)
→高度情報。使用しない。
・criteria.setBearingRequired(false)
→端末が指している方向情報。使用しない。
・criteria.setCostAllowed(true)
→基地局とのデータのやり取りを行い精度を高める。
やり取りによってデータ通信が増えるのでCostAllowedと呼ぶ。
使用する場合はユーザへ同意を求める実装にすればいいかも。

### LocationListener
位置情報を受け取るリスナーを設定する。
位置情報に変更があった場合はコールバックメソッドonLocationChangedが呼び出される。
LocationManager.requestLocationUpdatesで取得開始する際に設定する引数で呼び出す最低間隔を設定できる。
今回はminTimeに60000(ms)、minDistanceに3(m)を設定しています。

## まとめ
Criteriaの詳細設定に関しては今後実際にアプリを作りながらもっと調べてみます。
電力消費量や位置情報の精度など、アプリの目的に合わせて柔軟な設定ができればと思います。

以下のサイトを参照とさせていただきました。
[AndroidでGPS（位置情報取得）](https://qiita.com/yasumodev/items/5f0f030f0ebfcecdff11)
[[Android] GPSで位置情報を取得するアプリを作る](https://akira-watson.com/android/gps.html)
[位置情報を正確にトラッキングする技術 in Android](https://medium.com/location-tracking-tech/%E4%BD%8D%E7%BD%AE%E6%83%85%E5%A0%B1%E3%82%92%E6%AD%A3%E7%A2%BA%E3%81%AB%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E6%8A%80%E8%A1%93-in-android-%E7%AC%AC1%E5%9B%9E-%E3%83%90%E3%83%83%E3%82%AF%E3%82%B0%E3%83%A9%E3%83%B3%E3%83%89%E3%81%A7%E3%81%AE%E4%BD%8D%E7%BD%AE%E6%83%85%E5%A0%B1%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E3%82%92%E5%8F%AF%E8%83%BD%E3%81%AB%E3%81%99%E3%82%8B%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3-6bb36559a977)

ありがとうございました。