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.