Есть ли механизм обратного вызова в Android, когда есть данные, доступные для чтения в сокете - PullRequest
14 голосов
/ 30 июля 2011

Ну, я знаком с программированием сокетов в среде c, iOS. Но сейчас пытаюсь соединить мой android и мой удаленный сервер через сокеты ... В качестве запуска я написал простую серверную программу на C и запустил ее на мой рабочий стол, который терпеливо ждет запроса на соединение, принимает соединение, затем ждет какую-то строку запроса, и при получении строки запроса возвращает некоторую строку ответа, затем снова ждет следующий запрос и продолжает ... Ну, вы поняли идею ...

Пока

  1. Я установил соединение с моим android и сервером
  2. отправленные и полученные данные

А это мой код клиента ..

public class SocketMaster {
    private Socket clientSocket     =   null;
        BufferedReader socketReadStream =   null;
    public boolean connectToHost(String ip, int port){
        try {
            InetAddress serverAddr  =   InetAddress.getByName(ip);
            try {
                clientSocket        =   new Socket(serverAddr, port);
                socketReadStream    =   new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                String line     =   null;
                String stringToSend =   "This is from client ...Are you there???";

                //Write to server..stringToSend is the request string..
                this.writeToServer(stringToSend);

                //Now read the response..
                while((line = socketReadStream.readLine()) != null){
                    Log.d("Message", line);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public boolean writeToServer(String stringToSend){
        DataOutputStream dos        =   null;
        try {
            dos                         =   new DataOutputStream(clientSocket.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        try {
            dos.write(stringToSend.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

Я создаю объект SocketMaster из другого действия и вызывает connectToHost, передавая ip и порт сервера .. Все работает нормально, и я могу подключаться, отправлять и получать данные ..

Теперь мой вопрос касается этих строк

//Now read the response..
while((line = socketReadStream.readLine()) != null){
     Log.d("Message", line);
}

Из того, что я понимаю, поток, который выполняет этот код

  • блокируется, пока нет данных для чтения
  • если данные доступны, прочитайте их, потому что цикл снова блокируется до приходит следующий кусок данных

Просто я не могу запустить этот код в главном потоке, так как он заблокирует мои действия пользовательского интерфейса. Я могу подумать о запуске этого в фоновом потоке, который является одним из вариантов ...

Но в идеале я хочу ...

A callback (я думаю, слушатель в терминах Android, любой, кто знаком с iOS CFSocket и callback ), метод, который вызывается при наличии данных доступны для чтения, так что я могу просто прочитать данные и затем вернуться ..

Есть ли такой механизм в android / java ... Если нет (:-(), может кто-нибудь связать меня с примером, где обработка сокетов выполняется в фоновом режиме ...

EDIT: Я не спрашивал о создании механизма обратного вызова сам ... Я просто спрашивал, есть ли присутствующий слушатель, который уведомляет вас, когда есть данные для чтения, когда соединение было разорвано ... Таким образом, я могу устранить вышеупомянутое данное в то время как цикл ... Если обратного вызова нет, я готов переместить этот сокет в другой поток, который зацикливается и читает данные (что я уже сделал) ...

**

РЕДАКТИРОВАТЬ Щедрость снова ..

**

Ну, я 2 слабых старых android / java разработчика с хорошим 3,5-летним опытом разработки c / c ++ / target c за этим ... Так что я видел низкоуровневые нативные сокеты c (которые блокируют (настраиваются) при использовании read () и recv ()) и Apple CFSocket (который сам по себе является C-сокетом, но предоставляет обертку для обеспечения механизма обратного вызова) .. Я не спрашиваю о кодировании механизма обратного вызова самостоятельно здесь (что я от всей души) готов сделать, если нет готового альтернативного подарка, но зачем снова изобретать колесо?) ... Я начинаю понимать java как очень продвинутую и высокоуровневую платформу .. Я подумал, что должна быть какая-то библиотека-оболочка более высокого уровня, скрывающаяся вокруг. . Так что я думаю об увеличении аудитории в этой теме, начав вознаграждение ..

Ответы [ 3 ]

8 голосов
/ 05 августа 2011

Нет готового обратного вызова для этого в Java.Вы можете легко реализовать один, используя классы в пакете NIO.В основном вам нужны Selector и SocketChannel.Если вы знакомы с select (2), должно быть легко следовать.Если нет, попробуйте учебник NIO.

http://download.oracle.com/javase/1.5.0/docs/api/java/nio/channels/SocketChannel.html http://download.oracle.com/javase/1.5.0/docs/api/java/nio/channels/Selector.html

В любом случае вам придется опросить сокет, чтобы проверить, есть ли данные, а вы не можете сделатьэто в главном потоке.Если ваше приложение для Android не должно управлять несколькими соединениями в одном потоке, может быть гораздо проще использовать блокировку ввода-вывода.Еще одна вещь для рассмотрения: если это должно работать непрерывно / в течение длительного времени, вам нужно переместить его в службу.Даже в этом случае операционная система может уничтожить ее, если на ней не хватит ресурсов, поэтому будьте готовы обрабатывать повторные подключения.

HTH

1 голос
/ 31 июля 2011

Если вы ищете образец приложения Android, которое использует службу для управления фоновыми долгосрочными соединениями, проверьте ConnectBot:

http://code.google.com/p/connectbot/source/browse/

Служба используется для управления терминальными подключениями. Однако это очень сложный набор кода, возможно, потребуется немного покопаться, чтобы выяснить, какие биты вы хотите использовать.

Большинство людей подключаются к серверу через HTTP, и между контекстами запроса и обратными вызовами существует взаимно-однозначное сопоставление. Таким образом, мы можем использовать IntentService для обработки запросов в фоновом режиме и передать ResultReceiver, чтобы служба перезвонила потоку пользовательского интерфейса при завершении / сбое запроса:

http://developer.android.com/reference/android/os/ResultReceiver.html

Тот же самый общий метод должен применяться к длительным соединениям с сокетами, хотя вам, вероятно, придется выбрать другую форму обслуживания, чтобы вы могли управлять жизненным циклом самостоятельно. Но если вы это сделаете, вы должны быть в состоянии принять намерение с ResultReceiver как часть запроса, поставить его в очередь для обработки, а затем отправить () на ResultReceiver, чтобы передать обратный вызов в пользовательский интерфейс.

1 голос
/ 30 июля 2011

Вот что вы можете попробовать:

Определите обработчик в Java, как это

public interface NwkCallback
{
    void handle(int code, Object obj);
}

Теперь определите класс, который обрабатывает сетевые операции:

public class Nwk
{
    static DefaultHttpClient    CLIENT  = null;
    static DefaultHttpClient c()
    {
        if (null == CLIENT)
        {
        // Create a httpclient
        }
        else
            return CLIENT;
    }
}
public static void onReceive(final Object data, final NwkCallback callback)
{
    background(new Runnable()
    {
        @Override
        public void run()
        {
            try
            {
                // Write to server and wait for a response
                // Once you receive a response
                // return result back using
                // callback.handle(1,whatever_server_returned)
            }
            catch (Exception e)
            {
                Logger.e(e);
            }
        }
    }
});

В вашем потоке пользовательского интерфейса или в другом месте используйте метод onReceive, как

Nwk.onReceive(string_passed_to_server, new NwkCallback()
{
    @Override
    public void handle(final int code, Object obj)
    {
        if(code==1)
        {
            // Print obj
        }
    }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...