이전에 진행했었던 프로젝트 내용으로,
요구사항은 백그라운드에서 주기적으로 사용자의 위치를 파악하여 특정 위치에 사용자가 있다면, 알림을 띄우는 내용이였다.
활용으로는 아마, 광고성 알림도 있을것이고 또는 미세먼지나 각종 상황을 경고해줄수도 있고,
해당 지역의 정보또한 제공해줄수 있는 다양한 방법으로 활용 할 수 있을거같다.
우선적으로, 주기적으로 위치를 파악하는 문제가 있는데
당연하게도 앱이 꺼져있는 상태에서도 위치를 파악할수가 있어야했다.
그래서 백그라운드상에서 일정시간마다 해당 기기의 gps정보를 받아오는 방법을 택했고, 거기에 사용된 녀석은
AndroidX jetpack에 있는 work manger란 녀석이다.
*참고 사이트 android developer
https://developer.android.com/jetpack
백그라운드에서는 당연하게도 메모리나 cpu성능을 많이 잡아먹을수 없게 되어있기때문에
프로젝트를 진행했을 당시에는 최소시간이 15분으로 제한되어있었다.
**2021.2.4 추가
핸드폰이 sleep 상태이거나 ( 오랫동안 사용하지 않는 상태 )
또는 충전중이 아닌데, 다른 프로세스로 인하여 바쁜경우 등으로 정확히 지정된 시간에 실행되지는 않고
유동적으로 실행되는 듯 하다. 하지만 system에 박혀버리게 하는지, 꼭 실행은 된다
( 그렇다고 해서 실행되는 시간이 4분이상은 차이나지 않는거같다. 테스트해보니..)
**2021.8.21 추가
포그라운드로 전환이 되지않고 백그라운드에서만 앱이 실행될경우
종종 절전모드/초절전모드/배터리최적화 등에 걸려들어가서
네트워크사용에 제약을받는다. 예외앱으로 걸어두는 작업이 필요하다.
그렇다면 일단 worker를 만들어보자!
//google gps
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
//work manager
implementation "androidx.work:work-runtime-ktx:2.1.0"
//kotlin
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
우선 생각나는 의존성은 위치추적을 위한 gps,
그리고 15분마다 실행할 workmanager
사용할 언어인 kotlin
까지 의존성을 추가했다.
그다음은, Manifest에 권한을 추가해보자.
**(수정) android Q 이상을 타겟팅 하는 앱은 추가적으로 권한수집을 해야한다
//상세
uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
//대략적
uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
//Android Q
uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
워커를 이제 본격적으로 작성해보면, 일단 worker를 정의하고 호출하는 친구를 init()안에다가 넣어주었다.
PeriodicWorkRequestBuilder를 worker queue에 추가시킨다는 내용인데, 15분마다 실해시키겠다는 이야기다.
private fun doWorkWithPeriodic() {
LogUtil.d("CheckGpsWorker", "worker 시작함수 진입")
val workRequest = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES).build()
/*
ExistingPeriodicWorkPolicy.KEEP : 워크매니저가 실행중이 아니면 새로 실행하고, 실행중이면 아무작업도 하지 않는다.
ExistingPeriodicWorkPolicy.REPLACE : 워크매니저를 무조건 다시 실행한다.
*/
WorkManager.getInstance(this).enqueueUniquePeriodicWork(CHECK_GPS.code, ExistingPeriodicWorkPolicy.REPLACE, workRequest)
}
enqueueUniquePeriodicWork를 타고 들어가보면 친절하게 설명이 나와있는데, 말그대로 이름을 토대로 유니크하게 실행시키겠다는 이야기다.
나같은경우는 상수값인 CHECK_GPS.code라는 애를 만들어서 이름을 지정해주고,
ExistingPeriodicWorkPolicy 작업정책으로는 REPLACE로 해주었다 ( 앱이 다시 켜진다면, 바로 위치추적 하기 위함 )
/**
* This method allows you to enqueue a uniquely-named {@link PeriodicWorkRequest}, where only
* one PeriodicWorkRequest of a particular name can be active at a time. For example, you may
* only want one sync operation to be active. If there is one pending, you can choose to let it
* run or replace it with your new work.
* The {@code uniqueWorkName} uniquely identifies this PeriodicWorkRequest.
*
* @param uniqueWorkName A unique name which for this operation
* @param existingPeriodicWorkPolicy An {@link ExistingPeriodicWorkPolicy}
* @param periodicWork A {@link PeriodicWorkRequest} to enqueue. {@code REPLACE} ensures that if
* there is pending work labelled with {@code uniqueWorkName}, it will be
* cancelled and the new work will run. {@code KEEP} will run the new
* PeriodicWorkRequest only if there is no pending work labelled with
* {@code uniqueWorkName}.
* @return An {@link Operation} that can be used to determine when the enqueue has completed
*/
@NonNull
public abstract Operation enqueueUniquePeriodicWork(
@NonNull String uniqueWorkName,
@NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,
@NonNull PeriodicWorkRequest periodicWork);
}
이제 앱이켜질때 15분마다 Worker를 실행할테니, Worker가 할 일을 작성해보자.
**(수정) OS버전이 올라가면서
fusedlocation를 사용할때마다 권한체크를 필수적으로 하게 만들어두었다
아무래도 위치권한을 획득할때, 버전 P( 일부적용 )과 Q에 적용된 [앱이 사용중일때만 허용 및 일회성 허용] 이 생겨서
사용자의 선택에 따른 permission error를 염두한 듯 싶다.
해당 프로젝트는 mainActivity에서 권한체크를 필수적으로 하게 두었고, 일회성인 경우에도 해당 worker를 실행시킬 수 없게끔 설계를 해두었기 때문에
@SuppressLint("MissingPermission") 을 추가로 달아놓았다.
만약 해당 기능을 사용하기위해 주기적으로 체크해야한다면, if문을 추가하도록 하자.
@SuppressLint("MissingPermission")
class CheckGpsWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params){
companion object {
private val TAG = CheckGpsWorker::class.java.name
}
private val mContext = context
//위치 가져올때 필요
private val mFusedLocationClient: FusedLocationProviderClient by lazy {
LocationServices.getFusedLocationProviderClient(mContext)
}
override suspend fun doWork(): Result{
return try{
checkDistance()
}catch(err:Excpetion){
LogUtil.e(TAG, err)
Result.failure()
}
}
}
우선적으로 Log를 위한 TAG를 만들어주고
doWork를 override한다 ( worker가 실행되면 이 친구가 실행이된다 )
어떤 좌표값에서 얼마나 떨어져있는지 체크를 하기 위해서 checkDistance()라는 함수를 만들었다.
private fun checkDistance() {
try {
//마지막 위치를 가져오는데에 성공한다면
mFusedLocationClient.lastLocation.addOnCompleteListener { task ->
if( task.isSuccessful){
task.result?.let{ aLocation ->
val fromLat = aLocation.latitude
val fromLng = aLocation.longitude
val results = FloatArray(1)
/* WGS-84 타원체 계산 (m)단위의 대략적인 정보이므로, 실제거리와 차이가 있을 수 있다 */
if (aNotiLng != null && aNotiLat != null) {
Location.distanceBetween(
fromLat,
fromLng,
aNotiLat.toDouble(),
aNotiLng.toDouble(),
results
)
}
//300m 이내일 경우
if(results[0] < 300){
/* 프로젝트의 내용이므로 생략 */
}else{
}
}
}else{ LogUtil.e(TAG, "TASK 실패") }
}
}catch (err: SecurityException) { LogUtil.e(TAG, "Security", err) }
}
간단하게 300m 이내 이런식으로 사용이 가능하다.
프로젝트 안쪽의 내용으로는,
목표지점에서 300m내에 들어왔을경우 notification을 띄우는 형식이였다.
.
.
.
.
여담)
기본적으로 fusedlocation이 저전력을 목표로 마지막으로 알려진 위치정보를 가져오는 거라
( 최근에는 포그라운드에서도 locationManager를 사용 안하는 분위기 )
task ( 위치정보 ) 가 null 로 들어오는 경우가 있다.
일단 경험해본 null로 들어올때의 경우는
첫번째로 사용자가 gps를 꺼두었을 때에 안에있던 위치정보 캐시도 날아가 버리므로 null.
두번째로 Google API 가 모종의 이유로 캐시가 날아갔을때 ( 일부 보안/청소 프로그램이 위치정보를 날려버린다 )
.
.
.
requestUpdate ( locationRequst, callback, looper ) 를 호출해서 callback 메소드 안에다가 작성을 해야한다.
하지만 실제 정확한 위치를 가져오는대에 꽤 긴 시간이 걸린다 ( 대략 2~5분정도 )
그러므로 requestUpdate 또한 그만한 시간이..
**
이정도 시간은 실제 포그라운드에서 작동하는 지도앱 ( via 카카오맵, 구글맵, 네이버지도 ... )
들도 정확한 위치정보가져오는대에 꽤나 시간이 걸리는 모습이다. 이전보다 더 오래걸리는건 기분탓이 아닌듯.
**
'프로그래밍 공부 > Android' 카테고리의 다른 글
[android] context 란 (0) | 2022.12.21 |
---|---|
[android-kotlin] firebase notification / message (0) | 2022.07.14 |
[android] migration 진행중 ... kotlin v, IDE v, library v...etc... (0) | 2022.03.29 |
[kotlin] LiveData & DataBinding (0) | 2021.07.20 |
[kotlin] Android jetpack Navigation (2) | 2021.05.14 |