Skip to content

Instantly share code, notes, and snippets.

@kob-to-wni
Created July 14, 2016 10:55
Show Gist options
  • Save kob-to-wni/1c2253c2674e01092fb07e7eccf4a1f4 to your computer and use it in GitHub Desktop.
Save kob-to-wni/1c2253c2674e01092fb07e7eccf4a1f4 to your computer and use it in GitHub Desktop.
UdpListenerService.java
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresPermission;
import android.util.Log;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* UDPでデータを受信するサービス
*
* @author kob-to
*/
public class UdpListenerService extends Service {
/**
* デバッグ用タグ
*/
private static final String TAG = UdpListenerService.class.getSimpleName();
/**
* UDPデータを受信した際のハンドラ
*/
public static interface DatagramPacketReceivedHandler {
/**
* UDPデータを受信した際に呼ばれます
* @param packet UDPデータ
*/
void onDatagramPacketReceived(DatagramPacket packet);
}
/**
* UdpListenerServiceのBinder
*/
public static class Binder extends android.os.Binder {
/**
* UdpListenerServiceのインスタンス
*/
private final UdpListenerService mService;
/**
* UdpListenerServiceのインスタンスを指定してBinderを初期化します
* @param service UdpListenerServiceのインスタンス
*/
private Binder(UdpListenerService service) {
mService = service;
}
/**
* UdpListenerServiceのインスタンスを取得します
* @return
*/
public UdpListenerService getService() {
return mService;
}
}
/**
* UDPの待ち受けポート
*/
private static int port = 0;
/**
* UDPの割当ポート
*/
private static int boundPort = 0;
/**
* 待ち受け状態
*/
private static boolean listening = false;
/**
* 受信バッファサイズ
*/
private static int bufferSize = 1024;
/**
* ハンドラ
*/
private final List<DatagramPacketReceivedHandler> mHandlerList = new CopyOnWriteArrayList<>();
/**
* イベント発生用
*/
private Handler mHandler;
/**
* UDPソケット
*/
private DatagramSocket mSocket = null;
/**
* 受信スレッド
*/
private Thread mListeningThread = null;
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(getMainLooper());
}
@Override
public void onDestroy() {
super.onDestroy();
if (listening) {
// サービス破棄時には止める
stop();
}
}
@Override
public @Nullable IBinder onBind(final Intent intent) {
return new Binder(this);
}
/**
* UDPの受信を開始します
*/
@RequiresPermission("android.permission.INTERNET")
public synchronized void start() {
if (mListeningThread != null) {
throw new IllegalStateException("UdpListener was already started.");
}
if (BuildConfig.DEBUG) {
Log.i(TAG, "Starting UdpListener...");
}
listening = true;
mListeningThread = new Thread(new Runnable() {
@Override
public void run() {
try {
mSocket = new DatagramSocket(port);
} catch (SocketException e) {
if (BuildConfig.DEBUG) {
Log.e(TAG, e.getMessage(), e);
}
throw new RuntimeException(e);
}
boundPort = mSocket.getLocalPort();
if (BuildConfig.DEBUG) {
Log.i(TAG, "UdpListener bounded to " + boundPort + " successfully. Receiving UDP packet...");
}
byte[] buff = new byte[bufferSize];
DatagramPacket packet = new DatagramPacket(buff, bufferSize);
try {
while (listening) {
mSocket.receive(packet);
synchronized (mHandlerList) {
for (final DatagramPacketReceivedHandler handler : mHandlerList) {
if (handler == null) {
continue;
}
byte[] cloneBuffer = Arrays.copyOf(buff, packet.getLength());
final DatagramPacket clone = new DatagramPacket(cloneBuffer, cloneBuffer.length);
clone.setAddress(packet.getAddress());
clone.setPort(packet.getPort());
clone.setSocketAddress(packet.getSocketAddress());
mHandler.post(new Runnable() {
@Override
public void run() {
handler.onDatagramPacketReceived(clone);
}
});
}
}
}
} catch (IOException e) {
if (e instanceof SocketException) {
if (BuildConfig.DEBUG) {
Log.w(TAG, e.getMessage(), e);
}
} else {
if (BuildConfig.DEBUG) {
Log.e(TAG, e.getMessage(), e);
}
}
listening = false;
} finally {
if (BuildConfig.DEBUG) {
Log.i(TAG, "Receive UDP packet finished.");
}
if (mSocket != null && !mSocket.isClosed()) {
mSocket.close();
}
}
}
});
mListeningThread.start();
}
/**
* UDPの受信を停止します
*/
public synchronized void stop() {
if (mListeningThread == null) {
throw new IllegalStateException("UdpListener has not been started.");
}
if (BuildConfig.DEBUG) {
Log.i(TAG, "Stopping UdpListener...");
}
listening = false;
mSocket.close();
try {
mListeningThread.join();
} catch (InterruptedException ignored) {}
mListeningThread = null;
}
/**
* ハンドラを追加します
* @param handler ハンドラのインスタンス
*/
public void registerHandler(final DatagramPacketReceivedHandler handler) {
if (handler == null) {
throw new IllegalArgumentException("handler must not be null");
}
mHandlerList.add(handler);
}
/**
* ハンドラを削除します
* @param handler ハンドラのインスタンス
*/
public void unregisterHandler(final DatagramPacketReceivedHandler handler) {
if (handler == null) {
throw new IllegalArgumentException("handler must not be null");
}
mHandlerList.remove(handler);
}
/**
* 割り当てられたポートを取得します
* @return 自動割り当てなら割り当てられたポート番号、それ以外は指定されたポート番号
*/
public static int getPort() {
return port == 0 ? boundPort : port;
}
/**
* 待ち受けポートを設定します
* @param value 0〜65535以下のポート番号。0は自動割り当て。
*/
public static void setPort(final int value) {
if (value < 0 || 65535 < value) {
throw new IllegalArgumentException("Port must be between 0-65535");
}
if (listening) {
throw new UnsupportedOperationException("Can't change port while UDP listening");
}
port = value;
}
/**
* 受信バッファのサイズを取得します
* @return 受信バッファのサイズ
*/
public static int getBufferSize() {
return bufferSize;
}
/**
* 受信バッファのサイズを設定します
* @param value 受信バッファのサイズ
*/
public static void setBufferSize(final int value) {
if (value < 1) {
throw new IllegalArgumentException("Buffer size must be >1");
}
if (listening) {
throw new UnsupportedOperationException("Can't change buffer size while UDP listening");
}
bufferSize = value;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment