Monthly Archives: March 2016

Простой сетевой код под Android

Когда я работал над своим проектом управляемой лампочки, я решил, что нужно как в лучших домах Парижу и Лондону сделать приложение под Android которое бы задавало цвета для светодиодов, но то с чем я столкнулся просто сбило меня столку. Те тонны приложений, доступные в Андроид Маркете, как бы намекают, что разработка под Android дело простое и не хитрое, однако у меня разработка приложения для того, чтобы банально отправить строчку с цветами для светодиодов, затянулось на недели. Вот оно:

Нужно просто убиться, чтобы добиться такого результата, если ты этого раньше не делал… Но обо всём по порядку.

Итак, Задача. Есть некий сервис, который сидит и ждёт команду… строчку. Известен IP и порт. Клиент например на perl-е тривиален:

use strict;

use IO::Socket::INET;

my $sock = new IO::Socket::INET(PeerAddr => '192.168.1.48',
                                PeerPort => '6969',
                    Proto => 'tcp') || die "Can't bind $@\n";


$sock->send(shift."\n");

вопросов нет, он работает. Если запихнуть это дело в цикл то в примере с RGB светодиодной лампой можно наблюдать нечто вроде этого:

Итак, с чем же я столкнулся? Казалось бы что тут сложного, открываем google и ищем что-нить типа How to send tcp packet in Android. Первая же ссылка выглядит вполне прилично. Читаем внимательно. Ок, делаем простое приложение, не забывая о правах для этого самого приложения

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

и… оно крашится. Googl-им сообщение об ошибке и оказывается, сетевой код не может выполняться в основном треде и для него нужен AsyncTask, что в общем-то логично, но какой-нить Warning что ли не помешал бы.

Идём дальше. Скомпиляли, запустили на эмуляторе… ни хера не происходит. Кнопка жмякается, а серверная часть молчит… Толи этот AsyncTask ещё не выполнился, толи сервак не доступен толи хер знает чего.

Окей, сделал простой сервер на perl-е и запустил локально

use strict;

use IO::Socket::INET;

$| = 1;

my $soc = new IO::Socket::INET(LocalHost => '0.0.0.0',
    LocalPort => '6969',
    Proto => 'tcp',
    Listen => 5,
    Reuse => 1,);

die " Failed $!!" unless $soc;

while (1)
{
  my $client = $soc->accept();
  warn "accept: " .$client->peerhost();

  my $Data = "";
  $client->recv($Data, 1024);
  warn "$Data";
  shutdown($client,1);

}

$soc->close();

пробую подключится с эмулятора уже на perl-овый сервер, который работает на той же машине… и опять ничего. Что за… Опять гуглю и тут начинается: и Windows говно и не хватает прав и эмулятор говно… ладно… беру старый свой HTC телефон, опять начинают ставится Driver-а, вылезать HTC Sync Manager-ы, падать ADB, Android Studio то видит, то не видит телефон, то не может подключиться…

Через какое-то время, не знаю как, мне таки удалось запустить мое приложение на телефоне, и о чудо! Оно подключается к перловому серваку, но… ни хера не шлёт. В итоге, уже не помню как я к этому пришёл, заменил код, который отсылает строчку на отсылку байтов и вуаля – оно заработало как на видео в самом верху поста.

Итак, вот моя реализация. Делаем кнопку, OnClick которой собирает данные из разных текстовых полей (например IP-адресс), создает экземпляр AsyncTask и запускает его передавая туда IP и данные о цветах

            public void onClick(View v) {
                EditText etip = (EditText) findViewById(R.id.editText);
                EditText strData = (EditText) findViewById(R.id.testEditText);

                String ip = etip.getText().toString();
                String data = strData.getText().toString();

                ASColorSender asColorSender = new ASColorSender();
                asColorSender.execute(ip, data);
                final TextView tv = (TextView) findViewById(R.id.textView);
                tv.setText("done");
            }

и у нас имеется класс ASColorSender, который унаследован от AsyncTask и реализует один метод – doInBackground, который собственно и принимает переданные ему данные, устанавливает соединения с сервером и отсылает туда строчку с цветами:

public class ASColorSender extends AsyncTask<String,Void,Long> {
    @Override
    protected Long doInBackground(String... params) {
        String ip = params[0];
        String data = params[1];
        Socket socket = null;
        try {
            socket = new Socket(params[0].toString(), 6969);

            OutputStream out = socket.getOutputStream();
            out.write(params[1].getBytes());
            //PrintWriter output = new PrintWriter(out);

            //output.println("25;25;25;25;");//params[1]);

            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return new Long(1);
    }

отдельное “спасибо” авторам за extends AsyncTask<String,Void,Long>. Мало нам стандартных типов, давайте сделаем такие же только с блек джеком и шлюхами…

Резюмируя, видимо я не совместим с Java-ой или с разработкой под Android в принципе. Пока занимался этим проектом – пробовал некий Appcelerator, который должен сделать всё ещё проще и универсальнее, но это такой кусок говна, который ещё поискать надо. Смотришь в хелп, где написано, нажмите сюда и у вас появится пример приложения. Жмякаешь… и ничего не происходит. Там вообще много где кликаешь и ничего не происходит… Хоть бы какое сообщение типа чего не хватает или чего надо установить… но нет, просто грёбаное ничего…

Так к чему это я всё? Какой смысл в этом посте?

Если вдруг ты, случайный читатель, Java/Android девелопер и тебе вдруг нужен код, который отсылает TCP пакет, то пользуйся на здоровье. Вот.