2011년 12월 11일 일요일

3G, WIFI & CONNECTIVITY_ACTION receiver

현재 진행하고 있는 안드로이드 프로젝트... 원래는 내가 해야할 일은 아닌 것 같은데 소방수로 투여되었다.

문제는 일전에 말했던 "차트" 와 "네트워크 안정화"...
당연히 있을 줄 알고 미친듯이 테스트 하면서 왜 안되지 하는 삽질 끝에..

소스를 쭉.. 살펴보니  CONNECTIVITY_ACTION 에 대한 Receiver가 없었다.

모르는 사람들을 위해 간략히 설명하자면 "네트워크 상태에 대한 실시간 감시" 라고나 할까...
사용법에 대해서는 CONNECTIVITY_ACTION, Receiver 등으로 구글에서 검색하면 미친듯이 나오기 때문에 따로 설명하지는 않고 소스만 예제 첨부 하겠다. 아래 예제는 네트워크가 끊어짐을 감지하는 소스이다.

public class ConnectionBroadcastReceiver extends BroadcastReceiver
{
    private static final int STATE_NONE = 0;
    private static final int STATE_WIFI_CONNECTED = 1;
    private static final int STATE_MOBILE_CONNECTED = 2;
    private int state = STATE_NONE;

    @Override
    public void onReceive(Context context, Intent intent) 
    {
        try
        {
            String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
            {
                ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo niWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
                NetworkInfo niMobile = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
                if (state == STATE_NONE)
                {
                    if (niWifi.getState() == State.CONNECTED)
                    {
                        state = STATE_WIFI_CONNECTED;
                    }
                    else if (niMobile.getState() == State.CONNECTED)
                    {
                        state = STATE_MOBILE_CONNECTED;
                    }
                }
                else if (state == STATE_WIFI_CONNECTED)
                {
                    if (niWifi.getState() == State.DISCONNECTED || niWifi.getState() == State.DISCONNECTING)
                    {
                        state = STATE_NONE;
                        Log.d("CBR", "Wi-Fi 끊어졌다!");
                    }
                }
                else if (state == STATE_MOBILE_CONNECTED)
                {
                    if (niMobile.getState() == State.DISCONNECTED || niMobile.getState() == State.DISCONNECTING)
                    {
                        state = STATE_NONE;
                        Log.d("CBR", "3G/4G 끊어졌다!");
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

위 소스를 보면 Log에서 먼가 처리를 하는 이벤트를 날리면 되는데... 만약 앱 내부에서 네트워크 수신을 쓰레드에서 계속 감시한다고 하면....

int len = is.read(...);

이라는 함수에서 블럭이 걸릴텐데... 유닉스든 뭐든 네트워크가 끊어지면 일반적으로 위 함수에서 -1을 리턴한다. 근데... Wi-Fi와 모바일 네트워크 간에 작동이 좀 틀리다.

Wi-Fi는 문제가 생기면 거의 바로 -1을 리턴해주는데, 모바일 네트워크는 지가 죽어버린지 모르는 것이다... 그래서 위와 같은 리시버를 이용하여 네트워크 상태를 감지하고 강제 접속 종료를 하는데... 문제가 하나 더 있다. Wi-Fi일 경우에는 리시버 이벤트 보다 소켓에서 더 먼저 -1 반응이 나온다... 그리고 모바일 네트워크는 지가 죽은지 모르기 때문에 당연히 리시버가 먼저 떨어진다. 그래서... 한곳에서 처리를 못하고 결국은.... 모바일 네트워크가 끊겼을 때만 리시버에서 네트워크를 강제로 끊어주게 했다.

정리를 하자면

1. Wi-Fi는 소켓에서 먼저 반응을 하므로 쓰레드의 read 함수에서 접속 종료 처리
2. 3G/4G등의 모바일 네트워크는 소켓에서 반응이 없으므로 CONNECTIVITY_ACTION 리시버에서 처리...

정말.. 모바일 네트워크는... 10년동안 하지만 짜증이 난다.. 특히 Wi-Fi가 들어가고 스마트폰이 나오고 고객의 요구사항이 늘어갈수록...
이벤트 등록과 제거.. 그리고 Manifest에서 처리해야 할것들도 보너스로 올려본다..(사실은 내가 나중에 소스 안뒤지고 그냥 블로그에서 카피하려고 쓰는거지만..)

// 이벤트 등록
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
ConnectionBroadcastReceiver cbr = new ConnectionBroadcastReceiver();
registerReceiver(cbr, filter);

// 이벤트 제거
unregisterReceiver(cbr);

<!-- Manifest에 등록할 내용 -->
<application .... >

    <receiver android:name=".ConnectionBroadcastReceiver">
        <intent-filter>
            <action android:name="CONNECTIVITY_CHANGE"/>
        </intent-filter>
    </receiver>

</application>




댓글 없음: