fix: remove runBlocking(Dispatchers.Main) deadlock — app now starts
- NetworkBinder functions were suspend but never actually suspended (ConnectivityManager calls are synchronous) — made them plain fun - MainActivity.discoverNetworks() now calls them directly, no coroutine - Wrapped in try/catch so network discovery failure doesn't crash Activity - Removed unused awaitNetwork() extension + coroutine imports
This commit is contained in:
@@ -1,30 +1,20 @@
|
|||||||
package com.yarisobd.app
|
package com.yarisobd.app
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.yarisobd.app.ui.theme.YarisOBDTheme
|
import com.yarisobd.app.ui.theme.YarisOBDTheme
|
||||||
import com.yarisobd.app.util.NetworkBinder
|
import com.yarisobd.app.util.NetworkBinder
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
// Discovered at startup — null if the network isn't available
|
|
||||||
private var piClient: OkHttpClient? = null
|
private var piClient: OkHttpClient? = null
|
||||||
private var serverClient: OkHttpClient? = null
|
private var serverClient: OkHttpClient? = null
|
||||||
|
|
||||||
@@ -32,8 +22,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|
||||||
// Discover networks synchronously at startup (fast — just reads
|
|
||||||
// ConnectivityManager state, no network I/O)
|
|
||||||
discoverNetworks()
|
discoverNetworks()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
@@ -52,17 +40,22 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun discoverNetworks() {
|
private fun discoverNetworks() {
|
||||||
val context = applicationContext
|
try {
|
||||||
|
val piNetwork = NetworkBinder.findPiHotspotNetwork(applicationContext)
|
||||||
// Blocking is fine here — NetworkBinder discovery just reads
|
|
||||||
// ConnectivityManager state, no network calls.
|
|
||||||
runBlocking(Dispatchers.Main) {
|
|
||||||
val piNetwork = NetworkBinder.findPiHotspotNetwork(context)
|
|
||||||
piClient = piNetwork?.let { NetworkBinder.createBoundClient(it) }
|
piClient = piNetwork?.let { NetworkBinder.createBoundClient(it) }
|
||||||
|
Log.d(TAG, "Pi WiFi network: ${if (piClient != null) "bound" else "not found"}")
|
||||||
|
|
||||||
val cellNetwork = NetworkBinder.findCellularNetwork(context)
|
val cellNetwork = NetworkBinder.findCellularNetwork(applicationContext)
|
||||||
?: NetworkBinder.findDefaultNetwork(context)
|
?: NetworkBinder.findDefaultNetwork(applicationContext)
|
||||||
serverClient = cellNetwork?.let { NetworkBinder.createBoundClient(it) }
|
serverClient = cellNetwork?.let { NetworkBinder.createBoundClient(it) }
|
||||||
|
Log.d(TAG, "Cellular/default network: ${if (serverClient != null) "bound" else "not found"}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Network discovery failed", e)
|
||||||
|
// Both clients stay null — screen shows "not found" state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MainActivity"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,10 @@ import android.content.Context
|
|||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.net.NetworkRequest
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.Socket
|
|
||||||
import javax.net.SocketFactory
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discovers and binds OkHttp clients to specific Android [Network] objects
|
* Discovers and binds OkHttp clients to specific Android [Network] objects
|
||||||
@@ -30,7 +24,7 @@ object NetworkBinder {
|
|||||||
* Identified by: TRANSPORT_WIFI + NO NET_CAPABILITY_INTERNET
|
* Identified by: TRANSPORT_WIFI + NO NET_CAPABILITY_INTERNET
|
||||||
* (the Pi hotspot has no internet gateway).
|
* (the Pi hotspot has no internet gateway).
|
||||||
*/
|
*/
|
||||||
suspend fun findPiHotspotNetwork(context: Context): Network? {
|
fun findPiHotspotNetwork(context: Context): Network? {
|
||||||
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
return cm.allNetworks.firstOrNull { network ->
|
return cm.allNetworks.firstOrNull { network ->
|
||||||
@@ -44,7 +38,7 @@ object NetworkBinder {
|
|||||||
* Finds the cellular network.
|
* Finds the cellular network.
|
||||||
* Identified by: TRANSPORT_CELLULAR + NET_CAPABILITY_INTERNET
|
* Identified by: TRANSPORT_CELLULAR + NET_CAPABILITY_INTERNET
|
||||||
*/
|
*/
|
||||||
suspend fun findCellularNetwork(context: Context): Network? {
|
fun findCellularNetwork(context: Context): Network? {
|
||||||
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
|
||||||
return cm.allNetworks.firstOrNull { network ->
|
return cm.allNetworks.firstOrNull { network ->
|
||||||
@@ -98,23 +92,3 @@ object NetworkBinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for a network with the given [transport] to become available.
|
|
||||||
* Used at app startup to wait for the Pi hotspot to connect before
|
|
||||||
* making API calls.
|
|
||||||
*/
|
|
||||||
suspend fun ConnectivityManager.awaitNetwork(transport: Int): Network {
|
|
||||||
return suspendCancellableCoroutine { continuation ->
|
|
||||||
val callback = object : ConnectivityManager.NetworkCallback() {
|
|
||||||
override fun onAvailable(network: Network) {
|
|
||||||
continuation.resume(network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val request = NetworkRequest.Builder()
|
|
||||||
.addTransportType(transport)
|
|
||||||
.build()
|
|
||||||
registerNetworkCallback(request, callback)
|
|
||||||
continuation.invokeOnCancellation { unregisterNetworkCallback(callback) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user