You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nearby Connections在MVVM架构下的问题及多Activity连接咨询

Hey there, let's break down your questions one by one since you're working on a quiz game with Nearby Connections + MVVM/LiveData/Room—super solid tech stack, by the way!


1. Why does using Application Context in ViewModel throw ApiException: 13?

First, while Nearby.getConnectionsClient() accepts any Context, the advertise() method has hidden dependencies on a foreground Activity context that Application Context can't provide:

  • Nearby Connections often triggers system-level UI flows during advertising (like pairing confirmation popups or permission prompts). Application Context has no associated window or foreground lifecycle, so it can't handle these UI interactions, leading to the ERROR 13 (a subtype of ERROR_ADVERTISE_FAILED related to missing foreground support).
  • Google Play Services' binding logic for Nearby also ties into the Context's lifecycle. Application Context is global and detached from Activity lifecycle events, which can cause state mismatches for the ConnectionsClient.

Fixes to keep logic out of Activity:

  • Use ViewModel + LifecycleObserver: Have your ViewModel observe the Activity's lifecycle, grab the Activity context only when it's in a foreground state (like onResume) to run advertise(), and clean up connections when the Activity pauses/destroys.
  • Build a singleton ConnectionManager: Create a global manager that holds a WeakReference<Activity> to avoid memory leaks, and only execute foreground-dependent operations when an active Activity context is available.

2. Do you need to restart advertising after screen rotation?

Yes, but you can make it seamless:

  • When the Activity is destroyed by rotation, the ConnectionsClient tied to the old Activity context will be cleaned up, so advertising stops automatically.
  • Use your ViewModel (which survives rotation) to persist the "is advertising" state. When the new Activity instance is created, have the ViewModel trigger a restart of advertising using the new Activity context.
  • If you use the singleton ConnectionManager mentioned above, it can automatically restart advertising once it gets a reference to the new, active Activity context.

3. How to manage Nearby connections across multiple Activities?

The cleanest approach is a global ConnectionManager singleton that encapsulates all Nearby logic, with lifecycle-aware hooks to share the connection between your Lobby and Game Activities. Here's how to implement it:

Step 1: Create the singleton ConnectionManager

class ConnectionManager private constructor(private val appContext: Context) {
    private var connectionsClient: ConnectionsClient? = null
    private var currentActivityRef: WeakReference<Activity>? = null
    private val messageListeners = mutableListOf<OnMessageReceivedListener>()

    // Singleton instance
    companion object {
        @Volatile
        private var INSTANCE: ConnectionManager? = null

        fun getInstance(appContext: Context): ConnectionManager {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: ConnectionManager(appContext.applicationContext).also { INSTANCE = it }
            }
        }
    }

    // Initialize client with active Activity context
    fun attachActivity(activity: Activity) {
        currentActivityRef = WeakReference(activity)
        connectionsClient = Nearby.getConnectionsClient(activity)
    }

    // Start advertising (with context check)
    fun startAdvertising(serviceId: String, callback: AdvertisingStatusCallback) {
        currentActivityRef?.get()?.let { activity ->
            connectionsClient?.advertise(
                activity,
                "QuizGameHost",
                object : ConnectionLifecycleCallback() {
                    // Implement your connection lifecycle logic here
                },
                AdvertisingOptions(Strategy.P2P_STAR)
            )?.addOnSuccessListener { callback.onAdvertiseStarted() }
            ?.addOnFailureListener { callback.onAdvertiseFailed(it) }
        } ?: callback.onAdvertiseFailed(IllegalStateException("No active Activity context"))
    }

    // Register/unregister message listeners for different Activities
    fun registerMessageListener(listener: OnMessageReceivedListener) {
        messageListeners.add(listener)
    }

    fun unregisterMessageListener(listener: OnMessageReceivedListener) {
        messageListeners.remove(listener)
    }

    // Example interface for callbacks
    interface AdvertisingStatusCallback {
        fun onAdvertiseStarted()
        fun onAdvertiseFailed(e: Exception)
    }

    interface OnMessageReceivedListener {
        fun onMessageReceived(endpointId: String, message: ByteArray)
    }
}

Step 2: Use the manager in your Activities

Lobby Activity:

class LobbyActivity : AppCompatActivity() {
    private lateinit var connectionManager: ConnectionManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        connectionManager = ConnectionManager.getInstance(applicationContext)
        connectionManager.attachActivity(this)

        // Start advertising when the lobby loads
        connectionManager.startAdvertising("com.your.quizgame.service", object : ConnectionManager.AdvertisingStatusCallback {
            override fun onAdvertiseStarted() {
                // Update UI to show advertising is active
            }

            override fun onAdvertiseFailed(e: Exception) {
                // Handle error (show toast, retry, etc.)
            }
        })
    }

    override fun onDestroy() {
        super.onDestroy()
        // Clean up if needed
        connectionManager.stopAdvertising()
    }
}

Game Activity:

class GameActivity : AppCompatActivity(), ConnectionManager.OnMessageReceivedListener {
    private lateinit var connectionManager: ConnectionManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        connectionManager = ConnectionManager.getInstance(applicationContext)
        connectionManager.attachActivity(this)
        connectionManager.registerMessageListener(this)
    }

    override fun onMessageReceived(endpointId: String, message: ByteArray) {
        // Handle game messages (like quiz questions/answers)
    }

    override fun onDestroy() {
        super.onDestroy()
        connectionManager.unregisterMessageListener(this)
    }
}

This setup keeps all Nearby logic centralized, avoids memory leaks (thanks to WeakReference), and lets you seamlessly share the connection between Activities.


内容的提问来源于stack exchange,提问作者Jakub Kracina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:15:04