googlemaps / android-maps-compose

Jetpack Compose composables for the Maps SDK for Android
https://developers.google.com/maps/documentation/android-sdk/maps-compose
Apache License 2.0
1.16k stars 142 forks source link

GroundOverlayPosition() freezes app sometimes #378

Open psy0rz opened 1 year ago

psy0rz commented 1 year ago

I'm new to Android SDK, so maybe i'm making a rookie mistake.

This is my composable:


 @Composable
    private fun Overlays(startX: Int, endX: Int, startY: Int, endY: Int, cellSize: Int) {
        Log.d("GRID", "$startX, $startY  - $endX, $endY  ($cellSize)")

        for (x in startX..endX) {
            for (y in startY..endY) {

                var northEast = LatLng(yToLat(y + 1, cellSize), xTolng(x + 1, cellSize))
                var southWest = LatLng(yToLat(y, cellSize), xTolng(x, cellSize))

                Log.d(
                    "GRID",
                    "draw ${southWest.latitude} to ${northEast.latitude} EN ${southWest.longitude} toi ${northEast.longitude}"
                )
                val p = GroundOverlayPosition.create(LatLngBounds(southWest, northEast))
                Log.d("GRID", "get rect")
                val rect = getrect()
                Log.d("GRID", "do overlay")
                GroundOverlay(position = p, image = rect)
                Log.d("GRID", "overlay don")
                ;
            }

        }
        Log.d("GRID", "return")
    }

getrect is just a simple temporary test thing:

fun getrect(): BitmapDescriptor {
    val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    borderPaint.style = Paint.Style.STROKE
    borderPaint.alpha = 128
    borderPaint.strokeWidth = 10f
    borderPaint.color = Color.RED

    var bitmapborder = Bitmap.createBitmap(
        (512).toInt(),
        (512).toInt(), Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmapborder)
    canvas.drawRect(
        0f, 0f, 512f, 512f,
        borderPaint
    )

    borderPaint.color = Color.GREEN
    canvas.drawLine(20f, 20f, 490f, 490f, borderPaint)

    var o = GroundOverlayOptions()
    var bitmap = BitmapDescriptorFactory.fromBitmap(bitmapborder)
    bitmapborder.recycle()

    return bitmap
}

Using it in a GoogleMap like this:

                  GoogleMap(
                        properties = MapProperties(isMyLocationEnabled = true),
                        cameraPositionState = p
                    )
                    {

                        val visibleRegion = p.projection?.visibleRegion
                        if (visibleRegion != null) {
                            val level = gridLevels - p.position.zoom.toInt() + 5
//                            Log.d("GRID", "level =$level")

                            val cellSize = getCellSize(level)

                            val startX = lngToX(visibleRegion.farLeft.longitude, cellSize)
                            val endX = lngToX(visibleRegion.farRight.longitude, cellSize)
                            val endY = latToY(visibleRegion.farLeft.latitude, cellSize)
                            val startY = latToY(visibleRegion.nearRight.latitude, cellSize)

                            Overlays(startX, endX, startY, endY, cellSize)
                        }

                    }

This works perfectly, but after moving the map a while, the app suddenly freezes. Sometimes pretty quickly, sometimes it takes like 10-20 seconds.

This is the last log output:

2023-08-16 14:02:27.431 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  350, -75  - 351, -72  (20480)
2023-08-16 14:02:27.431 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.820531046707277 to -13.636257299417846 EN 104.75856316522831 toi 105.05787334570039
2023-08-16 14:02:27.431 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.433 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.433 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.433 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.636257299417846 to -13.451983552128416 EN 104.75856316522831 toi 105.05787334570039
2023-08-16 14:02:27.433 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.435 17740-17803 Counters                com...ng.backgroundlocationtracking  I  exceeded sample count in FrameTime
2023-08-16 14:02:27.435 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.436 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.436 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.451983552128416 to -13.267709804838987 EN 104.75856316522831 toi 105.05787334570039
2023-08-16 14:02:27.436 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.437 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.437 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.437 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.267709804838987 to -13.083436057549555 EN 104.75856316522831 toi 105.05787334570039
2023-08-16 14:02:27.437 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.438 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.439 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.439 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.820531046707277 to -13.636257299417846 EN 105.05787334570039 toi 105.35718352617246
2023-08-16 14:02:27.439 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.440 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.440 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.440 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.636257299417846 to -13.451983552128416 EN 105.05787334570039 toi 105.35718352617246
2023-08-16 14:02:27.440 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.441 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.441 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.441 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.451983552128416 to -13.267709804838987 EN 105.05787334570039 toi 105.35718352617246
2023-08-16 14:02:27.441 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.443 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.443 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.443 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  draw -13.267709804838987 to -13.083436057549555 EN 105.05787334570039 toi 105.35718352617246
2023-08-16 14:02:27.443 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  get rect
2023-08-16 14:02:27.444 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  do overlay
2023-08-16 14:02:27.444 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  overlay don
2023-08-16 14:02:27.444 17740-17740 GRID                    com...ng.backgroundlocationtracking  D  return

After that last return the app hangs and i get the 'app isnt responding' popup.

Tried to update everything to the latest versions to no avail:

plugins {
    id 'com.android.application' version '8.1.0' apply false
//    id 'com.android.application' version '8.1.0' apply false
    id 'com.android.library' version '8.1.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false
}

android {
    compileSdk 34

    defaultConfig {
        applicationId "com.plcoding.backgroundlocationtracking"
        minSdk 31
        //targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }
......

dependencies {

    implementation 'androidx.core:core-ktx:1.10.1'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
    implementation 'androidx.activity:activity-compose:1.7.2'
//    implementation 'org.osmdroid:osmdroid-android:6.1.16'
    implementation 'com.google.maps.android:maps-compose:2.13.0'
    implementation 'com.google.android.gms:play-services-maps:18.1.0'
    implementation 'androidx.sqlite:sqlite:2.3.1'

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
    debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"

    // Location Services
    implementation 'com.google.android.gms:play-services-location:21.0.1'

}

Am i using it wrong, or am i triggering some unkown bug due to the intensity of adding/removing groundoverlays?

wangela commented 1 year ago

If you would like to upvote the priority of this issue, please comment below or react on the original post above with :+1: so we can see what is popular when we triage.

@psy0rz Thank you for opening this issue. 🙏 Please check out these other resources that might help you get to a resolution in the meantime:

This is an automated message, feel free to ignore.

psy0rz commented 1 year ago

note that getCellSize is just a calculation, so its sideeffect free.

although i should probably move those calculations to the overlays function?

psy0rz commented 1 year ago

hmm i think this method is wrong anyway: My slow function is called for every cell everytime, instead of only to remove/add new cells. Ill implement it as one big image, instead of several tiles, probably avoiding the crash as well.

aabolfazl commented 1 year ago

@psy0rz Could you let me know if you are creating Bitmap per recomposition? I couldn't see any caching mechanism or async Bitmap creation operation, which can block your UI thread and cause the app to freeze.

psy0rz commented 1 year ago

for now i am, just as test. its fast enough, but at a certain moment it just freezes forever.

(i have to say i worked around this by just composing one groundoverlay, but i still want to know why this happens)

aabolfazl commented 1 year ago

@psy0rz let's try this code and see if it freezes again or not.

var bitmapDescriptor: BitmapDescriptor?=null

fun getrect(): BitmapDescriptor {
    if (bitmapDescriptor!=null){
        return bitmapDescriptor
    }

    val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    borderPaint.style = Paint.Style.STROKE
    borderPaint.alpha = 128
    borderPaint.strokeWidth = 10f
    borderPaint.color = Color.RED

    var bitmapborder = Bitmap.createBitmap(
        (512).toInt(),
        (512).toInt(), Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmapborder)
    canvas.drawRect(
        0f, 0f, 512f, 512f,
        borderPaint
    )

    borderPaint.color = Color.GREEN
    canvas.drawLine(20f, 20f, 490f, 490f, borderPaint)

    var bitmap = BitmapDescriptorFactory.fromBitmap(bitmapborder)
    bitmapborder.recycle()

    bitmapDescriptor = bitmap

    return bitmap
}

I've changed the code to create the Bitmap just once, please test this code instead of your current getrect() method or use remember api.

kikoso commented 1 year ago

Hi @psy0rz ,

As pointed out by @aabolfazl , the bitmap is probably abusing the recomposition, and hence the fall in performance.

Thanks.

ninovanhooff commented 3 months ago

@kikoso could you please re-open this issue? I have a reproducible sample.

My usecase: adding text-labels to a map that should rotate and scale with the map. ie. "locker-room", "toilet" etc. My idea was to draw these to a bitmap and add them to to the map this way. There may me about 30 such text labels added to the map. Please advice also if some other Composable / construct should be used instead for this usecase.

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.GroundOverlay
import com.google.maps.android.compose.GroundOverlayPosition
import com.google.maps.android.compose.rememberCameraPositionState

@Composable
fun GroundOverlayANR() = Box(
  modifier = Modifier
    .fillMaxSize()
) {
  val rijksmuseum = LatLng(52.3600866, 4.8857052)
  val cameraPositionState = rememberCameraPositionState {
    position = CameraPosition.fromLatLngZoom(rijksmuseum, 17f)
  }

  GoogleMap(
    modifier = Modifier.fillMaxSize(),
    cameraPositionState = cameraPositionState
  ) {
    for (idx in 0 until 100) { // change the upper limit to 1 to make the problem go away
      GroundOverlay(
        position = GroundOverlayPosition.create(
          rijksmuseum,
          50f,
          50f
        ),
        image = getrect(),
        zIndex = 1000f
      )
    }
  }
}

var bitmapDescriptor: BitmapDescriptor? = null

fun getrect(): BitmapDescriptor {
  Log.d("GROUNDOVERLAY", "getrect START")
  bitmapDescriptor?.let {
    Log.d("GROUNDOVERLAY", "getrect REUSE")
    return it
  }

  val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
  borderPaint.style = Paint.Style.STROKE
  borderPaint.alpha = 128
  borderPaint.strokeWidth = 10f
  borderPaint.color = Color.RED

  var bitmapborder = Bitmap.createBitmap(
    (512).toInt(),
    (512).toInt(), Bitmap.Config.ARGB_8888
  )
  val canvas = Canvas(bitmapborder)
  canvas.drawRect(
    0f, 0f, 512f, 512f,
    borderPaint
  )

  borderPaint.color = Color.GREEN
  canvas.drawLine(20f, 20f, 490f, 490f, borderPaint)

  var bitmap = BitmapDescriptorFactory.fromBitmap(bitmapborder)
  bitmapborder.recycle()

  bitmapDescriptor = bitmap
  Log.d("GROUNDOVERLAY", "getrect DONE")

  return bitmap
}
ninovanhooff commented 3 months ago

I just noticed I was running 4.3.3, and updating to 6.1.0 seems to have resolved the issue.

I'm very sorry to bother.

Leaving this here for anyone else running into this.

ninovanhooff commented 2 months ago

I found a way to reproduce this again in 6.1.0

It happens when removing items from the map; I think GroundOverlay and Markers are affected.

An in-progress camera move/animation might be the triggering factor that causes a deadlock.

I think I could produce an example project.

In short, the combination of these events triggers an ANR, I think:

May be related to https://github.com/googlemaps/android-maps-compose/issues/391