이 과정에서는 네트워크 리소스 사용을 세밀하게 제어하는 애플리케이션 작성 방법을 설명합니다. 애플리케이션에서 많은 네트워크 작업을 실행한다면 앱의 데이터 사용 패턴(예: 앱의 데이터 동기화 빈도, Wi-Fi에 연결되었을 때만 업로드/다운로드 실행 여부, 로밍 중 데이터 사용 여부)을 사용자가 제어할 수 있도록 사용자 설정을 제공해야 합니다. 이러한 제어 기능을 제공하면 사용량 한도에 가까워질 때 사용자가 앱의 데이터 사용량을 정밀하게 제어할 수 있으므로 앱의 백그라운드 데이터 액세스를 사용 중지할 가능성이 크게 줄어듭니다.
일정 기간의 네트워크 연결 횟수 및 유형을 포함한 앱의 네트워크 사용에 관한 자세한 내용은 웹 앱 및 네트워크 프로파일러로 네트워크 트래픽 검사를 읽어보세요. 다운로드 및 네트워크 연결이 배터리 수명에 미치는 영향을 최소화하는 앱 작성 방법에 관한 일반 가이드라인은 배터리 수명 최적화 및 배터리 소모 없이 데이터 전송을 참고하세요.
NetworkConnect 샘플도 확인할 수 있습니다.
기기의 네트워크 연결 확인
기기에는 다양한 유형의 네트워크 연결이 있을 수 있습니다. 이 과정에서는 Wi-Fi 또는 모바일 네트워크 연결을 사용하는 것에 중점을 둡니다. 사용 가능한 네트워크 유형의 전체 목록은 ConnectivityManager를 참고하세요.
일반적으로 Wi-Fi 속도가 더 빠릅니다. 또한, 모바일 데이터는 데이터 전송량 제한이 있는 경우가 많아서 비용이 많이 들 수 있습니다. 앱에서는 일반적으로 Wi-Fi 네트워크를 사용할 수 있을 때만 대용량 데이터를 가져옵니다.
네트워크 작업을 실행하기 전에 네트워크 연결 상태를 확인하는 것이 좋습니다. 무엇보다도 이렇게 하면 앱에서 실수로 잘못된 네트워크를 사용하는 것을 방지할 수 있습니다. 네트워크 연결을 사용할 수 없다면 애플리케이션이 적절하게 응답해야 합니다. 네트워크 연결을 확인하려면 일반적으로 다음 클래스를 사용합니다.
ConnectivityManager: 네트워크 연결 상태에 관한 쿼리에 답합니다. 또한, 네트워크 연결이 변경될 때 애플리케이션에 알립니다.NetworkInfo: 특정 유형(현재는 모바일 또는 Wi-Fi)의 네트워크 인터페이스 상태를 설명합니다.
이 코드 스니펫은 Wi-Fi 및 모바일 네트워크 연결을 테스트합니다. 이러한 네트워크 인터페이스가 사용 가능한지(즉, 네트워크 연결이 가능한지)와 연결되어 있는지(즉, 네트워크 연결 여부 및 소켓 설정과 데이터 전달 여부)를 확인합니다.
Kotlin
private const val DEBUG_TAG = "NetworkStatusExample"
...
val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
var isWifiConn: Boolean = false
var isMobileConn: Boolean = false
connMgr.allNetworks.forEach { network ->
    connMgr.getNetworkInfo(network).apply {
        if (type == ConnectivityManager.TYPE_WIFI) {
            isWifiConn = isWifiConn or isConnected
        }
        if (type == ConnectivityManager.TYPE_MOBILE) {
            isMobileConn = isMobileConn or isConnected
        }
    }
}
Log.d(DEBUG_TAG, "Wifi connected: $isWifiConn")
Log.d(DEBUG_TAG, "Mobile connected: $isMobileConn")
자바
private static final String DEBUG_TAG = "NetworkStatusExample";
...
ConnectivityManager connMgr =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isWifiConn = false;
boolean isMobileConn = false;
for (Network network : connMgr.getAllNetworks()) {
    NetworkInfo networkInfo = connMgr.getNetworkInfo(network);
    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
        isWifiConn |= networkInfo.isConnected();
    }
    if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
        isMobileConn |= networkInfo.isConnected();
    }
}
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
네트워크의 '사용 가능 여부'에 따라 결정을 내려서는 안 됩니다. 네트워크 작업을 실행하기 전에는 항상 isConnected()를 확인해야 합니다. isConnected()에서 비정상적인 모바일 네트워크, 비행기 모드, 제한된 백그라운드 데이터와 같은 사례를 처리하기 때문입니다.
네트워크 인터페이스가 사용 가능한지 확인하는 더 간단한 방법은 다음과 같습니다. getActiveNetworkInfo() 메서드는 처음 연결된 네트워크 인터페이스를 찾아서 이를 나타내는 NetworkInfo 인스턴스를 반환하거나 연결된 인터페이스가 없는 경우(즉, 인터넷 연결 사용 불가) null을 반환합니다.
Kotlin
fun isOnline(): Boolean {
    val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
    return networkInfo?.isConnected == true
}
자바
public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}
더 자세한 상태를 쿼리하려면 NetworkInfo.DetailedState를 사용할 수 있으나 이런 경우는 흔하지 않습니다.
네트워크 사용 관리
사용자가 앱의 네트워크 리소스 사용량을 명시적으로 제어하도록 하는 환경설정 활동을 구현할 수 있습니다. 예:
- 기기가 Wi-Fi 네트워크에 연결된 경우에만 사용자가 동영상을 업로드하도록 할 수 있습니다.
 - 네트워크 가용성, 시간 간격 등 특정 기준에 따라 동기화하거나 동기화하지 않을 수 있습니다.
 
네트워크 액세스 및 네트워크 사용 관리를 지원하는 앱을 작성하려면 매니페스트에 올바른 권한과 인텐트 필터가 있어야 합니다.
- 이 섹션 뒷부분에 나오는 발췌된 매니페스트에는 다음 권한이 포함됩니다.
android.permission.INTERNET: 애플리케이션에서 네트워크 소켓을 열 수 있습니다.android.permission.ACCESS_NETWORK_STATE: 애플리케이션에서 네트워크에 관한 정보에 액세스할 수 있습니다.
 ACTION_MANAGE_NETWORK_USAGE작업을 위한 인텐트 필터를 선언하여 데이터 사용량 제어 옵션을 제공하는 활동을 애플리케이션에서 정의한다는 것을 나타낼 수 있습니다.ACTION_MANAGE_NETWORK_USAGE는 특정 애플리케이션의 네트워크 데이터 사용량 관리용 설정을 보여줍니다. 앱에 사용자가 네트워크 사용량을 제어할 수 있는 설정 활동이 있다면 그 활동에 대해 이 인텐트 필터를 선언해야 합니다.
샘플 애플리케이션에서 이 작업은 SettingsActivity 클래스가 처리하며, 이 클래스는 사용자가 피드를 다운로드하는 시점을 선택할 수 있는 환경설정 UI를 표시합니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.networkusage"
    ...>
    <uses-sdk android:minSdkVersion="4"
           android:targetSdkVersion="14" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        ...>
        ...
        <activity android:label="SettingsActivity" android:name=".SettingsActivity">
             <intent-filter>
                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
                <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
        </activity>
    </application>
</manifest>
민감한 사용자 데이터를 처리하는 앱 및 Android 11 이상을 타겟팅하는 앱은 프로세스별로 네트워크 액세스를 부여할 수 있습니다. 네트워크 액세스가 허용되는 프로세스를 명시적으로 지정하면 데이터를 업로드할 필요가 없는 모든 코드를 분리할 수 있습니다.
이렇게 하면 앱이 실수로 데이터 업로드하는 것을 막는다는 보장은 없지만, 앱의 버그로 인한 데이터 유출 가능성을 줄일 수 있습니다.
다음은 프로세스별 기능을 사용하는 매니페스트 파일의 샘플입니다.
<processes>
    <process />
    <deny-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet1" />
    <process android:process="com.android.cts.useprocess.withnet1">
        <allow-permission android:name="android.permission.INTERNET" />
    </process>
    <allow-permission android:name="android.permission.INTERNET" />
    <process android:process=":withoutnet2">
        <deny-permission android:name="android.permission.INTERNET" />
    </process>
    <process android:process="com.android.cts.useprocess.withnet2" />
</processes>
환경설정 활동 구현
이 주제의 앞부분에 발췌된 매니페스트에서 확인할 수 있듯이 샘플 앱의 SettingsActivity 활동에는 ACTION_MANAGE_NETWORK_USAGE 작업용 인텐트 필터가 있습니다. SettingsActivity는 PreferenceActivity의 서브클래스입니다. 이 클래스는 사용자가 다음 사항을 지정할 수 있는 환경설정 화면(그림 1 참고)을 표시합니다.
- 각 XML 피드 항목에 관한 요약을 표시할지 또는 각 항목의 링크만 표시할지 설정
 - 네트워크 연결이 가능한 경우 XML 피드를 다운로드할지 또는 Wi-Fi를 사용할 수 있는 경우에만 다운로드할지 설정
 
 
그림 1. 환경설정 활동
다음은 SettingsActivity입니다. 이는 OnSharedPreferenceChangeListener를 구현합니다.
사용자가 환경설정을 변경하면 onSharedPreferenceChanged()를 실행하여 refreshDisplay를 참으로 설정합니다. 따라서, 사용자가 기본 활동으로 되돌아가면 화면이 새로고침됩니다.
Kotlin
class SettingsActivity : PreferenceActivity(), OnSharedPreferenceChangeListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences)
    }
    override fun onResume() {
        super.onResume()
        // Registers a listener whenever a key changes
        preferenceScreen?.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
    }
    override fun onPause() {
        super.onPause()
        // Unregisters the listener set in onResume().
        // It's best practice to unregister listeners when your app isn't using them to cut down on
        // unnecessary system overhead. You do this in onPause().
        preferenceScreen?.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
    }
    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true
    }
}
자바
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Loads the XML preferences file
        addPreferencesFromResource(R.xml.preferences);
    }
    @Override
    protected void onResume() {
        super.onResume();
        // Registers a listener whenever a key changes
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }
    @Override
    protected void onPause() {
        super.onPause();
       // Unregisters the listener set in onResume().
       // It's best practice to unregister listeners when your app isn't using them to cut down on
       // unnecessary system overhead. You do this in onPause().
       getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
    // When the user changes the preferences selection,
    // onSharedPreferenceChanged() restarts the main activity as a new
    // task. Sets the refreshDisplay flag to "true" to indicate that
    // the main activity should update its display.
    // The main activity queries the PreferenceManager to get the latest settings.
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // Sets refreshDisplay to true so that when the user returns to the main
        // activity, the display refreshes to reflect the new settings.
        NetworkActivity.refreshDisplay = true;
    }
}
환경설정 변경에 응답
사용자가 설정 화면에서 환경설정을 변경하면 일반적으로 앱의 동작에 영향을 미칩니다. 이 스니펫에서 앱은 onStart()의 환경설정을 확인합니다. 설정과 기기의 네트워크 연결이 일치하는 경우(예: 설정이 "Wi-Fi"이고 기기에 Wi-Fi 연결이 있는 경우) 앱에서 피드를 다운로드하고 화면을 새로고침합니다.
Kotlin
class NetworkActivity : Activity() {
    // The BroadcastReceiver that tracks network connectivity changes.
    private lateinit var receiver: NetworkReceiver
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Registers BroadcastReceiver to track network connection changes.
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        receiver = NetworkReceiver()
        this.registerReceiver(receiver, filter)
    }
    public override fun onDestroy() {
        super.onDestroy()
        // Unregisters BroadcastReceiver when app is destroyed.
        this.unregisterReceiver(receiver)
    }
    // Refreshes the display if the network connection and the
    // pref settings allow it.
    public override fun onStart() {
        super.onStart()
        // Gets the user's network preference settings
        val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi")
        updateConnectedFlags()
        if (refreshDisplay) {
            loadPage()
        }
    }
    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    fun updateConnectedFlags() {
        val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val activeInfo: NetworkInfo? = connMgr.activeNetworkInfo
        if (activeInfo?.isConnected == true) {
            wifiConnected = activeInfo.type == ConnectivityManager.TYPE_WIFI
            mobileConnected = activeInfo.type == ConnectivityManager.TYPE_MOBILE
        } else {
            wifiConnected = false
            mobileConnected = false
        }
    }
    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    fun loadPage() {
        if (sPref == ANY && (wifiConnected || mobileConnected) || sPref == WIFI && wifiConnected) {
            // AsyncTask subclass
            DownloadXmlTask().execute(URL)
        } else {
            showErrorPage()
        }
    }
    companion object {
        const val WIFI = "Wi-Fi"
        const val ANY = "Any"
        const val SO_URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest"
        // Whether there is a Wi-Fi connection.
        private var wifiConnected = false
        // Whether there is a mobile connection.
        private var mobileConnected = false
        // Whether the display should be refreshed.
        var refreshDisplay = true
        // The user's current network preference setting.
        var sPref: String? = null
    }
...
}
자바
public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort;=newest";
    // Whether there is a Wi-Fi connection.
    private static boolean wifiConnected = false;
    // Whether there is a mobile connection.
    private static boolean mobileConnected = false;
    // Whether the display should be refreshed.
    public static boolean refreshDisplay = true;
    // The user's current network preference setting.
    public static String sPref = null;
    // The BroadcastReceiver that tracks network connectivity changes.
    private NetworkReceiver receiver = new NetworkReceiver();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Registers BroadcastReceiver to track network connection changes.
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        receiver = new NetworkReceiver();
        this.registerReceiver(receiver, filter);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        // Unregisters BroadcastReceiver when app is destroyed.
        if (receiver != null) {
            this.unregisterReceiver(receiver);
        }
    }
    // Refreshes the display if the network connection and the
    // pref settings allow it.
    @Override
    public void onStart () {
        super.onStart();
        // Gets the user's network preference settings
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        // Retrieves a string value for the preferences. The second parameter
        // is the default value to use if a preference value is not found.
        sPref = sharedPrefs.getString("listPref", "Wi-Fi");
        updateConnectedFlags();
        if(refreshDisplay){
            loadPage();
        }
    }
    // Checks the network connection and sets the wifiConnected and mobileConnected
    // variables accordingly.
    public void updateConnectedFlags() {
        ConnectivityManager connMgr = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
        if (activeInfo != null && activeInfo.isConnected()) {
            wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
            mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
        } else {
            wifiConnected = false;
            mobileConnected = false;
        }
    }
    // Uses AsyncTask subclass to download the XML feed from stackoverflow.com.
    public void loadPage() {
        if (((sPref.equals(ANY)) && (wifiConnected || mobileConnected))
                || ((sPref.equals(WIFI)) && (wifiConnected))) {
            // AsyncTask subclass
            new DownloadXmlTask().execute(URL);
        } else {
            showErrorPage();
        }
    }
...
}
연결 변경 감지
마지막으로 필요한 부분은 BroadcastReceiver의 서브클래스인 NetworkReceiver입니다. 기기의 네트워크 연결이 변경되면 NetworkReceiver에서 CONNECTIVITY_ACTION 작업을 가로채고 네트워크 연결 상태가 무엇인지 확인한 다음, wifiConnected와 mobileConnected 플래그를 참/거짓으로 적절하게 설정합니다. 그러면 다음에 사용자가 앱으로 돌아올 때 앱이 최신 피드만 다운로드하고 NetworkActivity.refreshDisplay가 true로 설정된 경우 화면을 업데이트합니다.
불필요하게 호출되는 BroadcastReceiver를 설정하면 시스템 리소스가 소모될 수 있습니다. 샘플 애플리케이션은 onCreate()에서 BroadcastReceiver NetworkReceiver를 등록하고 onDestroy()에서 등록을 취소합니다. 이 방법은 매니페스트에서 <receiver>를 선언하는 것보다 가볍습니다. 매니페스트에서 <receiver>를 선언하면 몇 주간 앱을 실행하지 않았어도 언제든지 대기 상태를 해제할 수 있습니다. 기본 활동 내에서 NetworkReceiver를 등록하고 등록 취소하면 사용자가 앱을 닫은 후 앱이 대기 상태에서 전환되지 않습니다. 매니페스트에서 <receiver>를 선언하고 필요한 위치를 정확하게 알고 있는 경우 setComponentEnabledSetting()을 사용하여 적절하게 사용 설정하거나 사용 중지할 수 있습니다.
다음은 NetworkReceiver입니다.
Kotlin
class NetworkReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val conn = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkInfo: NetworkInfo? = conn.activeNetworkInfo
        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI == sPref && networkInfo?.type == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show()
            // If the setting is ANY network and there is a network connection
            // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY == sPref && networkInfo != null) {
            refreshDisplay = true
            // Otherwise, the app can't download content--either because there is no network
            // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
            // is no Wi-Fi connection.
            // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show()
        }
    }
}
Java
public class NetworkReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager conn =  (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = conn.getActiveNetworkInfo();
        // Checks the user prefs and the network connection. Based on the result, decides whether
        // to refresh the display or keep the current display.
        // If the userpref is Wi-Fi only, checks to see if the device has a Wi-Fi connection.
        if (WIFI.equals(sPref) && networkInfo != null
            && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            // If device has its Wi-Fi connection, sets refreshDisplay
            // to true. This causes the display to be refreshed when the user
            // returns to the app.
            refreshDisplay = true;
            Toast.makeText(context, R.string.wifi_connected, Toast.LENGTH_SHORT).show();
        // If the setting is ANY network and there is a network connection
        // (which by process of elimination would be mobile), sets refreshDisplay to true.
        } else if (ANY.equals(sPref) && networkInfo != null) {
            refreshDisplay = true;
        // Otherwise, the app can't download content--either because there is no network
        // connection (mobile or Wi-Fi), or because the pref setting is WIFI, and there
        // is no Wi-Fi connection.
        // Sets refreshDisplay to false.
        } else {
            refreshDisplay = false;
            Toast.makeText(context, R.string.lost_connection, Toast.LENGTH_SHORT).show();
        }
    }
}