뚜벅이!
Mobile :)
뚜벅이!
전체 방문자
오늘
어제
  • 분류 전체보기 (53)
    • 코딩테스트 (16)
      • programmers level1 (7)
      • codility (9)
    • 프로그래밍 공부 (31)
      • Spring Boot (6)
      • Nuxt.js (5)
      • Node.js (3)
      • Etc (11)
      • Android (6)
    • 잡다한 글 (4)
    • 토이프로젝트 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Spring
  • 초보자
  • Kotlin
  • Notification
  • JS
  • firebase
  • ad
  • nuxt
  • 부트
  • Jetpack
  • Vue
  • javascript
  • programmers
  • 연습
  • NavBar
  • lesson3
  • lesson4
  • level1
  • node
  • docker
  • node.js
  • codillity
  • token
  • 스킬체크테스트
  • AndroidX
  • 프로그래머스
  • Spring boot
  • nuxt.js
  • Vue.js
  • lesson2

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
뚜벅이!

Mobile :)

프로그래밍 공부/Android

[Android&Kotlin] gps tracking

2021. 2. 4. 12:44
728x90


이전에 진행했었던 프로젝트 내용으로,

요구사항은 백그라운드에서 주기적으로 사용자의 위치를 파악하여 특정 위치에 사용자가 있다면, 알림을 띄우는 내용이였다.

 

활용으로는 아마, 광고성 알림도 있을것이고 또는 미세먼지나 각종 상황을 경고해줄수도 있고,
해당 지역의 정보또한 제공해줄수 있는 다양한 방법으로 활용 할 수 있을거같다.

 

 

우선적으로, 주기적으로 위치를 파악하는 문제가 있는데
당연하게도 앱이 꺼져있는 상태에서도 위치를 파악할수가 있어야했다.

그래서 백그라운드상에서 일정시간마다 해당 기기의 gps정보를 받아오는 방법을 택했고, 거기에 사용된 녀석은

AndroidX jetpack에 있는 work manger란 녀석이다.

 

 

 

*참고 사이트 android developer

https://developer.android.com/jetpack

 

Android Developers

"WorkManager는 redBus 앱에서 백엔드 서버에 정보를 전달하는 방식을 단순화했습니다. 네트워크 연결, 배터리와 같은 매개변수를 처리하고 AlarmManager 또는 JobScheduler와 같은 적절한 핸들러를 사용하는 WorkManager 라이브러리의 기능을 통해 저희는 비즈니스 로직을 빌드하는 데 집중하고 WorkManager로 실행 복잡성을 줄일 수 있었습니다." 디네시 샨무감, redBus.in Android 담당자

developer.android.com

 

 

백그라운드에서는 당연하게도 메모리나 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 카카오맵, 구글맵, 네이버지도 ... )

들도 정확한 위치정보가져오는대에 꽤나 시간이 걸리는 모습이다. 이전보다 더 오래걸리는건 기분탓이 아닌듯.

**

 

 

728x90
저작자표시 (새창열림)

'프로그래밍 공부 > 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
    '프로그래밍 공부/Android' 카테고리의 다른 글
    • [android-kotlin] firebase notification / message
    • [android] migration 진행중 ... kotlin v, IDE v, library v...etc...
    • [kotlin] LiveData & DataBinding
    • [kotlin] Android jetpack Navigation
    뚜벅이!
    뚜벅이!
    2022. 4년차 안드로이드 개발자 wndnjs19@gmail.com

    티스토리툴바