Using the Location Manager
안드로이드에서의 LocationManager를 통한 위치획득 방식
아래글은 FusedLocationProvider가 나오기전 Making Your App Location Aware이라는 제목으로 구글 개발자 페이지에 LocationManager를 통해위치를 획득하는 방법에 대한 가이드글을 번역한 것입니다.
위치 기능을 사용하려고 하는데 선행작업은 뭐가 있을까?
1. 메니페스트에서 권한 설정을 한다.
GPS권한은 ACCESS_FINE_LOCATION과 ACCESS_COARSE_LOCATION으로 나뉘어 지는데
전자의 경우 GPS만을 통하여 값을 가져오고 후자는 네트워크와 GPS를 모두 이용할 수 있다.
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
<uses-permission android:name=”android.permission.INTERNET” />
2. LocationManager를 선언한다.
이는 생명주기상 onCreate()와 onStart()에 선언해야한다.
LocationManager locationManager =
(LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
3. 사용할 프로바이더를 설정한다.
일반적으로 프로바이더의 종류는 3가지로 네트워크, GPS, 패시브가 있다.
LocationProvider provider =
locationManager.getProvider(LocationManager.GPS_PROVIDER);
4. 사용할 각각의 프로바이더의 상태를 체크한 후 가장 상태가 좋은 프로바이더를 선정한다.
// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setCostAllowed(false);
…
String providerName = locManager.getBestProvider(criteria, true);// If no suitable provider is found, null is returned.
if (providerName != null) {
…
}
5. 프로바이더의 상태를 인증한다.
해당 프로바이더를 사용 못하는 상태면 네트워크 옵션으로 보낼수 있도록 메서드를 콜한다.
@Override
protected void onStart() {
super.onStart(); // This verification should be done during onStart() because the system calls
// this method when the user returns to the activity, which ensures the desired
// location provider is enabled each time the activity resumes from the stopped state.
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (!gpsEnabled) {
// Build an alert dialog here that requests that the user enable
// the location services, then when the user clicks the “OK” button,
// call enableLocationSettings()
}
}private void enableLocationSettings() {
Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
}
6. 프로바이더를 실행한다.
// gps와 network 프로바이더를 시행시킨다.
gpsLocation = requestUpdatesFromProvider(LocationManager.GPS_PROVIDER, R.string.not_support_gps);
networkLocation = requestUpdatesFromProvider(LocationManager.NETWORK_PROVIDER, R.string.not_support_network);
// If both providers return last known locations, compare the two and use the better
// one to update the UI. If only one provider returns a location, use it.
if (gpsLocation != null && networkLocation != null) {
// 어떤 프로바이더를 사용할지 결정하는 메서드
updateUILocation(getBetterLocation(gpsLocation, networkLocation));
} else if (gpsLocation != null) {
updateUILocation(gpsLocation);
} else if (networkLocation != null) {
updateUILocation(networkLocation);
} /**
* Method to register location updates with a desired location provider. If the requested
* provider is not available on the device, the app displays a Toast with a message referenced
* by a resource id.
*
* @param provider Name of the requested provider.
* @param errorResId Resource id for the string message to be displayed if the provider does
* not exist on the device.
* @return A previously returned {@link android.location.Location} from the requested provider,
* if exists.
*/
// 프로바이더의 속성을 설정한다.
private Location requestUpdatesFromProvider(final String provider, final int errorResId) {
Location location = null;
if (mLocationManager.isProviderEnabled(provider)) {
mLocationManager.requestLocationUpdates(provider, TEN_SECONDS, TEN_METERS, listener);
location = mLocationManager.getLastKnownLocation(provider);
} else {
Toast.makeText(this, errorResId, Toast.LENGTH_LONG).show();
}
return location;
}
7. 위치정보 결과를 받는다.
private final LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// A new location update is received. Do something useful with it. Update the UI with
// the location update.
updateUILocation(location);
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
8. 위경도를 주소로 보고싶어요!
먼저 아래의 조건이 참일 경우만 지오코딩을 사용하도록한다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && Geocoder.isPresent()){
// AsyncTask encapsulating the reverse-geocoding API. Since the geocoder API is blocked,
// we do not want to invoke it from the UI thread.
private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
Context mContext;
public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
@Override
protected Void doInBackground(Location… params) {
Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
Location loc = params[0];
List<Address> addresses = null;
try {
addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
} catch (IOException e) {
e.printStackTrace();
// Update address field with the exception.
Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
}
if (addresses != null && addresses.size() > 0) {
Address address = addresses.get(0);
// Format the first line of address (if available), city, and country name.
String addressText = String.format(“%s, %s, %s”,
address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : “”,
address.getLocality(),
address.getCountryName());
// Update address field on UI.
Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();
}
return null;
}
}
}
9. LocationManager 해제
생명주기를 생각해보면 LocationManger는 onStop()에 선언하는게 맞다.
알수없는 오류로 인한 종료는 onDestroy()를 타지 않는 경우도 있으며
onPause()의 경우는 백그라운드 상태진입시에도 종료되어질 수 있기때문이다.
protected void onStop() {
super.onStop();
mLocationManager.removeUpdates(listener);
}
10. LocationManager.PASSIVE_PROVIDER의 사용시점은 언제가 좋을까?
단순히 생각하면 아래와 같이 정의할수도 있지만 이것은 이렇게 쉽게만은 생각할 것이 아니다.if (gpsLocation != null && networkLocation != null) {
// 어떤 프로바이더를 사용할지 결정하는 메서드
updateUILocation(getBetterLocation(gpsLocation, networkLocation));
} else if (gpsLocation != null) {
updateUILocation(gpsLocation);
} else if (networkLocation != null) {
updateUILocation(networkLocation);
} else{
if(pasiveLocation != null){
updateUILocation(pasiveLocation);
}
}
왜냐하면 gps와 network로 위치정보를 획득못했다고 바로 패시브의 위치정보값을 사용한다면 사용자가 해당서비스의 위치정보의 신뢰도가 떨어질 수 있기 때문이다.
시점은 이정도가 좋지 않을까 한다.
- 최초구동 시 패시브의 위치값이 있다면 그 정보를 노출시킨다.(프로그레스는 계속돌고 있다.) 이후 GPS와 Network로 위치획득을 하면 해당위치로 좌표를 이동시킨다.
- 최초구동 시 패시브, GPS, Network모든 프로바이더를 getBetterLocation() 메서드에 넣고 돌린다. (프로그레스는 멈춰있다.)
- GPS, Network모든 프로바이더 위치획득 실패 시 리트라이한다. 이때 패시브, GPS, Network모든 프로바이더를 getBetterLocation() 메서드에 넣고 돌린다.
그러나 해당조건이 없을경우 알럿으로 현재 위치를 잡을수 없다는 메시지와 함께 패시브로 잡았던 위치정보를 사용할지 묻는다.