마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

몇 번의 질문 끝에 도저히 해결이 안되서 염치없이 코드를 들고왔습니다.

0 추천

UDP 브로드캐스팅을 통한 근거리 데이터 교환 앱을 짜고 있습니다.

소켓 부분은 서비스 클래스를 만들어서 따로 스레드를 구현하여 돌리고 있고,

메인 액티비티에서는 스레드 시작과 종료, UI 부분만 담당하고 있습니다.

 

두 장치 이상이 서로가 브로드캐스팅하면서 데이터를 주고 받는 것이 정상인데,

이게 화면이 켜져 있을 땐(Sleep 상태가 아닐 때) 정상적으로 데이터를 교환하는데,

화면이 꺼지면(Sleep 상태면) send만 되고 receive는 되지 않는 현상이 발생합니다.

 

온갖 실험과 반복과 질문을 통해서도 해결이 안되기에 안드로이드 API 자체의 오류가 아닌가 의심이 될 정도입니다.

 

메인 액티비티의 코드는 아래 안펍 게시물을 확인해주세요.

http://www.androidpub.com/2562377

본 게시판은 글자수 초과가 있네요. 아무래도 코드가져오기 금지 인것 같습니다. 죄송합니다.

아래는 서비스 부분 클래스입니다.

 

public class LocalService extends Service {

	WifiManager.WifiLock wifiLock = null;
	PowerManager.WakeLock wakeLock = null;
	
	static DatagramSocket socket;
	
	MessageReciver messageReceiver;
	MessageSender messageSender;
	private volatile boolean keepRunning = true;

	public static String SERVER_IP = "255.255.255.255";
	public static int SERVER_PORT = 15464;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
    	try {	
		if (wifiLock == null) { // 와이파이락 부분
		    WifiManager wifiManager = (WifiManager) this.getSystemService(this.WIFI_SERVICE);
		    wifiLock = wifiManager.createWifiLock("wifilock");
		    MulticastLock lock = wifiManager.createMulticastLock("multicast");
		    lock.acquire();
		    wifiLock.setReferenceCounted(true);
		    wifiLock.acquire();
		}
		if (wakeLock == null) { // 웨이크락 부분
		    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
		    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "wakelock");
		    wakeLock.acquire();
		}
		
		// UDP 소켓 생성, 브로드캐스트 모드
		socket = new DatagramSocket(SERVER_PORT);
		socket.setBroadcast(true);
		
		// 스레드 객체 생성, UDP 패킷 수신과 송신
		messageReceiver = new MessageReciver();
		messageSender = new MessageSender();
    	}
		catch (Exception e) {
	        Log.e("SOCKET", e.getMessage()); 
			e.printStackTrace();
		}
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService", "Received start id " + startId + ": " + intent);
        // 스레드 시작
		messageReceiver.start();
		messageSender.start();
		
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
    	// 스레드 중단
		keepRunning = false;
		messageReceiver.interrupt();
		messageSender.interrupt();

		// 와이파이락, 웨이크락 종료
		if (wifiLock != null) {
		    wifiLock.release();
		    wifiLock = null;
		}
		if (wakeLock != null) {
		    wakeLock.release();
		    wakeLock = null;
		}
		messageReceiver = null;
		messageSender = null;

		// 소켓 닫기
		socket.close();
		socket = null;
		System.gc();
    }

    // 메인 액티비티와 연결하기 위핸 수신 핸들러
	Handler handler = new Handler() {
		public void handleMessage(Message message) {
			super.handleMessage(message);
			// 받은 패킷의 아이피의 마지막 주소를 메인 액티비티에 전송
			String[] addr = ((String)message.obj).split("\\.");
			MainActivity.recive(Integer.parseInt(addr[3]));
		}
	};

	// 패킷 수신 스레드
	public class MessageReciver extends Thread {
		public void run() {
			try {
				while (keepRunning) {
					// 패킷 수신
					byte[] buffer = new byte[1000];
					DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
					socket.receive(packet); // ** 스레드가 블락되는 메소드 ** 이 메소드 도중에 프로그램이 종료되면 예상치않은 오류 메시지가 뜸 **
					
					// 패킷의 데이터는 버리고, 아이피주소만 핸들러로 전송
					Message message = handler.obtainMessage(1, packet.getAddress().getHostAddress());
					handler.sendMessage(message);
				}
			} catch (IOException e) {
		        Log.e("recvTHREAD", e.getMessage()); 
				e.printStackTrace();
			}
		}
	}
	
	// 패킷 송신 스레드, 1초마다 반복 송신
	public class MessageSender extends Thread {
		public void run() {
			try {
				while (keepRunning) {
					// 더미 패킷을 1초마다 브로드캐스트 송신
					String text = new String("signal");
					DatagramPacket packet = new DatagramPacket(text.getBytes(), text.getBytes().length, InetAddress.getByName(SERVER_IP), SERVER_PORT);
					socket.send(packet);
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
		        Log.e("sendTHREAD", e.getMessage()); 
				e.printStackTrace();
			} catch (IOException e) {
		        Log.e("sendTHREAD", e.getMessage()); 
			}
		}
	}

}

 

이 프로그램을 실행하면

0

0

으로 시작해서, 자신것이 켜져 있는 상태에서는 위에 텍스트 숫자 0이 1씩 증가합니다.

이 상태에서 다른 안드로이드폰을 가져와서 같은 프로그램을 또 실행하면 아래쪽 숫자도 1씩 증가하게 됩니다.

하지만 잠시 슬립상태로 화면을 껏다가 다시 켜면, 위쪽 숫자만 증가되어 있고, 아래쪽 숫자는 멈춰있다가

다시 켜면 그때서야 올라가는 것을 확인할 수 있습니다. 함께 켠 다른 폰도 마찬가지고요.

즉, 슬립상태에서는 자신이 보낸 패킷만 읽을 수 있고, 외부 수신은 전혀 되지 않는다는 걸 알 수 있습니다.

와이파이락과 웨이크락도 걸려있는 상태도인데도 말이죠...

 

너무 어이가 없고 해결이 안되서 안펍 고수님들께 여쭤보고자 염치없이 코드를 들고왔습니다.

 

ifreeta (260 포인트) 님이 2013년 11월 9일 질문

2개의 답변

0 추천

1 pause에 서비스를 죽이는 코드가 있는지 확인

2. http://blog.daum.net/sodloq/2

보시면알게지만 님이짠 소스는 메모리부족할경우 다시실행하지않는 서비스임... 

 

3. 안드로이드 서비스는 언제든지 운영체제가 지맘대로 죽일수있다는거 명심하고 짜셔야함

 

건방진프로그래머 (26,630 포인트) 님이 2013년 11월 9일 답변
물론 처음에는 onStartCommand의 리턴값은 START_STICKY 였습니다.
제가 자꾸 껏다켜니까 혹시 앱이 중단되어서 서비스가 leak 되는거 아닐까 걱정되서 저렇게 해놓은거 뿐이고요. 어떻게하든 똑같습니다...
만약 서비스가 죽었다면 송신 스레드도 죽었어야 겠죠...
메인액티비티의 onPause 콜백을 말씀하시는 거라면
아에 오버라이드 하지도 않았습니다. 죽이는 것도 없고요...흑흑....
0 추천
게시글에 답변 달았어요..
익명사용자 님이 2013년 11월 11일 답변
...