Есть ли какой-либо механизм обратного вызова в android, когда есть данные, доступные для чтения в сокете
Ну, я знаком с программированием сокетов в среде c, iOS. Но теперь пытаюсь подключить мой Android и мой удаленный сервер через сокеты... В качестве запуска я написал простую серверную программу на C и запустил ее мой рабочий стол, который терпеливо ждет запроса на соединение, принимает соединение, затем ждет некоторую строку запроса и при получении строки запроса возвращает некоторую строку ответа, затем снова ждет следующего запроса и продолжится. Ну, вы поняли идею..
Пока
- Я установил соединение с моим Android и сервером.
- отправленные и полученные данные
И это мой клиентский код.
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:
Я сам не спрашивал о создании механизма обратного вызова. Я просто спрашивал, есть ли присутствующий слушатель, который уведомляет вас, когда есть данные для чтения, когда соединение отключилось. Таким образом, я могу исключить вышеупомянутый цикл while... Если никакого обратного вызова нет, я готов переместить этот сокет в другой поток, который перемещается вокруг и читает данные (которые я уже сделал)...
**
EDIT Bounty time again..
**
Ну, я 2 слабый старый разработчик android/java с хорошим 3,5-летним опытом разработки c/С++/object c за этим... Итак, я видел низкоуровневые родные сокеты (которые блокируются (настраиваются) при использовании read() и recv()) и apple CFSocket (который сам является сосетом C, но предоставляет оболочку для обеспечения механизма обратного вызова). Я не прошу о кодировании механизма обратного вызова здесь. (который я весь искренне готов к работе, если нет готового альтернативного подарка, но зачем снова изобретать колесо?)... Я начинаю понимать java как очень продвинутую и высокоуровневую платформу. Я думал, что должна быть какая-то библиотека обертки более высокого уровня, скрывающаяся вокруг.. Так я думаю о том, чтобы увеличить аудиторию в этой теме, начав щедрость.
Ответы
Ответ 1
В Java нет готового обратного вызова. Вы можете легко реализовать его, используя классы в пакете NIO. Вам в основном нужны Selector и SocketChannel. Если вы знакомы с выбором (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 не должно управлять несколькими подключениями в одном и том же потоке, может быть намного проще использовать блокировку ввода-вывода. Еще одна вещь, которую следует учитывать: если это необходимо для непрерывной работы/в течение длительного времени, вам нужно переместить ее в службу. Даже тогда ОС может убить его, если у него закончится нехватка ресурсов, поэтому будьте готовы к переподключениям.
НТН
Ответ 2
Вот что вы можете попробовать:
Определите обработчик в 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
}
}
});
Ответ 3
Если вы ищете образец приложения для Android, который использует службу для управления долгосрочными подключениями к фоновому фонду, проверьте ConnectBot:
http://code.google.com/p/connectbot/source/browse/
Он использует службу для управления терминальными соединениями. Тем не менее, это очень сложный набор кода, возможно, потребуется некоторое копирование, чтобы выяснить, какие бит вы хотите использовать.
Большинство людей подключаются к серверу через HTTP, и между ними существует взаимно однозначное сопоставление между запросами и обратными вызовами. Поэтому мы можем уйти с IntentService для обработки фоновых запросов и передать в ResultReceiver, чтобы заставить службу перезвонить в поток пользовательского интерфейса, когда запрос завершен/не выполнен:
http://developer.android.com/reference/android/os/ResultReceiver.html
Такая же общая техника должна применяться для длительных соединений сокетов, хотя вам, вероятно, придется выбирать другую форму обслуживания, чтобы вы могли самостоятельно управлять жизненным циклом. Но если вы это сделаете, вы должны быть в состоянии принять намерение с помощью ResultReceiver в качестве части запроса, поставить его в очередь для обработки и затем отправить() в ResultReceiver, чтобы получить обратный вызов в пользовательский интерфейс.