본문 바로가기

Android/Troubleshooting

안드로이드 인터넷 연결상태 체크: HttpURLConnection 객체 이용

들어가며..

글로벌 서비스를 담당하다보면 네트워크 관련 이슈를 생각보다 많이 접한다.

이동 중 인증되지 않은 인터넷에 자동 연결되서 갑자기 연결이 끊어진다던지.. Api 통신 도중에 갑자기 기지국 범위를 벗어나 끊어진다던지..(몇 초 안걸릴텐데 이게 죽)

이런 종류의 네트워크 이슈를 다루다보니 연결 상태를 체크 해 주는 코드가 필요함을 느꼈고, 해당 기능을 모듈 형식으로 만들어서 사용하기로 했다.


 

🌐 HttpURLConnection

HttpURLConnection?

val url = URL("http://example.com")
val con = url.openConnection() as HttpURLConnection
con.requestMethod = "GET"
val responseCode = con.responseCode

 

HttpURLConnection은 보통 Java에서 HTTP 요청을 보내고 받기 위한 클래스를 칭한다.
이 클래스는 java.net 패키지에 속하며, URL을 사용하여 HTTP 요청을 생성하고, 서버로부터 응답을 받는 기능을 제공한다.
현재 앱이 Kotlin 환경임에도 불구하고 Java표준 라이브러리에 포함된 HttpURLConnection를 선택한 이유는 다음과 같다.
Kotlin은 Retrofit과 Ktor, Khttp같은 우수한 통신 라이브러리의 사용이 가능하다. 하지만, 이런 라이브러리는 Client를 생성하거나 gradle에 dependency를 추가하는 과정이 동반된다.
반면, HttpURLConnection은 JVM기반 언어에서 기본적으로 사용이 가능하기 때문에, 별도의 의존성 추가 및 Client 코드 생성이 필요하지 않다. 게다가 작성이 단순해 코드 가독성 확보에도 용이하다.
이런 이유로 HttpURLConnection을 사용하기로 했다.

HttpURLConnection에서 HTTP_OK는 HTTP 요청이 성공적으로 완료되었음을 나타내는 상태 코드이다.
문제가 되는 부분은, 인증받지 않은 Wifi 일지라도 디바이스에 연결이 되면 HttpURLConnection은 요청 성공으로 인식한다는 점이다.
이러면 네트워크에 연결은 되어있지만, 실질적인 사용은 하지 못하는 상황이 된다...

코시절 인터넷을 쓰기위한 인증..

위 사진과 같은 웹 프론트가 뜨면서 연결은 되지만 원하는 네트워크 활동을 하지 못한 경험이 있을 것이다(요즘도 그런진 모르겠는데 본인 학부 시절엔 그랬다).
따라서 HttpURLConnection 모듈의 기능은 다음과 같아야 했다.
HTTP_OK 상태와 더불어 네트워크 통신이 가능한 상태인지 체크하기

http://clients3.google.com/generate_204

 

위 URL은 Google이 인터넷 연결 상태를 간단하게 확인할 수 있도로 제공하는 서비스이다.
이 URL에 요청을 보내면, 서버는 "204 No Content" 상태 코드와 함께 응답을 반환한다.
응답을 해석하자면 '서버가 요청을 성공적으로 처리했으나 뭐 전송할 데이터는 없다'는 것으로, "인터넷도 연결되었고 다른 통신요청도 된다"라고 이해하면 편하다.
이 URL을 사용 할 시, wifi 가 연결되었지만 권한이 없는경우는 200을 리턴하고, 권한이 있는경우는 204을 리턴한다.


 

🅲 Code

/**
 * 네트워크 연결 상태를 확인하는 함수
 *
 * google이 제공하는 http://clients3.google.com/generate_204 사용
 *
 * 204 응답을 받으면 인터넷이 연결된 것으로 간주
 */
suspend fun hasInternetConnection(): Boolean {
    return withContext(Dispatchers.IO) {
        try {
            (URL("http://clients3.google.com/generate_204").openConnection() as HttpURLConnection).run {
                connectTimeout = 10000 // 10초 타임아웃
                connect() // 연결 시도
                responseCode == 204
            }
        } catch (e: Exception) {
            false
        }
    }
}

//오류 발생 시 호출될 함수
fun onConnectionFailureAndRetry(
    invoiceNo: String,
    errorMsg: String,
) = CoroutineScope(Dispatchers.IO).launch {

    Timber.e("on onConnectionFailureAndRetry")

    val invoiceData = LocalDB.getInstance().invoiceListDao().getSingleInvoiceData(invoiceNo)
    // 네트워크 연결이 복구될 때까지 대기
    while (!hasInternetConnection()) {
        delay(3_000) // 3초마다 연결 확인
    }
    Timber.e("Connected!")
    // 연결이 복구되면 오류 로그 전송
    /* TODO */
}

 

hasInternetConnection()는 응답으로 204를 반환받으면 true를 반환한다.
그래서 3초마다 연결을 확인하여 true가 반환되면 loop를 탈출하여 다음 코드를 진행하도록 했다.
TODO 부분은 이제 인터넷 연결이 확인되었을때 동작시킬 코드를 작성하면 된다. 본인은 회사 서버 DB와 firebase에 로그를 수집하는 코드를 작성했다.

무튼 이거 덕에 서비스 중인 지역의 어느 구역에서 인터넷이 끊어지는지 알 수 있었다..