Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Hướng dẫn gửi dữ liệu qua WiFi trên Android theo cách lập trình

Bài viết này minh họa cách truyền dữ liệu giữa hai thiết bị Android qua WiFi sử dụng mô hình Socket Server-Client. Bạn cần tạo hai dự án riêng biệt: một đóng vai Server (lắng nghe kết nối) và một đóng vai Client (kết nối đến Server).

Phần 1: Xây dựng ứng dụng Server

Bước 1: Tạo dự án mới

Mở Android Studio, chọn File → New Project, điền thông tin cần thiết và tạo dự án (ví dụ: package com.server.myapplication.server).

Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvIP"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />

    <TextView
        android:id="@+id/tvPort"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tvIP"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />

    <TextView
        android:id="@+id/tvConnectionStatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tvPort"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />

    <TextView
        android:id="@+id/tvMessages"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/etMessage"
        android:layout_below="@+id/tvConnectionStatus"
        android:inputType="textMultiLine"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/btnSend"
        android:hint="Nhập tin nhắn"
        android:inputType="text" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="GỬI" />
</RelativeLayout>

Bước 3: Viết code logic (src/MainActivity.java)

package com.server.myapplication.server;

import android.annotation.SuppressLint;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
    private ServerSocket serverSocket;
    private Thread serverThread = null;
    private TextView tvIP, tvPort, tvMessages;
    private EditText etMessage;
    private Button btnSend;
    public static String SERVER_IP = "";
    public static final int SERVER_PORT = 8080;
    private PrintWriter output;
    private BufferedReader input;

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

        tvIP = findViewById(R.id.tvIP);
        tvPort = findViewById(R.id.tvPort);
        tvMessages = findViewById(R.id.tvMessages);
        etMessage = findViewById(R.id.etMessage);
        btnSend = findViewById(R.id.btnSend);

        try {
            SERVER_IP = getLocalIpAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        serverThread = new Thread(new ServerThread());
        serverThread.start();

        btnSend.setOnClickListener(v -> {
            String message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
                new Thread(new SendThread(message)).start();
            }
        });
    }

    private String getLocalIpAddress() throws UnknownHostException {
        WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ipInt = wifiInfo.getIpAddress();
        return InetAddress.getByAddress(
            ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array()
        ).getHostAddress();
    }

    class ServerThread implements Runnable {
        @Override
        public void run() {
            Socket socket;
            try {
                serverSocket = new ServerSocket(SERVER_PORT);
                runOnUiThread(() -> {
                    tvMessages.setText("Chưa kết nối");
                    tvIP.setText("IP: " + SERVER_IP);
                    tvPort.setText("Port: " + SERVER_PORT);
                });

                socket = serverSocket.accept();
                output = new PrintWriter(socket.getOutputStream());
                input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                runOnUiThread(() -> tvMessages.setText("Đã kết nối\n"));
                new Thread(new ReceiveThread()).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ReceiveThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    final String message = input.readLine();
                    if (message != null) {
                        runOnUiThread(() -> tvMessages.append("Client: " + message + "\n"));
                    } else {
                        // Client ngắt kết nối, khởi động lại server
                        serverThread = new Thread(new ServerThread());
                        serverThread.start();
                        return;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class SendThread implements Runnable {
        private final String message;
        SendThread(String message) { this.message = message; }

        @Override
        public void run() {
            output.write(message + "\n");
            output.flush();
            runOnUiThread(() -> {
                tvMessages.append("Server: " + message + "\n");
                etMessage.setText("");
            });
        }
    }
}

Bước 4: Cấu hình quyền (AndroidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.server.myapplication.server">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity" android:label="Server">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Phần 2: Xây dựng ứng dụng Client

Bước 1: Tạo dự án mới

Tương tự, tạo dự án mới với package com.client.myapplication.client.

Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/etIP"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Địa chỉ IP Server"
        android:inputType="text" />

    <EditText
        android:id="@+id/etPort"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etIP"
        android:hint="Cổng (Port)"
        android:inputType="number" />

    <Button
        android:id="@+id/btnConnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etPort"
        android:layout_marginTop="16dp"
        android:text="Kết nối đến Server" />

    <TextView
        android:id="@+id/tvMessages"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/etMessage"
        android:layout_below="@+id/btnConnect"
        android:inputType="textMultiLine"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" />

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/btnSend"
        android:hint="Nhập tin nhắn"
        android:inputType="text" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="GỬI" />
</RelativeLayout>

Bước 3: Viết code logic (src/MainActivity.java)

package com.client.myapplication.client;

import android.annotation.SuppressLint;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
    private Thread clientThread = null;
    private EditText etIP, etPort, etMessage;
    private TextView tvMessages;
    private Button btnSend;
    private String SERVER_IP;
    private int SERVER_PORT;
    private PrintWriter output;
    private BufferedReader input;

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

        etIP = findViewById(R.id.etIP);
        etPort = findViewById(R.id.etPort);
        tvMessages = findViewById(R.id.tvMessages);
        etMessage = findViewById(R.id.etMessage);
        btnSend = findViewById(R.id.btnSend);
        Button btnConnect = findViewById(R.id.btnConnect);

        btnConnect.setOnClickListener(v -> {
            tvMessages.setText("");
            SERVER_IP = etIP.getText().toString().trim();
            SERVER_PORT = Integer.parseInt(etPort.getText().toString().trim());
            clientThread = new Thread(new ConnectThread());
            clientThread.start();
        });

        btnSend.setOnClickListener(v -> {
            String message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
                new Thread(new SendThread(message)).start();
            }
        });
    }

    class ConnectThread implements Runnable {
        @Override
        public void run() {
            try {
                Socket socket = new Socket(SERVER_IP, SERVER_PORT);
                output = new PrintWriter(socket.getOutputStream());
                input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                runOnUiThread(() -> tvMessages.setText("Đã kết nối\n"));
                new Thread(new ReceiveThread()).start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ReceiveThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    final String message = input.readLine();
                    if (message != null) {
                        runOnUiThread(() -> tvMessages.append("Server: " + message + "\n"));
                    } else {
                        // Server ngắt kết nối, thử kết nối lại
                        clientThread = new Thread(new ConnectThread());
                        clientThread.start();
                        return;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class SendThread implements Runnable {
        private final String message;
        SendThread(String message) { this.message = message; }

        @Override
        public void run() {
            output.write(message + "\n");
            output.flush();
            runOnUiThread(() -> {
                tvMessages.append("Client: " + message + "\n");
                etMessage.setText("");
            });
        }
    }
}

Bước 4: Cấu hình quyền (AndroidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.client.myapplication.client">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity" android:label="Client">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Cách chạy thử

  1. Cài đặt ứng dụng Server lên thiết bị Android thứ nhất.
  2. Cài đặt ứng dụng Client lên thiết bị Android thứ hai.
  3. Đảm bảo cả hai thiết bị kết nối cùng một mạng WiFi.
  4. Chạy Server trước, ghi lại địa chỉ IP và Port hiển thị trên màn hình.
  5. Chạy Client, nhập IP và Port của Server, nhấn "Kết nối đến Server".
  6. Sau khi kết nối thành công, bạn có thể gửi tin nhắn hai chiều giữa hai thiết bị.

Lưu ý quan trọng: Code trên sử dụng API cũ (Android Support Library). Khi triển khai thực tế, nên cập nhật lên AndroidX, sử dụng ExecutorService hoặc Coroutines thay vì Thread thô, và xử lý vòng đời Activity cẩn thận để tránh rò rỉ bộ nhớ.