본문 바로가기
코딩일기/날씨앱 만들기 프로젝트

날씨 앱 만들기 : FusedLocationProvider 사용법 Part 1 (MVVM / Java)

by 욱파이어니어 2021. 9. 25.
728x90
반응형

이전에 이미 위치정보를 LocationManager를 사용해서 위치정보를 받아왔었다.

 

https://wpioneer.tistory.com/190

 

날씨 앱 만들기 : 안드로이드 GPS 정보 받아오기

날씨 앱을 만들기 위해선 필수적으로 필요한 GPS 정보를 받아와 보기로 했다. 일단 GPS 정보를 받아오기 위해선 아래와 같은 과정을 거친다. 1. Manifest파일에 권한 추가하기 2. LocationManager 객체 생

wpioneer.tistory.com

 

하지만 GPS 정보 받아오는 부분을 알아보니 좀 더 배터리 효율이 좋고 정확성이 높은 

FusedLocationProvider 가 있다고 해서 해당 부분에 대해서 공부를 해봤다.

 

 

그럼 일단 FusedLocationProvider에 대해서 알아보자.

 

 

FusedLocationProvider란?

기존에 Android OS에서 제공하는 LocationService에서는 구현해야 할것들이 많았다.

배터리 효율성도 고려해야 했고 GPS가 닿지 않는곳에서는 NETWORK_PROVIDER를 사용해야 했었고

NETWORK_PROVIDER 의 정확하지 않은 위치 정보도 고려하면서 위치정보를 구했어야 했다.

 

하지만 2019 Google I/O 행사에서 이 문제들을 해결해줄 새로운 LocationService를 발표했다.

 

그것이 바로 FusedLocationProvider 이다.

 

FusedLocationProvider 는 건물에서는 구해지지 않는 GPS_PROVIDER 와 어디서든 위치정보를 구할수 있지만 

위치정보가 정확하지 않은 NETWORK_PROVIDER 의 간극을 메꿔주는 위치정보 서비스이다.

GPS_PROVIDER와 NETWORK_PROVDER의 위치 정보 정확성에 관한 그래프
GPS_PRIVDER와 NETWORK_PROVIDER의 장점들만 모아 놓은 FusedLocationProvider

 

이처럼 FusedLocationProvider 는 정확도가 높은 위치정보를 보내주는 위치서비스이다.

 

그렇담 FusedLocationProvider 가 앱에서 어떤 흐름으로 흘러가는지 알아보자.

 

 

 

FusedLocationProvider 의 흐름

 

나는 MVVM 패턴으로 소스를 작성하였기 때문에 MVVM 패턴에서 FusedLocationProvider 가 어떤 흐름으로 

흘러가는지 알려주겠다.

 

 

1. Activity(View) onCreate()에서 ViewModel과 LocationRequest 객체 생성

2. Activity(View)에서 위치정보 권한 체크

3. 위치정보 권한 승인되면 구글 플레이 서비스 위치정보 사용가능한지 확인

4. 2번과 3번 모두 만족하면 위치정보 업데이트 요청

5. 위치정보 업데이트 콜백 메소드 호출

 

위와 같은 흐름으로 앱이 흘러간다.

 

 

그럼 이제 대략적인 흐름을 알았으니 자세히 어떻게 사용하는지에 대해서 알아보자.

 

 

FusedLocationProvider 사용법

 

FusedLocationProvider 을 사용하기 위한 단계를 아래와 같이 만들었다.

 

 

1. build.gradle(프로젝트) 파일에 classpath 추가하기.

2. build.gradle(Module:app) 파일에 dependencies 추가하기

3. manifest에 권한 추가하기.

4. Activity에서 위치정보 권한 확인하기.

5. 권한 확인 요청에 관한 콜백 메소드 만들기

6. 구글 플레이 서비스에서 위치정보 사용하는지 안하는지 체크하기

7. 구글 플레이 서비스 위치정보 사용 콜백 메소드 만들기

8. 위치 정보 업데이트 요청 메소드 만들기

9. 위치정보 업데이트 콜백 메소드 만들기

 

그럼 이제 각각에 대해서 자세히 알아보자.

 

 

1. build.gradle(프로젝트) 파일에 classpath 추가하기.

FusedLocationProvider 는 구글 플레이서비스에서 제공을 해주는것이기떄문에 구글플레이 서비스를 사용을 위해선

아래와 같은 설정이 필요하다.

 

그래서 build.gradle(프로젝트) 파일에 구글 플레이서비스를 사용한다는 classpath를 추가해주면 된다.

        //구글 플레이 서비스를 사용하기 위해서 (FusedLocationProviderClient)
        classpath 'com.google.gms:google-services:4.3.10'

 

2. build.gradle(Module:app) 파일에 dependencies 추가하기

이것도 마찬가지로 위의 파일명에 dependencies를 추가해주면 된다.

    //구글 플레이서비스를 사용하기 위해서 사용하는 라이브러리
    implementation 'com.google.android.gms:play-services-location:17.1.0'
    implementation 'com.github.niqo01.rxplayservices:rx-play-services-location:0.4.0' //이게 위치 서비스를 사용하기 위해 필요한 라이브러리임

 

 

3. manifest에 권한 추가하기.

위치정보를 사용하려면 manifest 파일에 위치정보 권한을 추가해줘야 한다.

 

    <!-- 현재 위치값 받기 위해 필요한 permission -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

 

4. Activity에서 위치정보 권한 확인하기.

그럼 이제 위치정보를 모두 사용하였으니 위치정보를 사용하려는 Activity에 위치정보 권한이 주어졌는지

확인을 해야한다.  나는 해당 부분을 아래와 같이 만들었다.

 

 

    public void checkLocationPermission(){
        //위치정보 권한 허용되어 있는지 아닌지를 확인하는 부분
        if(ActivityCompat.checkSelfPermission(SplashActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            //위치정보 권한 허용되어 있지 않다면 실행하는 코드, 여기서 request에 대한 응답이 나오면 아래에 있는 onRequestPermissionsResult() 함수를 콜백하게 됨
            ActivityCompat.requestPermissions(SplashActivity.this,new String[]{ Manifest.permission.ACCESS_FINE_LOCATION},200); //위치정보 권한을 요청한다.
        }else {
            //위치정보 권한이 허용되어 있을때 실행하는 코드
            Log.d(TAG, "위치정보 허용됨");
            //구글 플레이 서비스 위치정보 권한 승인된건지 확인해야함
            checkLocationSetting();
        }
    }

 

5. 권한 확인 요청에 관한 콜백 메소드 만들기

권한 확인 요청하는 창을 만들었으니 그것에 대한 결과에 대한 콜백 메소드를 만들어야 한다.

나는 권한 확인을 했으면 requestCode를 200을 주게 했다.

그리고 그게 아니라면 그냥 지정한 위치의 날씨정보를 받게 했다.

 

    //권한요청되었을때 콜백되어지는 함수
    @Override
    public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if(requestCode == 200){ //permissionCode가 200이고
            if(grantResults[0] == 0){ // 그중 가장 첫번째 result가 0 즉 승인된경우 진입
                Toast.makeText(getApplicationContext(),"위치정보 권한 승인됨",Toast.LENGTH_SHORT).show(); //위치정보 승인됐다고 알리고
                //위치정보 권한을 받았다면 진입
                if(ActivityCompat.checkSelfPermission(SplashActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){

                    //구글 플레이 서비스 위치정보 권한 승인된건지 확인해야함
                    checkLocationSetting();
                }
            }else{ //위치정보를 허가받지 못했을경우 진입
                Toast.makeText(getApplicationContext(),"위치정보를 승인하지 않으면 현재위치 기반으로 \n날씨정보를 알려드릴수 없습니다.",Toast.LENGTH_LONG).show();
                Log.i(TAG,"위치정보를 허가 안해줌");

                //허가를 받지 못했을때의 결과를 받았을때 ViewModel의 메소드를 호출하면됨
                mavm.defaultLocation();// I call ViewModel at this part - This works
                obeserveAPI();
            }
        }
    }

 

6. 구글플레이 서비스에서 위치정보 사용하는지 안하는지 체크하기

그럼 이제 위치 정보 사용에 대한 권한을 확인 했으니 구글 플레이 서비스에서 위치정보를 사용할수 있게 설정을 해놨는지 안해놨는지 체크하는 부분이 필요하다.

 

해당 부분의 소스는 아래와 같다.

    public void checkLocationSetting() {
        Log.d(TAG,"LocationReqeust is in setting");
        lr.setPriority(DEFAULT_LOCATION_REQUEST_PRIORITY);
        lr.setInterval(DEFAULT_LOCATION_REQUEST_INTERVAL);
        lr.setFastestInterval(DEFAULT_LOCATION_REQUEST_FAST_INTERVAL);

        SettingsClient settingsClient = LocationServices.getSettingsClient(this);
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(lr).setAlwaysShow(true);
        settingsClient.checkLocationSettings(builder.build())
                .addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() { //구글 플레이 위치서비스를 사용할수 있을떄 진입
                    @Override
                    public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
                        //구글플레이 위치 서비스 사용할수 있게 됐을때 진입
                        mavm.requestUpdate(lr);
                        observeGps();
                    }
                })
                .addOnFailureListener(SplashActivity.this, new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {//구글 플레이 위치서비스를 사용할수 없을떄 진입
                        int statusCode = ((ApiException) e).getStatusCode();
                        switch (statusCode){
                            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: //충분히 설정 변경하는것으로 변경이 가능할떄
                                ResolvableApiException rae = (ResolvableApiException) e;
                                try {
                                    rae.startResolutionForResult(SplashActivity.this, 200);
                                } catch (IntentSender.SendIntentException ex) {
                                    Log.w(TAG,"LocationService approval canceled");
                                }
                                break;
                            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: //GPS가 아예 없거나 연결자체가 불가능하여 사용을 물리적으로 사용하지 못할때
                                Log.w(TAG,"No way to change setting");
                                Toast.makeText(getApplicationContext(),"GPS 사용을 하지 못하여 위치정보를 받아오지 못하고 있습니다.\n GPS 연결을 해주세요.",
                                        Toast.LENGTH_SHORT).show();
                                mavm.defaultLocation();
                                obeserveAPI();
                                break;
                        }
                    }
                });
    }

 

위 소스를 설명 하자면 

 

        lr.setPriority(DEFAULT_LOCATION_REQUEST_PRIORITY);
        lr.setInterval(DEFAULT_LOCATION_REQUEST_INTERVAL);
        lr.setFastestInterval(DEFAULT_LOCATION_REQUEST_FAST_INTERVAL);

일단 lr은 내가 해당 Activity onCreate()에서 만든 locationRequest 객체이다.

lr = LocationRequest.create();

그리고 함수들은 아래와 같은것들을 설정하는 부분이다.

 

setPriority()

- 요청의 우선순위를 설정하여 Google Play 서비스 위치 서비스에 사용할 위치 소스에 관한 강력한 힌트를 제공한다.

따라서 이 메소드에 들어가는 값은 아래와 같다.

 

나는 날씨 정보를 받아오기 위해 지역정보가 필요하기 때문에 아래의 값으로 설정하였다.

 

private final int DEFAULT_LOCATION_REQUEST_PRIORITY = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;

 

setInterval()

 - 위치 업데이트 수신 간격을 밀리초 단위로 설정하는 메소드이다. 따라서 나는 아래와 같이 설정 하였다.

private final long DEFAULT_LOCATION_REQUEST_INTERVAL = 2000L;

 

setFastestInterval()

 -  앱이 위치 업데이트를 처리할 수 있는 가장 빠른 간격을 밀리초 단위로 설정하는 메소드이다.

따라서 나는 아래와 같은 값으로 설정하였다.

private final long DEFAULT_LOCATION_REQUEST_FAST_INTERVAL = 10000L;

(LcationReust 설정에 관해 더 알고 싶은 사람들은 아래 링크를 통해 확인해보면 된다.)

https://developer.android.com/training/location/change-location-settings

 

위치 설정 변경  |  Android 개발자  |  Android Developers

위치 설정 변경 앱에서 위치를 요청하거나 권한 업데이트를 수신해야 한다면 기기는 GPS 또는 Wi-Fi 검색과 같은 적절한 시스템 설정을 사용해야 합니다. 앱은 서비스(예: 기기의 GPS)를 직접 사용

developer.android.com

 

이렇게 LocationRequest에 대한 설정을 완료하였고 그 밑에 있는 소스들은 

이제 구글 플레이 서비스에서 위치정보를 사용하는지 안하는지 확인을 하기 위한 소스이다.

다이얼로그처럼 창을 띄우는것이기때문에 해당 부분은 ViewModel에서 하지 않고 Activity(View)에서 만들었다.

 

만약 구글 플레이 위치 서비스 사용이 가능하다면 ViewModel을 통해 위치정보를 받게 했고

아니라면 구글 플레이 위치 서비스 사용 요청 창을 띄우게 했다.

 

(자세한건 소스의 주석을 참고하면 된다.)

 

7번 과정부터는 Part2에서 설명을 하겠다.

https://wpioneer.tistory.com/194

 

날씨 앱 만들기 : FusedLocationProvider 사용법 Part 2 (MVVM / Java)

이전에는 FusedLocationProvider 사용법의 6단계까지 알아봤다. 1. build.gradle(프로젝트) 파일에 classpath 추가하기. 2. build.gradle(Module:app) 파일에 dependencies 추가하기 3. manifest에 권한 추가하기...

wpioneer.tistory.com

 

반응형