ConnectivityService의 아키텍처와 동작 원리
Android 시스템에서 ConnectivityService는 네트워크 연결 상태를 중앙에서 관리하는 핵심 서비스로, 애플리케이션과 하드웨어 간의 네트워크 인터페이스를 조율한다. 이 서비스는 Wi-Fi, 셀룰러, 이더넷 등 다양한 네트워크 유형을 통합하여 관리하며, 네트워크 전환, 연결성 검증, 정책 적용 등의 기능을 수행한다.
서비스 초기화 과정
시스템 부팅 시 ConnectivityService는 Context와 여러 시스템 서비스(INetworkManagementService, INetworkStatsService 등)를 주입받아 초기화된다. 내부적으로는 전용 HandlerThread를 생성하여 비동기 작업을 처리하고, 네트워크 요청 및 에이전트 정보를 저장할 맵 구조를 준비한다.
public final class NetworkManagerService {
private final Context mContext;
private Handler mWorkerHandler;
private final SparseArray<ActiveNetwork> mNetworkRegistry;
private final List<NetworkRequestEntry> mPendingRequests;
public void systemReady() {
initializeDefaultNetworkRequest();
registerBroadcastReceivers();
startVpnControllers();
}
private void initializeDefaultNetworkRequest() {
final NetworkCapabilities defaultCaps = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_VPN)
.addCapability(NET_CAPABILITY_TRUSTED);
mDefaultNetworkRequest = new NetworkRequest(defaultCaps, TYPE_DEFAULT);
}
}
NetworkAgent와 네트워크 등록
각 네트워크 유형(예: WifiStateMachine, MobileDataController)은 자신의 상태를 반영하는 NetworkAgent를 생성하고 ConnectivityService에 등록한다. 등록 시 고유한 네트워크 식별자(NetId)가 할당되며, 해당 네트워크의 특성과 현재 상태가 시스템에 공유된다.
private void registerNetworkAgent(Messenger agentMessenger,
NetworkProperties props,
NetworkCapabilities caps,
int initialScore) {
final ActiveNetwork network = new ActiveNetwork(agentMessenger, props, caps);
mNetworkRegistry.put(network.netId, network);
evaluateAllPendingRequests();
}
private void evaluateAllPendingRequests() {
for (NetworkRequestEntry request : mPendingRequests) {
findBestSatisfyingNetwork(request);
}
}
네트워크 평가 및 우선순위 결정
시스템은 각 네트워크에 점수를 부여하여 최적의 연결 경로를 선택한다. 평가 요소로는 전송 유형, 신호 강도, 연결 유효성 여부, 사용자 설정 등을 포함한다. 예를 들어 이더넷은 기본 점수가 높고, Wi-Fi는 신호 세기에 따라 보너스 점수가 추가된다.
public class NetworkScorer {
public static int computeNetworkScore(NetworkCapabilities caps, boolean isValidated) {
int score = BASE_SCORE_NETWORK;
if (caps.hasTransport(TRANSPORT_ETHERNET)) {
score += 30;
} else if (caps.hasTransport(TRANSPORT_WIFI)) {
score += 20 + Math.min(caps.getSignalStrength(), 10);
} else if (caps.hasTransport(TRANSPORT_CELLULAR)) {
score += 15;
}
if (isValidated) score += 15;
if (caps.hasCapability(NET_CAPABILITY_FOREGROUND)) score += 5;
return Math.min(score, MAX_SCORE);
}
}
인터넷 연결성 검증 메커니즘
네트워크가 단순히 연결되어 있는지뿐 아니라 실제 인터넷 접근이 가능한지 확인하기 위해 HTTP 기반 연결성 테스트를 수행한다. 기본적으로 HTTPS를 사용한 HEAD 요청을 통해 응답 코드를 분석하며, 204 No Content 응답을 정상 연결로 판단한다.
public class ConnectivityProber {
private static final String PROBE_URL = "https://connectivitycheck.gstatic.com/generate_204";
public ProbeResult probe() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(PROBE_URL).openConnection();
conn.setInstanceFollowRedirects(false);
conn.setRequestMethod("HEAD");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
int code = conn.getResponseCode();
return code == 204 ?
ProbeResult.SUCCESS :
(code >= 200 && code < 400 ? ProbeResult.CAPTIVE_PORTAL : ProbeResult.FAILURE);
} catch (IOException e) {
return ProbeResult.TIMEOUT;
} finally {
if (conn != null) conn.disconnect();
}
}
}
애플리케이션 네트워크 요청 처리
앱에서 특정 네트워크 조건을 지정해 요청하면, 시스템은 이를 수신하고 조건을 만족하는 최적의 네트워크를 할당한다. 요청은 우선순위 기반으로 처리되며, 만료 시간(timeout)이 설정된 경우 자동 해제된다.
public Network requestNetworkSatisfying(NetworkRequest request,
INetworkCallback callback) {
final NetworkRequestEntry entry = new NetworkRequestEntry(request, callback);
mPendingRequests.add(entry);
final ActiveNetwork bestMatch = findBestNetworkForRequest(request);
if (bestMatch != null) {
notifyNetworkAvailable(callback, bestMatch.network);
return bestMatch.network;
}
scheduleTimeoutIfNecessary(entry);
return null;
}
실시간 네트워크 상태 모니터링
앱에서는 NetworkCallback을 등록하여 네트워크 상태 변화를 실시간으로 감지할 수 있다. 등록 시 원하는 네트워크 특성을 명시하면, 조건을 만족하는 네트워크가 활성화될 때 콜백이 호출된다.
public class LiveNetworkMonitor {
private ConnectivityManager cm;
private final NetworkCallback networkCallback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
Log.i(TAG, "활성 네트워크: " + network.toString());
updateUiConnected(true);
}
@Override
public void onLost(Network network) {
Log.w(TAG, "네트워크 연결 해제: " + network.toString());
updateUiConnected(false);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
boolean isValid = caps.hasCapability(NET_CAPABILITY_VALIDATED);
int rssi = caps.getSignalStrength();
Log.d(TAG, "연결 품질 - 유효성: " + isValid + ", 세기: " + rssi);
}
};
public void startMonitoring() {
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.build();
cm.registerNetworkCallback(request, networkCallback);
}
}
프로세스별 네트워크 바인딩
특정 네트워크(예: Wi-Fi 전용 API 서버 통신)에 프로세스를 고정하려면 bindProcessToNetwork()를 사용할 수 있다. 이 기능은 백그라운드 작업이나 민감한 데이터 전송 시 유용하다.
public void bindToWifiNetwork() {
Network wifiNetwork = findNetworkByTransport(TRANSPORT_WIFI);
if (wifiNetwork != null) {
cm.bindProcessToNetwork(wifiNetwork);
Log.d(TAG, "프로세스를 Wi-Fi 네트워크에 바인딩함");
}
}
성능 최적화 가이드
- 효율적인 감지: NetworkCallback 사용 권장 (정기轮询 불필요)
- 정확한 상태 확인: NET_CAPABILITY_VALIDATED 체크로 가짜 연결 방지
- 배치 처리: WorkManager를 사용해 네트워크 제약 조건 기반 작업 스케줄링
- 메모리 누수 방지: Activity 종료 시 반드시 unregisterNetworkCallback() 호출
// 올바른 예: 제약 조건 기반 백그라운드 작업
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED) // 비차감망 필요
.setRequiresBatteryNotLow(true)
.build();
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(DataSyncWorker.class)
.setConstraints(constraints)
.build();