Chat em tempo real com NodeJs e Android - Parte 2

Continuando a criação do chat em tempo real, vamos ver como utilizar a biblioteca Socket.io no Android.

Implementando no Android

Como na primeira parte, já configuramos o servidor (em NodeJs) para receber as conexões dos sockets, vamos apenas implementar a parte do Android.

Se você não viu a primeira parte, clique aqui para ver. Lembrando que estarei assumindo que você já leu a primeira parte.

Preparando a Activity

Crie um novo projeto no Android Studio, com uma Activity em branco. Eu dei o nome de MainActivity no meu caso.

No layout, vamos colocar uma ListView para as mensagens, um EditText para digitar o texto e um Button para enviar os dados para o servidor. Então temos o seguinte XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/lista_mensagens"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/layout_baixo"
        android:layout_alignParentTop="true" />

    <LinearLayout
        android:id="@+id/layout_baixo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/input_mensagem"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0.9" />

        <Button
            android:id="@+id/btn_enviar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0.1"
            android:text="Enviar" />
    </LinearLayout>
</RelativeLayout>

Agora voltando para o código da Activity, vamos criar os objetos para cada controle:

package com.joaoretamero.socketiochat;

import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

    private EditText mInputMensagem;
    private Button mBtnEnviar;
    private ListView mListaMensagens;
    private ArrayAdapter<String> mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInputMensagem = (EditText) findViewById(R.id.input_mensagem);
        mBtnEnviar = (Button) findViewById(R.id.btn_enviar);
        mListaMensagens = (ListView) findViewById(R.id.lista_mensagens);

        mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1);

        mListaMensagens.setAdapter(mAdapter);

        mBtnEnviar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // código para enviar os dados
            }
        });
    }
}

O que fizemos até agora foi: obter todos os controles da Activity, criar um Adapter para nossa ListView e prepara o evento onClick do botão “Enviar”.

Usando o Socket.io

Felizmente, o pessoal do Socket.io também fizeram uma versão para o Android, então podemos configurar o Gradle do projeto para resolver esta dependência. No seu arquivo build.gradle, adicione a seguinte linha no grupo “dependencies”:

compile 'io.socket:socket.io-client:0.6.2'

O Android Studio irá solicitar para fazer a sincronização do Gradle, faça isto e aguarde um pouco.

Para que o Socket.io funcione corretamente, adicione no seu AndroidManifest.xml, a permissão para usar a internet:

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

Antes de colocarmos o código na Activity, vamos ver como usar o Socket.io:

Socket socket = IO.socket("http://localhost:81");

// No método .on, definimos qualquer evento que desejarmos
// e vinculamos a um Emitter.Listener que será executado
// toda vez que recebermos um dado neste evento
socket.on("mensagem_cliente", new Emitter.Listener() {
	@Override
	public void call(final Object... args) {
		// Código para tratar o dado recebido
		
		// Apenas exemplo para enviar uma mensagem ao servidor
		socket.emit("mensagem_servidor", "Olá servidor");
	}
});

socket.connect();

Agora vamos colocá-lo na MainActivity, lembrando do evento onDestroy para desconectarmos o socket de forma segura.

package com.joaoretamero.socketiochat;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

import java.net.URISyntaxException;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class MainActivity extends AppCompatActivity {

    private EditText mInputMensagem;
    private Button mBtnEnviar;
    private ListView mListaMensagens;
    private ArrayAdapter<String> mAdapter;
    private Socket mSocket;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInputMensagem = (EditText) findViewById(R.id.input_mensagem);
        mBtnEnviar = (Button) findViewById(R.id.btn_enviar);
        mListaMensagens = (ListView) findViewById(R.id.lista_mensagens);

        mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1);

        mListaMensagens.setAdapter(mAdapter);

        mBtnEnviar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mInputMensagem.getText().toString().equals("")) {
                    mSocket.emit("mensagem_servidor", mInputMensagem.getText());
                    mInputMensagem.setText("");
                }
            }
        });

        try {
            mSocket = IO.socket("http://localhost:81");
        } catch (URISyntaxException e) {
            this.finish();
            return;
        }

        mSocket.on("mensagem_cliente", new Emitter.Listener() {
            @Override
            public void call(final Object... args) {
                if (args.length > 0) {
                    // Note a necessidade de usar o método runOnUiThread pois este código é
                    // executado numa thread separada, então precisamos rodar o código da UI
                    // na thread adequada
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAdapter.add((String) args[0]);
                            mAdapter.notifyDataSetChanged();

                            // Apenas faz um scroll para o novo item da lista
                            mListaMensagens.smoothScrollToPosition(mAdapter.getCount() - 1);
                        }
                    });
                }
            }
        });

        mSocket.connect();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mSocket != null)
            mSocket.disconnect();
    }
}

Feito isto, já pode executar o app (com o NodeJs já em execução) e brincar com o chat.

Você pode ver o projeto completo no repositório do GitHub.

Até mais.