Ocena serwera TCP - "pilot" do komputera przez androida

0

Cześć, jestem w trakcie pisania bardzo prostego systemu zarządzania moim złomem z poziomu androida przez TCP - mam stacjonarny komp, leżąc w nocy nie chce mi się ruszać zada żeby np. włączyć muzykę, zastopować film albo wyłączyć kompa. Póki co mam tylko serwer z dwiema prostymi komendami, tj. wyłącz komputer i zastopuj film na alekino. Apka siedzi w trayu, ikonka coś nie hula, ale tym się zajmę potem.

https://github.com/WizzieP/Pilot/tree/master/server

Używam Javy8, szczególnie zależy mi na ogarnięciu:

  1. Użycia referencji do metod (w HashMapie),

  2. Przy lambdzie w addActionListener chciałem zmieniać wartość zmiennej int portNumber, ale IDEA krzyczy, że zmienna musi być efektywnie finalna i zmieniła mi na finalną tablicę intów. Strasznie to dziwne, to znaczy, że coś robię źle.

  3. Wyciąganie argumentów z komendy, pewnie da się to zrobić jakoś lepiej (komenda zawsze wygląda tak: COMMAND arg1 arg2 ...):

String[] split = inputLine.split(" ");
String[] commandArgs = {};
if(split.length > 1)
    commandArgs = Arrays.copyOfRange(split, 1, split.length-1);
  1. Komendy jako statyczne metody klasy Commands to chyba dobre rozwiązanie?

PS. Zmiana portu aktualnie nic nie robi, tym się zajmę później :)

2

Z tym wysyłaniem komend, ja bym użył serializacji.
http://docs.oracle.com/javase/tutorial/jndi/objects/serial.html

Mógłbyś sobie stworzyć takie obiekty

class CommandShutdown{
  int timeout;
}

class CommandMovie{
  bool pause;
}

class CommandOpenChrome {
 // no arguments
}

W cliencie zamieniaj je sobie na bajty, wysyłaj , a potem na serverze

Object received = // odbierz je sobie;

if (received instanceOf CommandShutdown) {
  CommandShutdown command = (CommandShutdown)received;
  blueScreen(command.timeout);
}

if (received instanceOf CommandMovie) {
  CommandMovie command = (CommandMovie)received;
  movie.doSomthing(command.pause);
}

if (received instanceOf CommandChrome){
  chrome.doSomething();
}
1

Zgadzam się z @TomRiddle co do pierwszej części. Natomiast ja proponuję to rozłożyć jeszcze dalej.

class ShutdownCommandExecutor implements CommandExecutor<ShutdownCommand> {

    public void execute(ShutdownCommand parameter) {
        int tmp = parameter.timeout;
    }

    public Class<?> getCommandClass() {
        return ShutdownCommand.class;
    }

}

interface CommandExecutor<T extends Command> {
    void execute(T parameter);
    
    Class<?> getCommandClass();
}

class ShutdownCommand implements Command, Serializable {
    public int timeout;
}

interface Command {
}
  • Interfejsy wywalasz do modułu Common który jest widoczny w Androidze i PC.
  • Implementację Command wywalasz do osobnego moduł "ShutdownCommand" który używa jara Command. Dołączysz do Androida i PC.
  • Implementację ShutdownCommandExecutor który jest zależny ShutdownCommand tez wywalasz do osobnego jara i dołączasz tylko do PC.
  • Przy uruchomieniu aplikacji skanujesz jakiś jej folder w poszukiwaniu klas które implementują CommandExecutor i zpomocą metody getCommandClass tworzysz mapę klasaKomenty-executor.
  • Przy odebraniu zserializowanej komendy pobierasz jej klasę, na jej podstawie executora i masz wywołujesz jego działanie.

Zalety:

  • Całkowite odseparowanie logiki przesyłu od tego co faktycznie ma się wykonać. Dodanie/modyfikacja akcji nie wymaga zmian w serwerze (prócz dodania jarka do folderu).
  • Android i PC zawierają tylko te implementacje które są potrzebne (w executorach możesz śmiało używać javy7+).

Jak jeszcze bardziej pokombinujesz to może nawet przerobić aplikację tak, żeby commandy były pobierane z serwera, np. jako xml/json.

Oraz rozwiązanie bardziej alternatywne - po prostu użyć webserviców :).

2

To co napisał @TomRiddle (konkretnie druga wstawka z instanceof) ewidentnie łamie zasadę podstawienia:

http://en.wikipedia.org/wiki/Liskov_substitution_principle

W Javie możesz użyć pełnej serializacji obiektowej (ObjectInputStream) i serializować obiekty (będące jakimiś właśnie strukturami komend) w całości, klient w Javie bez problemu dogada się w ten sposób z serwerem w Javie.

0

@krzysiek050

Jak jeszcze bardziej pokombinujesz to może nawet przerobić aplikację tak, żeby commandy były pobierane z serwera, np. jako xml/json

Tak właśnie zamierzam zrobić, ale mam wątpliwości co formy wysyłania tego Potrzebuję wysłać także opis komendy i jakąś ludzką nazwę. Widzę takie opcje:

  1. Wysyłać z serwera osobno opis komendy w postaci jakiegoś jsona znajdującego się w jarze z komendą + zserializowana klasa przez ObjectInputStream, którą przy użyciu ClassLoadera wrzucę do classpatha na androidzie. Komendy z androida do serwera będę wysyłał jako obiekty przez ObjectOutputStream - IMO porąbane rozwiązanie, jakoś mi się to nie uśmiecha.

  2. Wysyłać z serwera jednego jsona z nazwą, opisem, nazwą klasy i parametrami komendy. W kliencie w takim przypadku nie potrzebuję żadnej specyficznych klas dla komend, tylko jedną klasę Command definiującą ogólną komendę, opis i parametry. Wtedy z klienta wysyłać tylko prostego jsona zawierającego nazwę klasy i parametry, a na serwerze mapować nazwę klasy do CommandExecutora. - chyba najlepsze i najprostsze rozwiązanie.

  3. Wariacja pierwszego rozwiązania: zamiast jsona z opisem i ludzką nazwą komendy wstawić pola statyczne do klasy komendy, np. public static final String DESC = "Opis komendy" i używać pełnej serializacji jak w pierwszym rozwiązaniu - chyba trochę mniej roboty jak w pierwszym, ale nadal coś nie tak.

Oczywiście oprócz prostoty i naturalności drugiego rozwiązania, plusem jest to, że mogę sobie potem rąbnąć klienta dla iOS, Windows Phone, tostera, lodówki i czajnika, a w pierwszym i drugim jestem uzależniony od serializacji obiektów Javowych.

W sumie pisząc to sam sobie chyba rozwiałem wątpliwości, ale liczę na jakieś pomocne słowa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1