관리 메뉴

나만을 위한 블로그

[Android] 네트워크 연결 상태를 확인하는 방법(JAVA + Kotlin) 본문

Android

[Android] 네트워크 연결 상태를 확인하는 방법(JAVA + Kotlin)

참깨빵위에참깨빵 2021. 5. 23. 20:19
728x90
반응형

네트워크 예외처리를 하다 보면 현재 기기에 인터넷이 연결된 상태인지를 확인해야 할 수 있다.

연결된 상태면 다음 로직을 이어서 수행하거나, 연결되지 않은 상태라면 다이얼로그를 띄워서 이전 액티비티로 보낸다거나 하는 처리가 그것이다. 대부분의 상용 앱에서도 데이터 연결, 와이파이 연결을 모두 해제하면 이러한 처리가 되어 있는 걸 볼 수 있다.

이번 포스팅에선 자바와 코틀린으로 인터넷 연결 상태를 어떻게 확인하는지에 대해 포스팅하려고 한다.

 

주의할 것은 이 기능을 구현하는 코드의 전개 방식은 정말 다양하니 아래의 방법이 정답이 아니라는 것만 짚어두고 간다.

아래 글을 읽기 전 매니페스트에 아래의 권한이 있는지 꼭 확인한다. 없으면 추가한다.

 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

 

먼저 자바로는 어떻게 하는지 확인해보자.

private class disconnectHandler implements Runnable
    {
        @Override
        public void run()
        {
            boolean isConnected = isNetworkConnected(SplashActivity.this);
            if (!isConnected)
            {
                AlertDialog.Builder builder = new AlertDialog.Builder(SplashActivity.this);
                builder.setMessage("인터넷 연결이 원활하지 않습니다")
                        .setCancelable(false)
                        .setPositiveButton("종료", new DialogInterface.OnClickListener()
                        {
                            @Override
                            public void onClick(DialogInterface dialog, int which)
                            {
                                finishAffinity();
                            }
                        }).show();
            }
            else
            {
                .
                .
                .
                SplashActivity.this.finish();
            }
        }
    }

내 경우엔 핸들러를 만들고 그 안에서 네트워크 연결 상태를 확인한 다음 그 값을 변수에 저장해서 어떤 상태냐에 따라 다이얼로그가 나오거나 다음 화면으로 이동하도록 처리했다. else 블럭 안 마지막 줄의 finish()가 그것이다. 그 위의 로직들은 지금 포스팅과는 상관없는 내용들이기 때문에 없앴다.

위의 코드에서 사용된 isNetworkConnected()의 내용은 아래와 같다.

public boolean isNetworkConnected(Context context)
    {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mobile = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        NetworkInfo wimax = manager.getNetworkInfo(ConnectivityManager.TYPE_WIMAX);
        boolean bwimax = false;
        if (wimax != null)
        {
            bwimax = wimax.isConnected();
        }
        if (mobile != null)
        {
            if (mobile.isConnected() || wifi.isConnected() || bwimax)
            {
                return true;
            }
        }
        else
        {
            if (wifi.isConnected() || bwimax)
            {
                return true;
            }
        }
        return false;
    }

wimax는 흔히 4G라 불리는 네트워크에 연결할 수 있도록 하는 통신 프로토콜이다. 그래서 3G, 와이파이, 4G 상태를 모두 체크하여 연결된 상태면 true, 아니면 false를 리턴시킨다. 이 메서드를 통해 네트워크 통신 예외처리를 할 수 있다.

주의할 것은 API 28부터 ConnectivityManager.TYPE_MOBILE, TYPE_WIFI, TYPE_WIMAX 3개는 deprecated되었다.

그래서 이걸 쓰기 찜찜하다면 아래의 포스팅들을 확인하면 좋을 것 같다.

https://gist.github.com/PasanBhanu/730a32a9eeb180ec2950c172d54bb06a

 

Check Internet Connection in Android (API Level 29) Using Network Callback

Check Internet Connection in Android (API Level 29) Using Network Callback - CheckNetwork.java

gist.github.com

https://developer.android.com/training/basics/network-ops/reading-network-state#java

 

네트워크 상태 읽기  |  Android 개발자  |  Android Developers

Android에서는 앱이 연결의 동적 변경사항을 학습할 수 있습니다. 다음 클래스를 사용하여 연결 변경사항을 추적하고 적절히 대응하세요. ConnectivityManager는 앱에 시스템의 연결 상태를 알립니다. N

developer.android.com

https://developer.android.com/reference/android/net/NetworkCapabilities.html#hasTransport(int) 

 

NetworkCapabilities  |  Android 개발자  |  Android Developers

 

developer.android.com

 

아무튼 자바로 하는 건 어떻게 하는지 대충 알았으니 이제 코틀린으로는 어떻게 만드는지 확인해보자.

먼저 클래스 하나를 만들어줘야 한다.

import android.annotation.TargetApi
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.*
import android.os.Build
import androidx.lifecycle.LiveData

class NetworkConnection(private val context: Context) : LiveData<Boolean>()
{
    private var connectivityManager: ConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    private lateinit var networkCallback: ConnectivityManager.NetworkCallback

    override fun onActive()
    {
        super.onActive()
        updateConnection()
        when
        {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ->
            {
                connectivityManager.registerDefaultNetworkCallback(connectivityManagerCallback())
            }

            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ->
            {
                lollipopNetworkRequest()
            }
            else ->
            {
                context.registerReceiver(
                    networkReceiver,
                    IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
                )
            }
        }
    }

    override fun onInactive()
    {
        super.onInactive()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            connectivityManager.unregisterNetworkCallback(connectivityManagerCallback())
        } else
        {
            context.unregisterReceiver(networkReceiver)
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun lollipopNetworkRequest()
    {
        val requestBuilder = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
        connectivityManager.registerNetworkCallback(
            requestBuilder.build(),
            connectivityManagerCallback()
        )
    }

    private fun connectivityManagerCallback(): ConnectivityManager.NetworkCallback
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            networkCallback = object : ConnectivityManager.NetworkCallback()
            {
                override fun onLost(network: Network)
                {
                    super.onLost(network)
                    postValue(false)
                }

                override fun onAvailable(network: Network)
                {
                    super.onAvailable(network)
                    postValue(true)
                }
            }
            return networkCallback
        } else
        {
            throw IllegalAccessError("Error")
        }
    }

    private val networkReceiver = object : BroadcastReceiver()
    {
        override fun onReceive(context: Context?, intent: Intent?)
        {
            updateConnection()
        }
    }

    private fun updateConnection()
    {
        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
        postValue(activeNetwork?.isConnected == true)
    }

}

 

 

이제 이 클래스를 사용해서 액티비티에서 네트워크 연결을 확인해 보자. 먼저 XML 파일의 코드다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    tools:context=".SecondActivity">

    <LinearLayout
        android:id="@+id/layoutDisconnected"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">

        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/wifi_no"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="DISCONNECTED"
            android:textAllCaps="true"
            android:textColor="@android:color/holo_red_dark"
            android:textSize="24sp"
            android:textStyle="bold"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="40dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="40dp"
            android:gravity="center"
            android:text="인터넷에 연결되어 있지 않습니다"
            android:textSize="18sp"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/layoutConnected"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="gone"
        android:gravity="center">

        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/wifi_ok"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="DISCONNECTED"
            android:textAllCaps="true"
            android:textColor="@android:color/holo_green_dark"
            android:textSize="24sp"
            android:textStyle="bold"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="40dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="40dp"
            android:gravity="center"
            android:text="인터넷에 연결되어 있습니다"
            android:textSize="18sp"/>

    </LinearLayout>

</FrameLayout>

마지막으로 위 XML 파일을 사용하는 코틀린 파일을 아래와 같이 작성한다.

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import kotlinx.android.synthetic.main.activity_second.*

class SecondActivity : AppCompatActivity()
{
    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val connection = NetworkConnection(applicationContext)
        connection.observe(this, Observer { isConnected ->
            if (isConnected)
            {
                layoutDisconnected.visibility = View.GONE
                layoutConnected.visibility = View.VISIBLE
            } else
            {
                layoutConnected.visibility = View.GONE
                layoutDisconnected.visibility = View.VISIBLE
            }
        })
    }
}

여기까지 작성한 후 예제를 빌드하면 아래와 같이 작동하는 걸 볼 수 있다.

 

코틀린 코드의 경우 LiveData를 사용했기 때문에 코드 분석 난이도가 좀 높지만, 이걸 쓴 덕분에 실시간으로 인터넷 연결 상태를 확인해서 뒤의 이미지와 글자가 즉시 바뀌는 걸 볼 수 있다.

주의할 것은 위의 움짤처럼 마구 테스트한 다음 앱을 종료하면 에러 메시지가 나오면서 앱이 죽는 경우가 생길 수 있는데, 그것에 필요한 예외처리를 하지 않아 생기는 현상이다. 예외처리만 해주면 정상 작동한다.

반응형
Comments