안드로이드 개발 Android GPS 정보 알아오기
안드로이드에서 GPS 정보를 가져온 후 자기 위치를 MAP 에 표시하거나 다른 사람에게 위치 정보를 알릴 수도 있습니다. 앱에서 GPS 정보는 아주 다양하게 쓰이기 때문에 별도의 클래스를 만들어서 사용하는 것이 좋겠죠. 스마트폰에 GPS 설정이 되어 있지 않을 때 팝업창을 띄워서 설정창으로 이동할 수 있는 소스도 추가가 되어 있습니다. 그리고 최근에 수정한 내용 중 API 23 버전부터 퍼미션을 추가해야 되는 부분도 참고 하시기 바랍니다.
▼ 먼저 AndroidManifest.xml 에 GPS 정보를 가져올 수 있도록 환경을 셋팅해야 합니다.
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> |
▼ GPS 정보를 알아오는 클래스에 대해서 알아봅니다. 클래스명은 GpsInfo.java 입니다. 추상클래스 Service 와 위치정보를 받아오기 위한 Listener 인터페이스 클래스 LocationListener 상속합니다. 그리고 GPS나 네트워크 사용유무, 얼마에 한번씩 데이터를 업데이트 할 것인지에 대한 변수들을 만듭니다. 실질적으로 위치정보를 알아오는 클래스는 LocationManager 입니다. 이것도 변수지정해 놓습니다.
private final Context mContext;
// 현재 GPS 사용유무 boolean isGPSEnabled = false;
// 네트워크 사용유무 boolean isNetworkEnabled = false;
// GPS 상태값 boolean isGetLocation = false;
Location location; double lat; // 위도 double lon; // 경도
// 최소 GPS 정보 업데이트 거리 10미터 private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;
// 최소 GPS 정보 업데이트 시간 밀리세컨이므로 1분 private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1;
protected LocationManager locationManager; |
▼ 다음은 GPS 위치값을 가져오기 위한 함수입니다. LocationManager 을 사용하였으며 requestLocationUpdates() 함수로 현재 정보를 업데이트 하고 getLastKnownLocation()
함수로 위치값을 가져옵니다.
this.isGetLocation = true; // 네트워크 정보로 부터 위치값 가져오기 if (isNetworkEnabled) { locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); if (location != null) { // 위도 경도 저장 lat = location.getLatitude(); lon = location.getLongitude(); } } }
if (isGPSEnabled) { if (location == null) { locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this); if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location != null) { lat = location.getLatitude(); lon = location.getLongitude(); } } } } |
▼ 위 함수를 실행전에 GPS 상태정보와 네트워크 정보를 가져와 제대로 환경이 되어있지 않다면 정보를 가져오는 부분을 스킵하겠죠.
// GPS 정보 가져오기 isGPSEnabled = locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER);
// 현재 네트워크 상태 값 알아오기 isNetworkEnabled = locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER); |
▼ 이렇게 환경정보를 확인했는데 제대로 되어있지 않다면 GPS 정보가 제대로 설정 되어있는지 확인을 위한 alert 창을 띄우게 되고, 설정창으로 바로 가서 다시 GPS 를 사용할수 있도록 셋팅페이지로 가게 하는 기능입니다.
public void showSettingsAlert(){ AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
alertDialog.setTitle("GPS 사용유무셋팅"); alertDialog.setMessage("GPS 셋팅이 되지 않았을수도 있습니다. \n 설정창으로 가시겠습니까?"); // OK 를 누르게 되면 설정창으로 이동합니다. alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int which) { Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); mContext.startActivity(intent); } }); // Cancle 하면 종료 합니다. alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } });
alertDialog.show(); } |
▼ 안드로이드가 업그레이드 되면서 보안을 좀더 강화했는데 AndroidManifest.xml 에만 퍼미션을 넣어서는 안됩니다. 소스에서 사용자에게 퍼미션을 요청하는 로직을 넣어야 합니다. 그렇지 않으면 다음과 같은 에러가 발생합니다.
▼ 일단 GpsInfo.class 에 퍼미션 체크 로직을 넣어야 에러가 사라집니다.
if ( Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null; } |
▼ 아래는 GPS 정보를 가져오기 위한 GpsInfo.java 의 전체 소스입니다.
import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Service; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.provider.Settings; import android.support.v4.content.ContextCompat;
public class GpsInfo extends Service implements LocationListener {
private final Context mContext;
// 현재 GPS 사용유무 boolean isGPSEnabled = false;
// 네트워크 사용유무 boolean isNetworkEnabled = false;
// GPS 상태값 boolean isGetLocation = false;
Location location; double lat; // 위도 double lon; // 경도
// 최소 GPS 정보 업데이트 거리 10미터 private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;
// 최소 GPS 정보 업데이트 시간 밀리세컨이므로 1분 private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1;
protected LocationManager locationManager;
public GpsInfo(Context context) { this.mContext = context; getLocation(); }
@TargetApi(23) public Location getLocation() { if ( Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null; }
try { locationManager = (LocationManager) mContext .getSystemService(LOCATION_SERVICE);
// GPS 정보 가져오기 isGPSEnabled = locationManager .isProviderEnabled(LocationManager.GPS_PROVIDER);
// 현재 네트워크 상태 값 알아오기 isNetworkEnabled = locationManager .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!isGPSEnabled && !isNetworkEnabled) { // GPS 와 네트워크사용이 가능하지 않을때 소스 구현 } else { this.isGetLocation = true; // 네트워크 정보로 부터 위치값 가져오기 if (isNetworkEnabled) { locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); if (location != null) { // 위도 경도 저장 lat = location.getLatitude(); lon = location.getLongitude(); } } }
if (isGPSEnabled) { if (location == null) { locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this); if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location != null) { lat = location.getLatitude(); lon = location.getLongitude(); } } } } }
} catch (Exception e) { e.printStackTrace(); } return location; }
/** * GPS 종료 * */ public void stopUsingGPS(){ if(locationManager != null){ locationManager.removeUpdates(GpsInfo.this); } }
/** * 위도값을 가져옵니다. * */ public double getLatitude(){ if(location != null){ lat = location.getLatitude(); } return lat; }
/** * 경도값을 가져옵니다. * */ public double getLongitude(){ if(location != null){ lon = location.getLongitude(); } return lon; }
/** * GPS 나 wife 정보가 켜져있는지 확인합니다. * */ public boolean isGetLocation() { return this.isGetLocation; }
/** * GPS 정보를 가져오지 못했을때 * 설정값으로 갈지 물어보는 alert 창 * */ public void showSettingsAlert(){ AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
alertDialog.setTitle("GPS 사용유무셋팅"); alertDialog.setMessage("GPS 셋팅이 되지 않았을수도 있습니다. \n 설정창으로 가시겠습니까?");
// OK 를 누르게 되면 설정창으로 이동합니다. alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int which) { Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); mContext.startActivity(intent); } }); // Cancle 하면 종료 합니다. alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } });
alertDialog.show(); }
@Override public IBinder onBind(Intent arg0) { return null; }
public void onLocationChanged(Location location) { // TODO Auto-generated method stub
}
public void onStatusChanged(String provider, int status, Bundle extras) { // TODO Auto-generated method stub
}
public void onProviderEnabled(String provider) { // TODO Auto-generated method stub
}
public void onProviderDisabled(String provider) { // TODO Auto-generated method stub
} } |
두 번째는 GPS 정보를 가져오는 Activity 에서의 퍼미션 체크 로직을 삽입해야 합니다. 기존에 없던 로직으로 callPermission() 함수가 그것입니다. 함수로 퍼미션 요청을 사용자에게 하면 그림과 같은 요청 팝업창이 뜹니다. 팝업창에서 DENY/ALLOW 하나를 선택하면 Override 한 onRequestPermissionsResult() 로 결과 값을 전달합니다.
▼ 이번에 추가한 callPermission() 와 onRequestPermissionsResult() 소스는 다음과 같습니다.
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PERMISSIONS_ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isAccessFineLocation = true;
} else if (requestCode == PERMISSIONS_ACCESS_COARSE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED){
isAccessCoarseLocation = true; }
if (isAccessFineLocation && isAccessCoarseLocation) { isPermission = true; } }
// 전화번호 권한 요청 private void callPermission() { // Check the SDK version and whether the permission is already granted or not. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions( new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_ACCESS_FINE_LOCATION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
requestPermissions( new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS_ACCESS_COARSE_LOCATION); } else { isPermission = true; } } |
▼ GPS 정보를 사용하는 메인 Activity 전체 소스입니다. Toast 를 이용해 화면에 위치 정보를 띄우고 TextView 에 위도와 경도를 알려줍니다.
import android.Manifest; import android.app.Activity; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast;
/** GPS 샘플 */ public class GpsActivity extends Activity {
private Button btnShowLocation; private TextView txtLat; private TextView txtLon; private final int PERMISSIONS_ACCESS_FINE_LOCATION = 1000; private final int PERMISSIONS_ACCESS_COARSE_LOCATION = 1001; private boolean isAccessFineLocation = false; private boolean isAccessCoarseLocation = false; private boolean isPermission = false;
// GPSTracker class private GpsInfo gps;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gps);
btnShowLocation = (Button) findViewById(R.id.btn_start); txtLat = (TextView) findViewById(R.id.tv_latitude); txtLon = (TextView) findViewById(R.id.tv_longitude);
// GPS 정보를 보여주기 위한 이벤트 클래스 등록 btnShowLocation.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { // 권한 요청을 해야 함 if (!isPermission) { callPermission(); return; }
gps = new GpsInfo(GpsActivity.this); // GPS 사용유무 가져오기 if (gps.isGetLocation()) {
double latitude = gps.getLatitude(); double longitude = gps.getLongitude();
txtLat.setText(String.valueOf(latitude)); txtLon.setText(String.valueOf(longitude));
Toast.makeText( getApplicationContext(), "당신의 위치 - \n위도: " + latitude + "\n경도: " + longitude, Toast.LENGTH_LONG).show(); } else { // GPS 를 사용할수 없으므로 gps.showSettingsAlert(); } } });
callPermission(); // 권한 요청을 해야 함 }
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PERMISSIONS_ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isAccessFineLocation = true;
} else if (requestCode == PERMISSIONS_ACCESS_COARSE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED){
isAccessCoarseLocation = true; }
if (isAccessFineLocation && isAccessCoarseLocation) { isPermission = true; } }
// 전화번호 권한 요청 private void callPermission() { // Check the SDK version and whether the permission is already granted or not. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions( new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_ACCESS_FINE_LOCATION);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
requestPermissions( new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS_ACCESS_COARSE_LOCATION); } else { isPermission = true; } } } |
API 23 버전부터 아래 에러가 난다면 두 가지 permission 을 추가해야 합니다. ACCESS_FINE_LOCATION 은 이미 있고 ACCESS_COARSE_LOCATION 만 추가하면 되겠죠.
Call requires permission which may be rejected by user. Code should explicitly check to see if permission is available.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> |
출처: https://mainia.tistory.com/1153 [녹두장군 - 상상을 현실로]
'프로그래밍 > Android (Java)' 카테고리의 다른 글
구글 아이콘 무료 사용 가능 (0) | 2019.06.24 |
---|---|
naverMap 을 전역 변수로 활용하기 (0) | 2019.06.07 |
Android 액티비티를 팝업(Popup)으로 띄우기, 데이터 주고받기 (0) | 2019.06.05 |
xml 파일로 말풍선 만들기 (0) | 2019.05.27 |
DialogFragment로 Custom Dialog 만들기 (0) | 2019.05.17 |