github 프로젝트를 본 학생분께서 질문메일을 보내주셨습니다. (관심있게 봐주셔서 감사합니다.)
저 메일을 받고, 머리를 탁 맞은 기분이였습니다.
단순예제를 보이기 위함이였지만, 그럼에도 설명이 많이 부족했구나를 깨달았습니다.
그에따라 해당 부분에 대한 코드를 수정하였는데요.
nonce 를 생성하는 더 좋은 코드와 방법이 있다면, 답글로 알려주시면 감사하겠습니다 (꾸벅)
/**
* Nonce : 안드로이드에서 SafetyNet 에서 사용
* SafetyNet Attestation API를 호출할 때 nonce를 전달해야 합니다.
* SafetyNet 요청에 사용되는 nonce는 길이가 16바이트 이상이어야 합니다.
* */
class SafetyUtils {
//랜덤한 문자열을 생성하기 위한 BASE String
private fun loadBaseString(): String {
return "abcdefghijklmnopqrstuvwxyz0123456789_-"
}
//현재 년월일시분초밀리초를 반환
private fun createDateNow(): String {
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")
return current.format(formatter)
}
//결합하여 고유하면서 랜덤한 nonce 생성 (Size : 34)
fun createNonce() : ByteArray {
val dateStr = createDateNow()
val baseStr = loadBaseString()
val nonce = StringBuilder()
for (i in dateStr.toCharArray()) {
//date 로 BASE 참조 (고유) : 0~9 를 참조하기 위해 i - ASCII
nonce.append(baseStr[i.toInt()-48])
//랜덤하게 BASE 참조 (랜덤)
nonce.append(baseStr[Random().nextInt(baseStr.length - 1)])
}
return nonce.toString().toByteArray()
}
}
val FIREBASE_AUTH_NONCE = SafetyUtils().createNonce()
앞으로 github 에 코드를 작성할 때, 코드를 보는 사람이 추가적으로 작성을 해야하는 부분이나, 이미 작성된 부분에 대해서
권한이 없는 것도 확인이 되고, 권한 요청을 시도해야하는 Cusom UI 까지 동작을 합니다. (권한이 없기때문에)그런데 권한요청팝업을 띄우는 코드가 동작하지 않습니다.
라는 질문을 받았습니다.
제 처음 답변은
"정말 코드가 정상적으로 동작하는 코드라는 확신이 있거나, 이전에 동작하는것에 문제가 없다면"
안드로이드 버전에 따른 이슈 등을 확인하여 변경된 사항을 체크해야 한다는 것을 알려주었습니다.
그러나, 쉽사리 해결하지 못하고 있었기에 직접 찾아보았습니다.
1. 앱에서 위치데이터를 수집하기 위해 아래와 같은 퍼미션을 추가하였다. <uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"/>
2. 그러나 질문자는 이전처럼 위치권한을 수락해도, 백그라운드에서 데이터를 수집할 수 없다는 것을 알고 있다.
서비스(Service)는 사용자와 상호 작용하지 않고 더 오래 실행되는 작업을 수행하려는 응용 프로그램 구성 요소 또는 다른 응용 프로그램이 사용할 수 있는 기능을 제공하는 응용 프로그램 구성 요소이다.
사용자와 상호 작용하지 않고 : UI 가 존재하지 않습니다. 더 오래 실행되는 작업을 수행 : 백그라운드에서 작업을 수행합니다. 다른 응용 프로그램이 사용할 수 있는 : A 앱에서 B 앱의 기능을 사용할 수 있습니다. (B앱의 서비스가 이를 제공해야함)
* 서비스는 별도의 프로세스가 아니다. - 서비스를 백그라운드에서 돌릴 수 있지만, 서비스 자체가 별도의 프로세스는 아닙니다. 따로 지정하지 않는 이상, 서비스는 앱의 프로세스와 동일한 프로세스에서 동작합니다.
* 서비스는 쓰레드가 아니다. - 흔히 앱에서 백그라운드쓰레드로 작업을 처리하는 경우가 있는데, 서비스는 단지, 백그라운드에게 "수행해야 할 작업" 이 있다 를 "알리는" 것일 뿐입니다.
* 서비스는 프로세스 내의 주 쓰레드에서 동작하기 때문에, MP3 재생 등과 같은 기능을 하려면 별도의 쓰레드를 서비스 내에 만들어 주어야 한다.
서비스 LifeCycle
onStartCommand()시스템이 이 메서드를 호출하는 것은 또 다른 구성 요소(예: 액티비티)가 서비스를 시작하도록 요청하는 경우입니다. 이때 startService()를 호출하는 방법을 씁니다. 이 메서드가 실행되면 서비스가 시작되고 백그라운드에서 무한히 실행될 수 있습니다. 이것을 구현하면 서비스의 작업이 완료되었을 때 해당 서비스를 중단하는 것은 개발자 본인의 책임이며, 이때 stopSelf() 또는 stopService()를 호출하면 됩니다. 바인딩만 제공하고자 하는 경우, 이 메서드를 구현하지 않아도 됩니다.
onBind()시스템은 다른 구성 요소가 해당 서비스에 바인딩되고자 하는 경우(예를 들어 RPC를 수행하기 위해)에도 이 메서드를 호출합니다. 이때 bindService()를 호출하는 방법을 사용합니다. 이 메서드를 구현할 때에는 클라이언트가 서비스와 통신을 주고받기 위해 사용할 인터페이스를 제공해야 합니다. 이때 IBinder를 반환하면 됩니다. 이 메서드는 항상 구현해야 하지만, 바인딩을 허용하지 않으려면 null을 반환해야 합니다.
onCreate() 시스템은 서비스가 처음 생성되었을 때(즉 서비스가 onStartCommand() 또는 onBind()를 호출하기 전에) 이 메서드를 호출하여 일회성 설정 절차를 수행합니다. 서비스가 이미 실행 중인 경우, 이 메서드는 호출되지 않습니다.
onDestroy()시스템이 이 메서드를 호출하는 것은 서비스를 더 이상 사용하지 않고 소멸시킬 때입니다. 서비스는 스레드, 등록된 리스너 또는 수신기 등의 각종 리소스를 정리하기 위해 이것을 구현해야 합니다. 이는 서비스가 수신하는 마지막 호출입니다.