안드로이드 생존코딩 (오준석) 챕터10 학습 정리
10장. 지도와 GPS
<요약>
ㅡ하나의 액티비티로 구성한다.
ㄴ프로젝트 생성할 때 Google Maps Activity를 선택: 지도 표시하는 기본 템플릿
ㅡ현재 위치 정보
ㄴ권한 필요
ㅡ액티비티 생명주기에 따라 위치 업데이트 리스너 등록/해제 필요
ㅡ위도, 경도 정보를 가지고 있는 Location 객체
ㅡ주기적으로 현재 위치를 갱신하며 선 그리기
ㄴ구글지도가 메서드 제공
<사용 라이브러리>
Anko: 인텐트, 로그 등의 구현에 도움 되는 라이브러리
play-services-maps: 구글 지도 라이브러리
play-services-location: 위치 정보 라이브러리
라이브러리 의존성 추가 (구글지도)
위에 거는 위치 정보, 아래 거는 구글 지도.
구글 지도 의존성은 Google Maps Activity를 생성할 때 자동으로 추가된다.
<내용 정리>
ㅡ구글 지도 사용하기
기본 액티비티로 MapsActivity를 선택하는 것이 가장 간단하다. 이 챕터에서는 기본 액티비티에서 구글 지도를 사용한다.
ㅡ구글 지도 API키 발급받기
API키: 어떤 기능을 사용하는 열쇠와 같다. 이 열쇠가 있는 사람만 해당 기능 사용 가능. 중요한 열쇠이므로 분실하거나 노출되면 안된다. 보통 API 키마다 사용횟수 등 제한 있다.
+ 구글 지도 사용은 모바일 기기에서 무제한 무료였지만, 앞으로 유료화된다고 한다.
google_map_api.xml에 있는 링크에 들어가 API키를 발급받고 xml에 키를 넣었다. 그리고 기기에서 실행했다. 지도가 뜬다.
ㅡMapsActivity 기본 코드 분석 (작성 안한 상태 그대로)
기본 코드 분석
onCreate()에서 레이아웃 리소스를 불러온다.
또한 SupportMapFragment를 가져와서 지도가 준비되면 알림을 받는다.
getMapAsync()를 통해 알림을 받는 것.
onMapReady()
즉 지도가 준비되면 GoogleMap 객체를 인자로 얻는다.
또한 위도와 경도로 시드니의 위치를 정하고, addMarker하고, moveCamera한다.
ㅡ프래그먼트는 어떻게 되어 있는 상태인가?
이런 기본 프래그먼트가 화면을 꽉 채우고 있다. 구글 지도가 내장된 프래그먼트로, play-services-maps 라이브러리에서 제공된다.
ㅡ위치 권한 추가 - 매니페스트
매니페스트에 이렇게 위치 권한이 추가되어있다.
위치 권한은 위험 권한이므로 사용 시 권한을 요청해야 한다.
ㅡ위치 정보 요청 - onResume()에서
위치 정보를 요청하는 행동은 액티비티가 화면에 보일 때만 수행하는 것이 좋겠다. 그래서 일반적으로 onResume() 콜백에서 위치 정보 요청을 하고, onPause()에서 요청을 삭제한다.
위치 권한 요청 코드를 작성해보자. onResume()에서.
onResume() 콜백에서는 위치 권한 허용 여부를 체크하는 permissionCheck() 메서드를 호출하고 있다.
여기서 잠깐 permissionCheck() 메서드를 확인해보자.
인자로 함수 두 개를 받는다. 인자로 들어가는 함수는 인자가 없고() 반환값도 없다 ->Unit
권한이 없는지 본다. 권한이 없으면 '거부했었나?' 아니면 '거부기록이없는가'를 판단한다.
권한 거부를 했다면 cancel()를 호출하고, 권한을 허락하면 ok()를 호출한다.
애초에 권한이 있었다면 ok()를 호출한다.
권한을 허용해 ok()를 호출하면, addLocationListener()를 호출해 드디어 위치를 요청한다.
먼저 '위치 권한 허용 여부'를 체크하는 permissionCheck()를 보자.
코틀린의 '고차 함수' 기능을 사용해서 이 권한 요청 메서드를 작성한다.
권한 요청하는 메서드 permissionCheck()를 작성하여 권한을 요청할 때마다 이를 부르게 할 것이다.
권한 요청 메서드는 다음과 같이 작성한다.
이 때, 만약 cancel()이 호출되었다면 어떤 동작을 해야겠는가? 위치 정보가 필요하다고 다시 말해줘야 할 것이다. 그래야 어플을 이용할 수 있으니까..
아까 말했다. 위치정보 요청은 onResume() 콜백에서 한다고 했다. 이 onResume()에서 위치정보를 요청하기 위해서 우선적으로 "위치정보가 허용되었는가?" 를 판단해야 한다. 그래서 permissionCheck()를 부른 것이다. 아래와 같이 onResume()에서 permissionCheck()를 부르고 있다.
permissionCheck() 메서드 실행 중 cancel()이 호출되면 '위치정보가 필요한 이유'에 대한 다이얼로그를 표시해줘야 한다. 반면 ok()면 현재 위지를 주기적으로 요청하는 메서드를 부르면 된다.
자 그럼 permissionCheck()을 실행하고 cancel()이 호출되었을 때 표시되는 다이얼로그 부분을 보자.
바로 이거다. 한번 거절했었기 때문에 다시 다이얼로그를 통해 권한이 필요한 이유를 설명하고, yes no 선택지를 다시 주게 된 것이다.
위 내용을 알았다면 이제 onResume()에서 작성한 코드를 보고 아하~ 할 수 있다.
아직 onResume()에서 안 본거 하나 남았다.
addLocationListener()
permissionCheck()에서 ok()를 불렀을 때, addLocationListener()를 부른다고 했다.
권한을 받고 나서 호출된 이 메서드는 위치를 요청한다.
잠깐!
아직 빨간줄이 뜨는 이유: 위치 정보를 사용하는 코드가 존재하는데, 위치 권한 요청 코드를 작성하지 않았기 때문이다. + 안드로이드 스튜디오에서는 9장 전자액자에서 한 것처럼, 권한이 필요한 코드의 주변에 직접 작성한 권한 요청 코드만 인식한다. 따라서 권한 요청 부분을 제대로 작성했는데 빨간 글씨로 뜨면, 제안된 메뉴에서 권한 요청 에러를 표시하지 않도록 설정 해주면 된다. (별도의 메서드 안에 권한 요청 코드를 작성하면 이럴 수 있다)
아래는 @suppressLint("MissiongPermission")을 추가해주어 에러를 표시하지 않도록 한 모습이다.
그럼 addLocationListener() 메서드에는 어떤 내용이 있을까?
requestLocationUpdates() 메서드는 위치 정보를 요청한다.
이 메서드는 FusedLocationProviderClient 클래스에 있다.
구성은 이렇다.
requestLocationUpdates(locationRequest: LocationRequest, locationCallback: LocationCallback, looper: Looper)
인자 순서대로 '위치 요청 객체', '위치가 갱신되면 호출되는 콜백', '특정 루퍼 스레드 지정(특별한 경우 아니면 null)'이다.
이 인자들에 대한 구체적인 내용은 다음 접은글에서 다루겠다.
ㅡaddLocationListener()
인자1, 인자2에 대한 이야기를 하겠다.
일단, 인자로 들어가는 객체들을 초기화하는 메서드이다.
초기화하는 메서드에서 인자1인 LocationRequest 객체를 생성하고, 인자2인 LocationCallback 객체를 생성한다. 순서대로 보자.
ㅡ인자1
LocationRequest 객체
위치 정보를 요청하는 시간 주기를 설정하는 객체.
바로 위 사진을 보자. locationInit() 메서드를 통해서 객체가 생성되며, 위치정보요청에 대한 시간 주기를 설정하고 있다.
ㅇpriority: 정확도를 나타낸다. 저기 있는 상수는 정확도를 나타내는 상수 중 하나.
ㅇinterval: 위치 갱신시 필요한 시간 밀리초 단위로
즉, 저기 작성된 요청은 '가장 정확한 위치 요구' + '10초마다 위치 정보 갱신'
그 사이에 다른 앱에서 위치를 갱신했다면 5초마다 확인해 그 값을 활용하여 배터리를 절약.
ㅡ인자2
LocationCallback 객체
다음과 같이 내부 클래스를 작성하여 객체를 구현한다. lastLocation 프로퍼티로 최근 현재 위치에 대한 Location 객체를 얻을 수 있다. 이 객체는 위도, 경도 정보를 가진다.
잘 보면 내부적으로 메서드가 LocationResult 객체를 반환한다.
그리고 이 때, lastLocation 프로퍼티로 Location 객체를 얻는 것이다.
Location 객체가 null이 아닐때, 위도와 경도 위치로 카메라를 이동한다.
null: GPS가 꺼져 있거나, 위치 정보 얻기가 불가능할 때
ㅡ사용자의 권한 요청 선택에 대한 처리 - onRequestPermissionsResult()
아래 두 사진을 보자.
권한을 요청했을 때마다 공통적으로 작성된 코드가 있다.
가만 보면, permissionCheck()로 권한이 있는지 없는지 확인할 때, 권한이 있는 상태인 경우에 ok()를 부르고, 권한을 거부했던 적이 있는 상태인 경우 cancel()을 부른다. 즉, 허용이든 거절이든 이전에 물어본 적이 있다면 permissionCheck()의 결과인 ok()나 cancel()에 따라 권한요청이냐, 다이얼로그를 통한 이유 어필이냐를 정한다.
on resume() 콜백에서 permissionCheck()를 실행한 후 ok()일 경우 바로 addLocationListener()를 부르고, cancel일 경우 결국 다이얼로그에서 이유를 말한 후 권한을 요청한다.
'권한을 요청'할 때, 공통적인 코드는 다음 코드이다.
그럼 이렇게 권한이 요청되었을 경우 어떻게 처리할 것인가? addLocationListener()를 불러야겠지!
또, 거부되었을 때는 어떻게 처리할 것인가? toast를 통해 거부되었다고 말한다.
이러한 처리는 onRequestPermissionsResult()를 오버라이드하여 다음과 같이 작성하더라.
+ 사용자가 권한을 요청하면, 시스템이 onRequestPermissionsResult() 메서드를 호출하는 것이다. 그렇게 사용자의 응답을 전달한다. when을 통해 특정 상수값으로 사용자의 권한에 대한 응답에 어떤 동작을 할 지를 정의한다.
requestCode가 REQUEST_ACCESS_FINE_LOCATION이면 결국 addLocationListener()로 가고, 아니면 단지 토스트메세지를 나타낸다.
+ grantResult 배열에는 요청한 권한들의 결과가 전달된다. 하나의 권한만 요청하고 있으므로 0번 인덱스값을 확인하면 된다.
+권한이 승인되면 PERMISSION_GRANTED를, 거부되면 PERMISSION_DENIED를 반환하게 된다.
ㅡ로그 사용 - 위치 정보가 갱신되었는지 확인하기 위해
로그를 추가한 모습이다.
로그캣을 보면 위도와 경도가 갱신되며 표시된다.
ㅡ이동 자취를 선으로 그리기
구글 지도는 이동 자취를 그리는 다양한 메서드를 제공한다.
그 중에서 "addPolyLine() : 선의 집합으로 지도에 경로와 노선 표시"를 사용한다.
ㅡ이동 경로 그리기
먼저 PolylineOptions() 객체를 생성한다.
그리고 위치 정보가 갱신되면 좌표를 PolylineOptions()객체에 추가한다.
ㅡ화면 유지하기
화면 방향을 고정하고, 화면 자동꺼짐 방지를 하자.
<감명깊었던 것>
Location 객체와, 구글 지도를 이용하기 위한 API키, 그리고 구글 지도 서비스를 지원받아 내가 이용해볼 수 있다는 것.
꺄!! 저의 동선 ㅎㅅㅎ
가다가 강에 빠졌어요ㅠㅠ
오늘 갑작스런 한파라 많이 춥더라고요 1/28
+ 바람도 많이 불고
+ 눈 완전 오고
하필 강에 빠져서 많이 추웠네요..
'JAVA' 카테고리의 다른 글
[Java] String.format() | 문자열 형식 지정하기 | %%%ds가 뭘까? | %% (0) | 2021.06.24 |
---|---|
Spring 학습 1일차 - 자바 웹 개발 첫경험을 위해 (0) | 2021.02.07 |
안드로이드 생존코딩(오준석) - 챕터9 전자액자 Fragment 상세설명 - 안드로이드 스튜디오 4.1 (0) | 2021.01.25 |
동반객체와 팩토리 패턴 | companion object, Factory Method Pattern (0) | 2021.01.24 |
[안드로이드 스튜디오] Layout 개념 기본 정리 / View binding (0) | 2021.01.20 |