Quantcast
Channel: NumeroReal » android
Viewing all articles
Browse latest Browse all 12

Android, Socket

$
0
0

Socket

Nesse artigo vamos apresentar um App no Android, que envia dados para um servidor, utilizando Socket. Uma network socket é um endpoint de um fluxo de comunicação entre processos através de uma rede de computadores. Hoje em dia, a maioria da comunicação entre computadores é baseado no Protocolo de Internet; portanto, a maioria dos network sockets são Internet Sockets.

O que é Socket?

Um socket API é uma interface de programação de aplicativo (API), normalmente fornecido pelo sistema operacional, que permite que os programas de aplicação para controlar e usar soquetes de rede. APIs de soquete Internet são geralmente baseados na soquetes padrão Berkeley.

Um endereço de socket é a combinação de um endereço de IP e um número de porta, bem como uma extremidade de uma ligação telefônica é a combinação de um número de telefone e uma extensão particular. Com base nesse endereço, os internet sockets entregam pacotes de dados de entrada para o processo de aplicação apropriada ou segmento.

Onde Usar

A utilização do Socket é indicada para a comunicação entre pontos, onde um servidor que irá atender a N clientes, onde seu protocolo não possa ser um protocolo aberto usando interfaces no mercado, como por exemplo um WebService ou um sereviço de componentes remotos.

Socket no Android


Primeiramente será necessário construir 2 aplicações. Como vocês viram na definição existem aplicações Clientes e Servidores para compor o funcionamento deste canal de comunicação. Em nosso exemplo termos que fazer ambas as partes para podermos mostrar o funcionamento tanto do Servidor quanto do Cliente.

Características

Alguns pontos precisam ser levados em consideração:

  1. Toda conexão precisa ser feita em uma thread separada
  2. O Android não permite alterar componentes visuais em threads separadas
  3. O servidor tem que estar ouvindo em um IP que o Android possa conectar

Antes de abordarmos o código de fato e esses pontos, vamos criar primeiro o design da aplicação. O layout principal vai consistir em um campo que você pode informar o host e a porta, um campo de texto para você escrever os dados, um botão para enviar os dados e um label para exibir o status.

activity_main.xml

Isso pode ser contemplado com o seguinte código de layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
 
        <EditText
            android:id="@+id/txtHostPort"
            android:layout_width="236dp"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@+id/textView1"
            android:ems="10"
            android:hint="@string/host_port_hint" >
 
            <requestFocus />
        </EditText>
 
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:text="@string/host_port_label" />
    </RelativeLayout>
 
    <EditText
        android:id="@+id/txtValor"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.50"
        android:ems="10"
        android:inputType="textMultiLine" />
 
    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn_send" />
 
    <TextView
        android:id="@+id/txtStatus"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text=""
        android:textSize="20sp" />
 
</LinearLayout>

 

Também devemos dar permissão de conexão (internet) para o aplicativo. No AndroidManifest.xml adicione a seguinte permissão:

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

 Agora vamos ao primeiro ponto: “Toda conexão precisa ser feita em uma thread separada”. Para realizar esse feito vamos utilizar um AsyncTask. O AsyncTask é uma classe excelente para quando precisamos realizar tarefas separadas da thread principal de maneira assíncrona e precisamos também atualizar a interface com o usuário que está em outra thread. Essa classe possui um método chamado publishProgress. Ao chamar esse método ele automaticamente chama o método onProgressUpdate que pode ser utilizado para atualizar a interface com o usuário. Dessa maneira também solucionamos o ponto 2: “O Android não permite alterar componentes visuais em threads separadas”.

O processamento principal deve ser feito no método doInBackground. Com essas informações, implementei a seguinte AsyncTask:

package br.com.numeroreal.android.socketandroid;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
 
import android.os.AsyncTask;
import android.util.Log;
 
/**
 * Classe para envio de dados via socket
 *
 * @author Numeroreal
 *
 */
public abstract class SocketTask extends AsyncTask<String, String, Boolean> {
 
    private Socket socket;
    private InputStream is;
    private OutputStream os;
    private String host;
    private int port;
    private int timeout;
 
    /**
     * Construtor com host, porta e timeout
     *
     * @param host
     *            host para conexão
     * @param port
     *            porta para conexão
     * @param timeout
     *            timeout da conexão
     */
    public SocketTask(String host, int port, int timeout) {
        super();
        this.host = host;
        this.port = port;
        this.timeout = timeout;
    }
 
    /**
     * Envia dados adicionais se estiver conectado
     *
     * @param data
     *            dados adicionais
     * @throws IOException
     */
    public void sendData(String data) throws IOException {
        if (socket != null && socket.isConnected()) {
            os.write(data.getBytes());
        }
    }
 
    @Override
    protected Boolean doInBackground(String... params) {
        boolean result = false;
        try {
            SocketAddress sockaddr = new InetSocketAddress(host, port);
            socket = new Socket();
            socket.connect(sockaddr, timeout); // milisegundos
            if (socket.isConnected()) {
                publishProgress("CONNECTED");
                is = socket.getInputStream();
                os = socket.getOutputStream();
                for (String p : params) {
                    os.write(p.getBytes());
                }
                byte[] buff = new byte[2048];
                int buffData = is.read(buff, 0, 2048);
                while (buffData != -1) {
                    String response = new String(buff);
                    // Envia os dados
                    publishProgress(response);
                    buffData = is.read(buff, 0, 2048);
                }
            } else {
                publishProgress("CONNECT ERROR");
            }
        } catch (IOException e) {
            publishProgress("ERROR");
            Log.e("SocketAndroid", "Erro de entrada e saida", e);
            result = true;
        } catch (Exception e) {
            publishProgress("ERROR");
            Log.e("SocketAndroid", "Erro generico", e);
            result = true;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e) {
                Log.e("SocketAndroid", "Erro ao fechar conexao", e);
            }
        }
        return result;
    }
}

 O código realiza um conexão socket (linha 62, 63 e 64), envia os dados (linha 70) e envia a resposta para a tela na linha 77. Agora é só utilizá-la na Activity principal.

package br.com.numeroreal.android.socketandroid;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
 
public class MainActivity extends Activity {
    private Button btnSend;
    private TextView txtStatus;
    private TextView txtValor;
    private TextView txtHostPort;
    private SocketTask st;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnSend = (Button) findViewById(R.id.btnSend);
        txtStatus = (TextView) findViewById(R.id.txtStatus);
        txtValor = (TextView) findViewById(R.id.txtValor);
        txtHostPort = (TextView) findViewById(R.id.txtHostPort);
        btnSend.setOnClickListener(btnConnectListener);
    }
 
    private OnClickListener btnConnectListener = new OnClickListener() {
        public void onClick(View v) {
 
            // Recupera host e porta
            String hostPort = txtHostPort.getText().toString();
            int idxHost = hostPort.indexOf(":");
            final String host = hostPort.substring(0, idxHost);
            final String port = hostPort.substring(idxHost + 1);
 
            // Instancia a classe de conexão com socket
            st = new SocketTask(host, Integer.parseInt(port), 5000) {
                @Override
                protected void onProgressUpdate(String... progress) {
                    SimpleDateFormat sdf = new SimpleDateFormat(
                            "dd/MM/yyyy HH:mm:ss");
                    // Recupera o retorno
                    txtStatus.setText(sdf.format(new Date()) + " - "
                            + progress[0]);
                }
            };
 
            st.execute(txtValor.getText() == null ? "" : txtValor.getText()
                    .toString()); // Envia os dado
        }
    };
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        st.cancel(true);
    }
}

 

O primeiro passo é instanciar a SocketTask (linha 41) sobreescrever o método onProgressUpdate (linha 43) e invocar o método execute (linha 52), que é responsável por colocar a thread nova em execução.

Após isso, precisamos resolver o ponto 3: “O servidor tem que estar ouvindo em um IP que o Android possa conectar”. Inicie o aplicativo desse post utilizando um IP que esteja na mesma rede do Android ao invés de 127.0.0.1. Execute a aplicação Android, informe o IP e a porta utilizados para iniciar o servidor, escreva um texto e clique no botão “Enviar Dados”. O servidor irá receber os dados e gravar em um arquivo.

Bom pessoal no nosso próximo artigo estaremos apresentando o Cliente para este servidor.

The post Android, Socket appeared first on NumeroReal.


Viewing all articles
Browse latest Browse all 12