sforkowany z mirror/meshtastic-android
use coroutines in the mesh service to move processing out of the GUI thread
rodzic
1e34e77fe9
commit
83c1bfda69
8
TODO.md
8
TODO.md
|
@ -1,6 +1,12 @@
|
|||
# High priority
|
||||
Work items for soon alpha builds
|
||||
|
||||
* use states for meshservice: disconnected -> connected -> deviceasleep -> disconnected
|
||||
|
||||
* use compose on each page, but not for the outer wrapper
|
||||
* one view per page: https://developer.android.com/guide/navigation/navigation-swipe-view-2
|
||||
* use viewgroup with a unique ID https://developer.android.com/reference/kotlin/androidx/ui/core/package-summary#(android.view.ViewGroup).setContent(kotlin.Function0)
|
||||
|
||||
* let channel be editited
|
||||
* make link sharing work
|
||||
* finish map view
|
||||
|
@ -19,6 +25,8 @@ Work items for soon alpha builds
|
|||
# Medium priority
|
||||
Features for future builds
|
||||
|
||||
* use coroutines in the services, to ensure low latency for both API calls and GUI operations https://developer.android.com/kotlin/coroutines &
|
||||
https://medium.com/@kenkyee/android-kotlin-coroutine-best-practices-bc033fed62e7 & https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#5
|
||||
* fix notification setSmallIcon parameter - change it to use the meshtastic icon
|
||||
* ditch compose and use https://github.com/zsmb13/MaterialDrawerKt + https://github.com/Kotlin/anko/wiki/Anko-Layouts?
|
||||
* describe user experience: devices always point to each other and show distance, you can send texts between nodes
|
||||
|
|
|
@ -82,6 +82,10 @@ dependencies {
|
|||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
// Coroutines
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
|
||||
// You need to depend on the lite runtime library, not protobuf-java
|
||||
// For now I'm not using javalite, because I want JSON printing
|
||||
//implementation 'com.google.protobuf:protobuf-java:3.11.1'
|
||||
|
|
|
@ -19,3 +19,8 @@
|
|||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
# per https://medium.com/@kenkyee/android-kotlin-coroutine-best-practices-bc033fed62e7
|
||||
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
|
||||
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
|
||||
-keepclassmembernames class kotlinx.** { volatile <fields>; }
|
|
@ -11,6 +11,7 @@ import android.os.Build
|
|||
import android.os.IBinder
|
||||
import android.os.RemoteException
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.PRIORITY_MIN
|
||||
import com.geeksville.analytics.DataPair
|
||||
|
@ -27,12 +28,26 @@ import com.geeksville.util.toRemoteExceptions
|
|||
import com.google.android.gms.common.api.ResolvableApiException
|
||||
import com.google.android.gms.location.*
|
||||
import com.google.protobuf.ByteString
|
||||
import kotlinx.coroutines.*
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
class RadioNotConnectedException() : Exception("Not connected to radio")
|
||||
|
||||
|
||||
private val errorHandler = CoroutineExceptionHandler { _, exception ->
|
||||
Exceptions.report(exception, "MeshService-coroutine", "coroutine-exception")
|
||||
}
|
||||
|
||||
/// Wrap launch with an exception handler, FIXME, move into a utility lib
|
||||
fun CoroutineScope.handledLaunch(
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) = this.launch(context = context + errorHandler, start = start, block = block)
|
||||
|
||||
/**
|
||||
* Handles all the communication with android apps. Also keeps an internal model
|
||||
* of the network state.
|
||||
|
@ -96,6 +111,9 @@ class MeshService : Service(), Logging {
|
|||
IRadioInterfaceService.Stub.asInterface(it)
|
||||
}
|
||||
|
||||
private val serviceJob = Job()
|
||||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
|
||||
/*
|
||||
see com.geeksville.mesh broadcast intents
|
||||
// RECEIVED_OPAQUE for data received from other nodes
|
||||
|
@ -117,7 +135,7 @@ class MeshService : Service(), Logging {
|
|||
private var lastSendMsec = 0L
|
||||
|
||||
override fun onLocationResult(locationResult: LocationResult) {
|
||||
exceptionReporter {
|
||||
serviceScope.handledLaunch {
|
||||
super.onLocationResult(locationResult)
|
||||
var l = locationResult.lastLocation
|
||||
|
||||
|
@ -163,6 +181,7 @@ class MeshService : Service(), Logging {
|
|||
* per https://developer.android.com/training/location/change-location-settings
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
@UiThread
|
||||
private fun startLocationRequests() {
|
||||
if (fusedLocationClient == null) {
|
||||
GeeksvilleApplication.analytics.track("location_start") // Figure out how many users needed to use the phone GPS
|
||||
|
@ -375,6 +394,7 @@ class MeshService : Service(), Logging {
|
|||
radio.close()
|
||||
|
||||
super.onDestroy()
|
||||
serviceJob.cancel()
|
||||
}
|
||||
|
||||
|
||||
|
@ -712,14 +732,17 @@ class MeshService : Service(), Logging {
|
|||
if (!myNodeInfo!!.hasGPS) {
|
||||
// If we have at least one other person in the mesh, send our GPS position otherwise stop listening to GPS
|
||||
|
||||
if (numOnlineNodes >= 2)
|
||||
startLocationRequests()
|
||||
else
|
||||
stopLocationRequests()
|
||||
serviceScope.handledLaunch(Dispatchers.Main) {
|
||||
if (numOnlineNodes >= 2)
|
||||
startLocationRequests()
|
||||
else
|
||||
stopLocationRequests()
|
||||
}
|
||||
} else
|
||||
debug("Our radio has a built in GPS, so not reading GPS in phone")
|
||||
}
|
||||
|
||||
|
||||
/// Called when we gain/lose connection to our radio
|
||||
private fun onConnectionChanged(c: Boolean) {
|
||||
debug("onConnectionChanged connected=$c")
|
||||
|
@ -773,41 +796,42 @@ class MeshService : Service(), Logging {
|
|||
|
||||
// Important to never throw exceptions out of onReceive
|
||||
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
|
||||
serviceScope.handledLaunch {
|
||||
debug("Received broadcast ${intent.action}")
|
||||
when (intent.action) {
|
||||
RadioInterfaceService.RADIO_CONNECTED_ACTION -> {
|
||||
try {
|
||||
onConnectionChanged(intent.getBooleanExtra(EXTRA_CONNECTED, false))
|
||||
|
||||
debug("Received broadcast ${intent.action}")
|
||||
when (intent.action) {
|
||||
RadioInterfaceService.RADIO_CONNECTED_ACTION -> {
|
||||
try {
|
||||
onConnectionChanged(intent.getBooleanExtra(EXTRA_CONNECTED, false))
|
||||
|
||||
// forward the connection change message to anyone who is listening to us. but change the action
|
||||
// to prevent an infinite loop from us receiving our own broadcast. ;-)
|
||||
intent.action = ACTION_MESH_CONNECTED
|
||||
explicitBroadcast(intent)
|
||||
} catch (ex: RemoteException) {
|
||||
// This can happen sometimes (especially if the device is slowly dying due to killing power, don't report to crashlytics
|
||||
warn("Abandoning reconnect attempt, due to errors during init: ${ex.message}")
|
||||
// forward the connection change message to anyone who is listening to us. but change the action
|
||||
// to prevent an infinite loop from us receiving our own broadcast. ;-)
|
||||
intent.action = ACTION_MESH_CONNECTED
|
||||
explicitBroadcast(intent)
|
||||
} catch (ex: RemoteException) {
|
||||
// This can happen sometimes (especially if the device is slowly dying due to killing power, don't report to crashlytics
|
||||
warn("Abandoning reconnect attempt, due to errors during init: ${ex.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadioInterfaceService.RECEIVE_FROMRADIO_ACTION -> {
|
||||
val proto =
|
||||
MeshProtos.FromRadio.parseFrom(
|
||||
intent.getByteArrayExtra(
|
||||
EXTRA_PAYLOAD
|
||||
)!!
|
||||
)
|
||||
info("Received from radio service: ${proto.toOneLineString()}")
|
||||
when (proto.variantCase.number) {
|
||||
MeshProtos.FromRadio.PACKET_FIELD_NUMBER -> handleReceivedMeshPacket(
|
||||
proto.packet
|
||||
)
|
||||
RadioInterfaceService.RECEIVE_FROMRADIO_ACTION -> {
|
||||
val proto =
|
||||
MeshProtos.FromRadio.parseFrom(
|
||||
intent.getByteArrayExtra(
|
||||
EXTRA_PAYLOAD
|
||||
)!!
|
||||
)
|
||||
info("Received from radio service: ${proto.toOneLineString()}")
|
||||
when (proto.variantCase.number) {
|
||||
MeshProtos.FromRadio.PACKET_FIELD_NUMBER -> handleReceivedMeshPacket(
|
||||
proto.packet
|
||||
)
|
||||
|
||||
else -> TODO("Unexpected FromRadio variant")
|
||||
else -> TODO("Unexpected FromRadio variant")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> TODO("Unexpected radio interface broadcast")
|
||||
else -> TODO("Unexpected radio interface broadcast")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.3.61'
|
||||
ext.compose_version = '0.1.0-dev07'
|
||||
ext.coroutines_version = "1.3.5"
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
|
Ładowanie…
Reference in New Issue