안드로이드 개발 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 [녹두장군 - 상상을 현실로]

 

 

 

반응형

댓글()