Introduce Google Nearby Connections

7 min read

Bạn đã bao giờ tự hỏi làm thế nào tính năng Quick Share (trước đây là Nearby Share trên Android) hay AirDrop của hệ sinh thái Apple có thể truyền những file video nặng hàng GB giữa hai điện thoại chỉ trong chớp mắt mà không cần đến một giọt data 4G hay kết nối chung một mạng Wi-Fi nào chưa?

Hay thử tưởng tượng, làm sao để viết một tựa game multiplayer, hoặc một ứng dụng nhắn tin cục bộ cho nhóm bạn đi cắm trại ở một nơi “khỉ ho cò gáy” hoàn toàn mất sóng?

Nếu bạn là một mobile developer và đang tìm cách giải bài toán giao tiếp giữa các thiết bị ở cự ly gần, thì Google Nearby Connections API chính là “vũ khí tối thượng” đứng sau những trải nghiệm mượt mà đó.

Hôm nay, chúng ta sẽ cùng “mổ xẻ” thật sâu API này: từ kiến trúc bên dưới, cách cấu hình vòng đời kết nối, cho đến những kinh nghiệm triển khai thực tế – đặc biệt là cơn ác mộng mang tên quản lý tiến trình ngầm trên các dòng máy tùy biến. Bắt đầu thôi!

1. Kiến trúc của Nearby Connections

Bản chất của Nearby Connections là một API mạng ngang hàng (Peer-to-Peer) cho phép các thiết bị giao tiếp với nhau trong một mạng cục bộ hoàn toàn offline.

Điểm “ăn tiền” nhất của API này chính là sự trừu tượng hóa tuyệt vời. Thay vì bắt bạn phải vật lộn với các giao thức vật lý cấp thấp, Google đã gói gọn tất cả vào một hộp đen thông minh.

Cơ chế nâng cấp băng thông tự động: Khi hai thiết bị bắt đầu dò tìm nhau, chúng không dùng Wi-Fi ngay. API sẽ sử dụng Bluetooth Low Energy cực kỳ tiết kiệm pin để tìm kiếm các thiết bị xung quanh.

Ngay khi hai máy nhận diện được nhau và bạn đồng ý kết nối, một luồng đàm phán ngầm sẽ diễn ra. Nếu hệ thống nhận thấy bạn cần gửi một file lớn, API sẽ tự động thiết lập một mạng Wi-Fi Direct hoặc Wi-Fi Hotspot giữa hai máy. Quá trình “chuyển số” từ BLE sang Wi-Fi này diễn ra hoàn toàn mượt mà, giúp tốc độ truyền tải có thể vọt lên tới 60-80 MB/s.

2. Lựa chọn chiến lược kết nối

Việc đầu tiên trước khi gõ phím là bạn phải xác định kiến trúc mạng của app. Google thiết kế 3 chiến lược cực kỳ rõ ràng:

  • P2P_CLUSTER (Mạng lưới cụm – Mesh lỏng lẻo):
    • Đặc điểm: Cấu trúc M-to-N. Bất kỳ thiết bị nào cũng có thể kết nối với nhiều thiết bị khác. Băng thông thường bị giới hạn ở mức kết nối Bluetooth.
    • Ứng dụng: Rất phù hợp cho các app chat nhóm cục bộ bằng text, hoặc các board game offline (như Ma Sói) nơi các luồng dữ liệu (tọa độ, tin nhắn) rất nhỏ nhưng cần sự đa chiều.
  • P2P_STAR (Mạng hình sao):
    • Đặc điểm: Cấu trúc 1-to-N. Sẽ có một thiết bị làm Host và nhiều thiết bị Client kết nối vào.
    • Ứng dụng: Tưởng tượng bạn làm một app giáo dục. Máy tính bảng của giáo viên (Host) sẽ đẩy bài tập trắc nghiệm xuống 30 máy học sinh (Clients). Học sinh làm xong đẩy kết quả ngược lại cho giáo viên.
  • P2P_POINT_TO_POINT (Mạng 1-1):
    • Đặc điểm: Chỉ 1 máy kết nối với 1 máy. Băng thông được tối đa hóa mức cao nhất.
    • Ứng dụng: Xây dựng tính năng giống hệt AirDrop/Quick Share để chuyển file dung lượng lớn.

3. Quản Lý Vòng Đời Kết Nối

Kết nối P2P không giống như việc gọi một API RESTful (gọi cái là có JSON trả về). Nó là một cuộc hội thoại đòi hỏi sự đồng thuận qua 4 bước “bắt tay”:

Accept: Cả 2 bên gọi hàm acceptConnection(). Boom! Đường ống dữ liệu chính thức được thông.

Advertising & Discovery: Máy A “hét” lên: “Tôi ở đây!”. Máy B lắng nghe: “A, tôi thấy anh rồi!”.

Request Connection: Máy B gửi yêu cầu kết nối kèm theo một gói thông tin cơ bản.

Authentication (Xác thực): Để tránh việc gửi nhầm file cho người lạ đang ngồi quán cafe bên cạnh, hệ thống sinh ra một authenticationToken (giống như mã PIN). Bạn cần lấy mã này show lên UI cả 2 máy để người dùng tự đối chiếu.

4. Phân Tích Các Loại Dữ Liệu & Code Demo

Nearby Connections hỗ trợ 3 loại Payload. Tùy vào mục đích mà bạn chọn loại tương ứng:

  • Bytes (Payload.fromBytes()): Tin nhắn text, metadata, file JSON nhỏ (dưới 32KB). Gửi cực nhanh.
  • File (Payload.fromFile()): Gửi nguyên một file vật lý. API tự động chia nhỏ (chunk) và truyền đi.
  • Stream (Payload.fromStream()): Truyền dữ liệu dạng luồng. Cực kỳ hữu dụng nếu bạn muốn làm tính năng video call.

Code Snippet: Thiết lập kết nối 1-1 (Kotlin)

Bước 1: Máy A phát tín hiệu (Advertiser)

val connectionsClient = Nearby.getConnectionsClient(context)

val MyCallback = object : ConnectionLifecycleCallback() {
    override fun onConnectionInitiated(id: String, info: ConnectionInfo) = 
        connectionsClient.acceptConnection(id, payloadCallback).let(::println)

    override fun onConnectionResult(id: String, res: ConnectionResolution) = 
        if (res.status.statusCode == ConnectionsStatusCodes.STATUS_OK) connectionsClient.stopAdvertising() else Unit

    override fun onDisconnected(id: String) = 
        Log.d("Nearby", "Mất kết nối: $id").let(::println)
}

val options = AdvertisingOptions.Builder().setStrategy(Strategy.P2P_POINT_TO_POINT).build()
connectionsClient.startAdvertising("Máy Của A", packageName, MyCallback, options)

Bước 2: Lắng nghe tiến độ gửi File (PayloadCallback)

val payloadCallback = object : PayloadCallback() {
    override fun onPayloadReceived(id: String, p: Payload) = 
        if (p.type == Payload.Type.FILE) Log.d("Nearby", "Nhận file: ${p.asFile()}").let(::println) else Unit

    override fun onPayloadTransferUpdate(id: String, u: PayloadTransferUpdate) = 
        if (u.status == PayloadTransferUpdate.Status.IN_PROGRESS) Log.d("Nearby", "Tiến độ: ${(u.bytesTransferred * 100 / u.totalBytes).toInt()}%").let(::println) else if (u.status == PayloadTransferUpdate.Status.SUCCESS) Log.d("Nearby", "Hoàn tất!").let(::println) else Unit
}

5. Những kinh nghiệm đúc kết từ thực tế

Mọi thứ trên docs của Google đều màu hồng, cho đến khi bạn mang app đi test trên nhiều dòng máy khác nhau. Đây là những kinh nghiệm xương máu bạn nhất định phải biết:

5.1. Bức tường lửa mang tên “Permissions”

Từ Android 12 và đặc biệt là Android 13+, việc xin quyền đã trở nên rất khó khăn. Bạn không chỉ cần quyền Bluetooth (BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT, BLUETOOTH_SCAN) mà hệ thống còn ép bạn xin quyền ACCESS_FINE_LOCATIONNEARBY_WIFI_DEVICES.

Trải nghiệm người dùng (UX): Bạn nghĩ sao khi tải một app “Chuyển ảnh cho bạn bè” mà nó lại đòi quyền bật GPS? Rất nhiều user sẽ bấm “Deny” và vote 1 sao. Do đó, bạn bắt buộc phải có các màn hình Onboarding giải thích cực kỳ tinh tế rằng: “Chúng tôi cần quyền Vị trí chỉ để phần cứng Bluetooth quét các máy xung quanh, cam kết không thu thập dữ liệu GPS của bạn”.

5.2. Khó khăn trong các rom tuỳ biến Trung Quốc

Nếu bạn build một app đòi hỏi kết nối liên tục (ví dụ như một app giao tiếp nội bộ), bạn sẽ sớm nhận ra một sự thật: Các bản ROM tùy biến sâu của Trung Quốc như MIUI hay HyperOS (Xiaomi) có cơ chế quản lý pin cực kỳ tàn bạo.

Khi kết nối Nearby Connections đang chạy mà người dùng vuốt về màn hình Home, app của bạn có thể bị hệ điều hành “kill” ngay lập tức để giải phóng RAM, khiến kết nối bị đứt.

Cách xử lý:

  1. Foreground Service: Bạn bắt buộc phải bọc logic Nearby Connections bên trong một Foreground Service đi kèm với một Notification liên tục trên thanh thông báo.
  2. App Whitelisting: Bằng code, rất khó để can thiệp sâu vào hệ thống của Xiaomi. Bạn phải chủ động hiển thị hướng dẫn người dùng vào phần Cài đặt -> Tìm ứng dụng của bạn -> Kích hoạt Autostart và chọn No Restrictions trong mục Tiết kiệm pin. Dù hơi phiền phức cho user, nhưng đây là con đường sống sót duy nhất trên các rom tuỳ biến này.

5.3. Bài toán hao pin

Advertising và Discovery sử dụng ăng-ten liên tục. Đừng bao giờ để tính năng quét chạy vô tận. Hãy set timeout (ví dụ 60 giây không thấy ai thì tự động tắt quét), và nhớ gọi stopDiscovery()stopAdvertising() ngay lập tức khi sự kiện onConnectionResult báo thành công.

Lời Kết

Google Nearby Connections thực sự là một giải pháp mạnh mẽ cho bài toán kết nối P2P offline. Dù chặng đường tối ưu hệ thống trên các dòng máy tùy biến đòi hỏi không ít sự kiên nhẫn, nhưng thành quả nhận lại hoàn toàn xứng đáng. Đó chính là một trải nghiệm kết nối liền mạch, không độ trễ, mang lại giá trị thực sự vượt trội cho ứng dụng của bạn

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *