오늘 포스팅할 내용은,
안드로이드 디바이스가 현재
인터넷을 사용할 수 있는 상태인지
또한 사용할 수 있는 상태라면
어떤 인터넷 환경인지 (Wifi or 3g ,4g ) 알아보는 방법에 대해 기술한다.
인터넷을 사용할 수 있는 상태인지를 체크하기 앞서,
인터넷이 연결되 있는 경우
어떤 인터넷 환경인지 가져오는 것부터 할 것이다.
먼저 AndoridManifest.xml 에
해당 퍼미션을 등록해준다.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
인터넷 환경을 탐색하기 위한 멤버변수를 선언 해준다.
public static final String WIFI_STATE = "WIFE";
public static final String MOBILE_STATE = "MOBILE";
public static final String NONE_STATE = "NONE";
그 후 아래의 메서드를 작성한다.
public static String getWhatKindOfNetwork(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
return WIFI_STATE;
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
return MOBILE_STATE;
}
}
return NONE_STATE;
}
핵심코드는 getActiveNetworkInfo( ) 메서드이다.
해당 메서드는 인터넷이 연결되있는 경우 인터넷 환경에 대한 여러가지 정보를 담은 객체를 리턴한다.
만약 인터넷 환경이 연결되있는 상태가 아니라면 null을 리턴한다.
위의 메서드를 사용할 시, 인터넷 환경이 wifi 상태인지 3g,4g 상태인지 알 수 있다.
이 메서드를 통해서 인터넷이 연결됬는지 확인 할 수도 있지만,
이 메서드만 사용한다면 캐치 못하는 경우의 수가 있다.
Wifi 연결은 되었지만 로그인, 인증서 등 권한을 획득하지 못하여 인터넷을 쓰지 못하는 경우.
위의 경우의 수에서는 사용자가 인터넷을 사용하진 못하지만 디바이스에서는 wifi가 연결된 걸로 인지한다.
이러한 경우의 수까지 캐치 하고 싶다면 아래의 코딩을 추가 해야한다.
1. Ping 보내기.
ping이란, 원하는 네트워크 주소에 신호를 보내, 응답이 도착하는가 확인하는 것이다.
만약 인터넷을 사용하지 못한다면, ping 신호는 도달 하지 못할 것이다.
안드로이드도 리눅스 기반 이므로, 리눅스 명령어를 통해 ping 을 보낼 수 있는데,
java 에서는 제공해주는 리눅스 api가 없기 때문에,
밑단까지 내려가 명령을 줘야한다. 아래의 코드를 보자.
private static class PingTask extends Thread {
private boolean success;
private String host;
public PingTask(String host) {
this.host = host;
}
@Override
public void run() {
super.run();
try {
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec("ping -c 1 -W 100" + host);
proc.waitFor();
int exitCode = proc.exitValue();
if (exitCode == 0) {
success = true;
} else {
success = false;
}
} catch (IOException e) {
e.printStackTrace();
success = false;
} catch (InterruptedException e) {
e.printStackTrace();
success = false;
}
}
public boolean isSuccess() {
return success;
}
}
윗 코드는 리눅스 터미널까지 내려가 리눅스 명령어를 내리는 코드이다.
해당 코드를 쓰면 인터넷의 사용 가능 상태를 체크 할 수 있다.
하지만, 문제점은 생각보다 느렸다.
백그라운드 스레드로 사용한다면 상관없겠지만,
만약 인터넷 사용 가능 상태를 미리 조사를 해야 하는 상황이라면,
사용자가 느끼기엔 느릴 것이다.
느린 원인은 잘 모르겠다. 추측성으론,
안드로이드 명령어가 아닌 시스템 명령어를 내리는 것이므로 오버헤드가 발생한것이 아닌가 생각한다.
대안책으로 아래의 코드를 보자.
2. HttpURLConnection 객체 이용하기.
HttpURLConnection객체를 응용해서 인터넷 사용 가능 상태를 검사할 것이다.
문제점은 HttpURLConnection이 연결되는지 안되는지 기준은 HTTP_OK 상태 이므로,
만약 인증받지 않은 와이파이 일지라도 HttpURLConnection은 연결상태로 뜰 것이다.
따라서 아래의 방법으로 해결한다.
HTTP_OK 상태로만 연결 상태를 체크하지 말고, HTTP_OK 상태 체크 후,
웹페이지의 원하는 내용을 긁어올 수 있는지 여부로 체크하기.
무슨 말 이냐면, 인터넷 사용 가능한 권한을 받지 못하는 경우도 connect 는 되지만 원하는 웹페에지의 내용은 안긁어와진다.
하지만 인터넷 사용 가능한 환경이 주어진다면, connect는 당연히 되고, 원하는 웹페이지의 내용도 긁어올수 있을것이다.
아래의 URL은 위의 논리를 구현하기 위해 구글이 제공하는 URL이다.
http://clients3.google.com/generate_204
해당 URL은 아무내용도 없는 즉, 인터넷이 연결된 상태에서 긁어올 내용을 넣지 않았다.
해당 URL은 HTTP_OK 를 넘어서, HTTP_NO_CONTENT ( 204번 ) 을 리턴한다.
이 URL을 사용 할 시, wifi 가 연결되었지만 권한이 없는경우는 200번을 리턴하고,
권한이 있는경우는 204번을 리턴한다.
아래의 코드를 보자.
멤버변수로 아래의 코드를 추가한다.
public static final String CONNECTION_CONFIRM_CLIENT_URL = "http://clients3.google.com/generate_204";
HttpURLConnection 객체를 호출하는 코드도 추가한다.
private static class CheckConnect extends Thread{
private boolean success;
private String host;
public CheckConnect(String host){
this.host = host;
}
@Override
public void run() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection)new URL(host).openConnection();
conn.setRequestProperty("User-Agent","Android");
conn.setConnectTimeout(1000);
conn.connect();
int responseCode = conn.getResponseCode();
if(responseCode == 204) success = true;
else success = false;
}
catch (Exception e) {
e.printStackTrace();
success = false;
}
if(conn != null){
conn.disconnect();
}
}
public boolean isSuccess(){
return success;
}
}
이제 인터넷 상태를 체크해주는 메서드도 만들어본다.
public static boolean isOnline() {
CheckConnect cc = new CheckConnect(CONNECTION_CONFIRM_CLIENT_URL);
cc.start();
try{
cc.join();
return cc.isSuccess();
}catch (Exception e){
e.printStackTrace();
}
return false;
}
필자는 인터넷 환경체크를 우선시 하므로
스레드에 join을 걸어 mainThread의 작업이랑 동기화 하였다.
만약 서브스레드로만 돌려도 되는 상황이라면 join을 지우면 되겠다.
이제 isOnline( ) 메서드와 getWhatKindOfNetwork( ) 메서드만 사용한다면,
인터넷 상태를 완벽하게 체크 할 수 있다.