Мое приложение для Android должно выполнять следующие действия:
MainActivity запускает другой поток в начале, называемый UdpListener, который может принимать вызовы UDP от удаленного сервера. Если он получает пакет с содержимым «ОБНОВЛЕНИЕ», UdpListener должен уведомить MainActivity, чтобы что-то сделать.
(В реальном приложении вариант использования выглядит так, что мое приложение прослушивает на удаленном сервере. Если на удаленном сервере есть какие-либо новые данные, оно уведомляет каждого клиента (приложение) по UDP, чтобы клиент знал что он может загрузить новые данные с помощью HTTP).
Я пытался смоделировать это в тесте JUnit. Тест содержит внутренний класс, который проверяет MainActivity, а также отправляет UDP-вызов UdpListener:
public class UdpListener extends Thread implements Subject {
private DatagramSocket serverSocket;
private DatagramPacket receivedPacket;
private boolean running = false;
private String sentence = "";
private Observer observer;
private static final String TAG = "UdpListener";
public UdpListener(Observer o) throws SocketException {
serverSocket = new DatagramSocket(9800);
setRunning(true);
observer = o;
}
@Override
public void run() {
setName(TAG);
while (isRunning()) {
byte[] receivedData = new byte[1024];
receivedPacket = new DatagramPacket(receivedData, receivedData.length);
try {
serverSocket.receive(receivedPacket);
}
catch (IOException e) {
Log.w(TAG, e.getMessage());
}
try {
sentence = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8");
if ("UPDATE".equals(sentence)) {
notifyObserver();
}
}
catch (UnsupportedEncodingException e) {
Log.w(TAG, e.getMessage());
}
}
}
private boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void notifyObserver() {
observer.update();
}
}
Это соответствующий тест:
@RunWith(RobolectricTestRunner.class)
public class UdpListenerTest {
private MainActivityMock mainActivityMock = new MainActivityMock();
@Before
public void setUp() throws Exception {
mainActivityMock.setUpdate(false);
}
@After
public void tearDown() throws Exception {
mainActivityMock.setUpdate(false);
}
@Test
public void canNotifyObserver() throws IOException, InterruptedException {
UdpListener udpListener = new UdpListener(mainActivityMock);
udpListener.setRunning(true);
udpListener.start();
InetAddress ipAddress = InetAddress.getByName("localhost");
DatagramSocket datagramSocket = new DatagramSocket();
DatagramPacket sendPacket = new DatagramPacket("UPDATE".getBytes(), "UPDATE".length(), ipAddress, 9800);
datagramSocket.send(sendPacket);
datagramSocket.close();
assertTrue(mainActivityMock.isUpdate());
udpListener.setRunning(false);
}
private class MainActivityMock implements Observer {
private boolean update = false;
@Override
public void update() {
update = true;
}
public boolean isUpdate() {
return update;
}
public void setUpdate(boolean update) {
this.update = update;
}
}
}
Хорошо, что моя концепция работает. Но этот тест не делает. Это означает, что это происходит только тогда, когда я останавливаюсь с точкой останова на этой строке datagramSocket.close();
и жду около секунды. Почему это происходит, понятно. Но как я могу сделать это автоматически? Я думал об использовании wait (), но для этого мне нужно вызвать notify () из другого потока. Та же проблема с CountDownLatch. Я не уверен, как решить эту проблему без изменения UdpListener.