본문 바로가기

안드로이드/프로젝트

[안드로이드] 임장노트 #2 LocationManager를 이용한 대략적인 주소 구하기

LocationManager를 이용하여 현재 내 위치를 구현할 것이다.

LocationManager를 쓰는게 여러가지를 두고 봤을 때 가장 간단해서 쓰는 것이다. 

 

 

 

위 사진과 같이 임장노트를 작성할 때 대략적인 나의 위치를 미리 매핑해주는 역할을 한다. 그렇게 되면, 세부적인 주소만 작성하고 나머지 주소는 따로 타이핑 하지 않아도 되서 간편할 것이다.

 

과정은 간단하게 아래에 순서대로 서술하겠다.

  1. AndroidManifest에 ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION 권한을 추가해준다.
  2. 해당 권한을 사용할 수 있게 PermissionListener를 만든다. (tedPermission강력 추천)
  3. MyLocation 클래스를 만들어준다. 이때 파라미터에 Activity Context를 넣어준다. LocationManager를 Activity Context의 만들어주는 이유는, Activity가 onDestroy 됐을 때 GC가 메모리를 알아서 처리해주기 때문이다.
  4. 내 현재 위치를 반환해주는 get 함수를 만든다.

이제 위 과정들을 한번 구현해보자.

 

1. useMyLocation.kt

view에 해당하는 클래스이다. Permission이 주제인 포스팅은 아니니까 Permission에 대한 것은 간단하게 view에서 만들어주고 ViewModel에 있는 내 위치 가져오는 메소드만 불러줄 것이다.

class UseMyLocation:BaseActivity(){

	...

	private fun permissionCheck() {
        if (viewModel.checkCoarseLocationPermission(this@WriteAuctionActivity) &&
            viewModel.checkFineLocationPermission(this@WriteAuctionActivity)
        ) {
            tedPermission()
        } else {
            viewModel.getLocation(this@WriteAuctionActivity)
        }
    }


    private fun tedPermission() {
        val permissionListener = object : PermissionListener {
            override fun onPermissionGranted() {
                viewModel.getLocation(this@WriteAuctionActivity)
            }

            override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
                Toast.makeText(
                    this@WriteAuctionActivity,
                    "설정에서 권한을 허가 해주세요.",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }

        TedPermission.with(this)
            .setPermissionListener(permissionListener)
            .setRationaleMessage("서비스 사용을 위해서 몇가지 권한이 필요합니다.")
            .setDeniedMessage("[설정] > [권한] 에서 권한을 설정할 수 있습니다.")
            .setPermissions(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            )
            .check()
    }
}

 

 

2. UseMyLocationViewModel.kt

ViewModel에서는 MyLocation 클래스를 통하여 주소를 불러와 myLocation 변수에 주소를 넣어줄 것이다 .이때 myLocation 변수는 ObservableField<String> 타입으로 지정하여 값이 변하면 자동으로 값이 TextView에 반영되게 해 두었다.

@HiltViewModel
class UseMyLocationViewModel @Inject constructor() : ViewModel() {

    val myLocation: ObservableField<String> = ObservableField("주소를 표시할 수 없습니다.")

    fun checkFineLocationPermission(context: Context): Boolean = ActivityCompat.checkSelfPermission(
        context, Manifest.permission.ACCESS_FINE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED

    fun checkCoarseLocationPermission(context: Context): Boolean =
        ActivityCompat.checkSelfPermission(
            context, Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED


    @SuppressLint("MissingPermission")
    fun getLocation(context: Context) {
        val locationLoader = MyLocation(context)
        myLocation.set(locationLoader.get())
    }
}

 

 

3. Mylocation.kt

실질적으로 이곳에서 현재 내 위치를 받아오는 곳이다.

class MyLocation constructor(private val context: Context) {

    private lateinit var locationManager: LocationManager

    fun get(): String {
        setLocationManager()
        return setMyLocation(getLastLocation())
    }

    private fun setLocationManager() {
        // locationManager 초기화 해주기
        locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    }
	
	/* 
    Permission은 MyLocation 클래스가 아니라 해당 클래스를 사용해주는
    클래스에서 처리해주기
    따라서 MissingPermission을 Supress 해준다. 
    */
    @SuppressLint("MissingPermission")
    private fun getLastLocation(): Location? =
        if (locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) != null) {
            locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
        } else {
            locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
        }
	
    // loc 변수에서 latitude, longtitude 추출
    private fun setMyLocation(loc: Location?): String {
        return if (loc != null) {
            val latlng = LatLng(loc.latitude, loc.longitude)
            return getAddress(latlng)
        } else {
            "주소정보 가져올 수 없음"
        }
    }
	
    // Geocoder를 자기 나라에 맞게 설정해준다.
    // try catch 문으로 grpc 오류 대처해주기
    private fun getAddress(position: LatLng): String {
        val geoCoder = Geocoder(context, Locale.KOREA)
        var addr = "주소 오류"

        try {
            addr = geoCoder.getFromLocation(position.latitude, position.longitude, 1).first()
                .getAddressLine(0)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return addr
    }
}

 

 

앞서 설명한 과정을 코딩하면 위와 같은 코드가 나온다.

 

이때 무조건적으로 조심해야 할 것이 있다. 바로 GRPC 오류이다. 이 grpc 오류는 getCoder를 사용할때 주로 생기며, 발생 원인은 에뮬레이터의 네트워크가 불안정하면, 주소를 제대로 가져올 수 없어서 해당 오류가 발생하게 된다.

따라서 geoCoder를 사용할때는 try~catch문을 이용해서 오류에 대한 대처를 해주는게 가장 현명하다. 

 

 

 

 

참고

https://angangmoddi.tistory.com/227

 

GEOcoder 사용 시 java.io.IOException: grpc failed오류 해결

안드로이드에서 Geocoder를 사용해서 위도, 경도를 주소(서울특별시 xxxx~~)로 바꾸는 과정에서 java.io.IOException: grpc failed에러가 발생해서 해결법을 좀 찾아봤다. 보통 에뮬레이터 사용 시 에러가 발

angangmoddi.tistory.com