From 55c629717a9f4c762cabf656eec26d5315479759 Mon Sep 17 00:00:00 2001 From: stanfchristina Date: Wed, 18 Dec 2024 16:05:04 -0800 Subject: [PATCH] fix: stop memory leak in CoordTileProvider (#1929) Bitmaps served by this provider cause memory leaks after the map is destroyed. Narrowing the scope to a field allows it to be garbage collected and does not affect the provider/activity's ability to serve and render the custom tiles. --- .../mapdemo/TileCoordinateDemoActivity.java | 38 +++++++++---------- .../mapdemo/TileCoordinateDemoActivity.java | 38 +++++++++---------- .../kotlindemos/TileCoordinateDemoActivity.kt | 31 ++++++++------- 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/ApiDemos/java/app/src/main/java/com/example/mapdemo/TileCoordinateDemoActivity.java b/ApiDemos/java/app/src/main/java/com/example/mapdemo/TileCoordinateDemoActivity.java index 72ed124b1..deae1dffd 100755 --- a/ApiDemos/java/app/src/main/java/com/example/mapdemo/TileCoordinateDemoActivity.java +++ b/ApiDemos/java/app/src/main/java/com/example/mapdemo/TileCoordinateDemoActivity.java @@ -24,6 +24,7 @@ import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Bundle; @@ -59,24 +60,15 @@ private static class CoordTileProvider implements TileProvider { private final float scaleFactor; - private final Bitmap borderTile; - public CoordTileProvider(Context context) { /* Scale factor based on density, with a 0.6 multiplier to increase tile generation * speed */ scaleFactor = context.getResources().getDisplayMetrics().density * 0.6f; - Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - borderPaint.setStyle(Paint.Style.STROKE); - borderTile = Bitmap.createBitmap((int) (TILE_SIZE_DP * scaleFactor), - (int) (TILE_SIZE_DP * scaleFactor), android.graphics.Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(borderTile); - canvas.drawRect(0, 0, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor, - borderPaint); } @Override public Tile getTile(int x, int y, int zoom) { - Bitmap coordTile = drawTileCoords(x, y, zoom); + Bitmap coordTile = createTile(x, y, zoom); ByteArrayOutputStream stream = new ByteArrayOutputStream(); coordTile.compress(Bitmap.CompressFormat.PNG, 0, stream); byte[] bitmapData = stream.toByteArray(); @@ -84,16 +76,23 @@ public Tile getTile(int x, int y, int zoom) { (int) (TILE_SIZE_DP * scaleFactor), bitmapData); } - private Bitmap drawTileCoords(int x, int y, int zoom) { - // Synchronize copying the bitmap to avoid a race condition in some devices. - Bitmap copy = null; - synchronized (borderTile) { - copy = borderTile.copy(android.graphics.Bitmap.Config.ARGB_8888, true); - } - Canvas canvas = new Canvas(copy); + private Bitmap createTile(int x, int y, int zoom) { + Bitmap tile = + Bitmap.createBitmap( + (int) (TILE_SIZE_DP * scaleFactor), + (int) (TILE_SIZE_DP * scaleFactor), + Config.ARGB_8888); + Canvas canvas = new Canvas(tile); + + // Draw the tile borders. + Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + borderPaint.setStyle(Paint.Style.STROKE); + canvas.drawRect(0, 0, TILE_SIZE_DP * scaleFactor, + TILE_SIZE_DP * scaleFactor, borderPaint); + + // Draw the tile position text. String tileCoords = "(" + x + ", " + y + ")"; String zoomLevel = "zoom = " + zoom; - /* Paint is not thread safe. */ Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(18 * scaleFactor); @@ -101,7 +100,8 @@ private Bitmap drawTileCoords(int x, int y, int zoom) { TILE_SIZE_DP * scaleFactor / 2, mTextPaint); canvas.drawText(zoomLevel, TILE_SIZE_DP * scaleFactor / 2, TILE_SIZE_DP * scaleFactor * 2 / 3, mTextPaint); - return copy; + + return tile; } } } diff --git a/ApiDemos/java/app/src/v3/java/com/example/mapdemo/TileCoordinateDemoActivity.java b/ApiDemos/java/app/src/v3/java/com/example/mapdemo/TileCoordinateDemoActivity.java index 23ad60062..49764d5c2 100755 --- a/ApiDemos/java/app/src/v3/java/com/example/mapdemo/TileCoordinateDemoActivity.java +++ b/ApiDemos/java/app/src/v3/java/com/example/mapdemo/TileCoordinateDemoActivity.java @@ -24,6 +24,7 @@ import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Bundle; @@ -58,24 +59,15 @@ private static class CoordTileProvider implements TileProvider { private final float mScaleFactor; - private final Bitmap mBorderTile; - public CoordTileProvider(Context context) { /* Scale factor based on density, with a 0.6 multiplier to increase tile generation * speed */ mScaleFactor = context.getResources().getDisplayMetrics().density * 0.6f; - Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - borderPaint.setStyle(Paint.Style.STROKE); - mBorderTile = Bitmap.createBitmap((int) (TILE_SIZE_DP * mScaleFactor), - (int) (TILE_SIZE_DP * mScaleFactor), android.graphics.Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(mBorderTile); - canvas.drawRect(0, 0, TILE_SIZE_DP * mScaleFactor, TILE_SIZE_DP * mScaleFactor, - borderPaint); } @Override public Tile getTile(int x, int y, int zoom) { - Bitmap coordTile = drawTileCoords(x, y, zoom); + Bitmap coordTile = createTile(x, y, zoom); ByteArrayOutputStream stream = new ByteArrayOutputStream(); coordTile.compress(Bitmap.CompressFormat.PNG, 0, stream); byte[] bitmapData = stream.toByteArray(); @@ -83,16 +75,23 @@ public Tile getTile(int x, int y, int zoom) { (int) (TILE_SIZE_DP * mScaleFactor), bitmapData); } - private Bitmap drawTileCoords(int x, int y, int zoom) { - // Synchronize copying the bitmap to avoid a race condition in some devices. - Bitmap copy = null; - synchronized (mBorderTile) { - copy = mBorderTile.copy(android.graphics.Bitmap.Config.ARGB_8888, true); - } - Canvas canvas = new Canvas(copy); + private Bitmap createTile(int x, int y, int zoom) { + Bitmap tile = + Bitmap.createBitmap( + (int) (TILE_SIZE_DP * mScaleFactor), + (int) (TILE_SIZE_DP * mScaleFactor), + Config.ARGB_8888); + Canvas canvas = new Canvas(tile); + + // Draw the tile borders. + Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + borderPaint.setStyle(Paint.Style.STROKE); + canvas.drawRect(0, 0, TILE_SIZE_DP * mScaleFactor, + TILE_SIZE_DP * mScaleFactor, borderPaint); + + // Draw the tile position text. String tileCoords = "(" + x + ", " + y + ")"; String zoomLevel = "zoom = " + zoom; - /* Paint is not thread safe. */ Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(18 * mScaleFactor); @@ -100,7 +99,8 @@ private Bitmap drawTileCoords(int x, int y, int zoom) { TILE_SIZE_DP * mScaleFactor / 2, mTextPaint); canvas.drawText(zoomLevel, TILE_SIZE_DP * mScaleFactor / 2, TILE_SIZE_DP * mScaleFactor * 2 / 3, mTextPaint); - return copy; + + return tile; } } } diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TileCoordinateDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TileCoordinateDemoActivity.kt index 70280bd1b..fede1726a 100644 --- a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TileCoordinateDemoActivity.kt +++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TileCoordinateDemoActivity.kt @@ -48,9 +48,8 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback { private class CoordTileProvider(context: Context) : TileProvider { private val scaleFactor: Float - private val borderTile: Bitmap override fun getTile(x: Int, y: Int, zoom: Int): Tile { - val coordTile = drawTileCoords(x, y, zoom) + val coordTile = createTile(x, y, zoom) val stream = ByteArrayOutputStream() coordTile!!.compress(Bitmap.CompressFormat.PNG, 0, stream) val bitmapData = stream.toByteArray() @@ -58,14 +57,20 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback { (TILE_SIZE_DP * scaleFactor).toInt(), bitmapData) } - private fun drawTileCoords(x: Int, y: Int, zoom: Int): Bitmap? { - // Synchronize copying the bitmap to avoid a race condition in some devices. - var copy: Bitmap? = null - synchronized(borderTile) { copy = borderTile.copy(Bitmap.Config.ARGB_8888, true) } - val canvas = Canvas(copy!!) + private fun createTile(x: Int, y: Int, zoom: Int): Bitmap? { + val tile = Bitmap.createBitmap((TILE_SIZE_DP * scaleFactor).toInt(), + (TILE_SIZE_DP * scaleFactor).toInt(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(tile) + + // Draw the tile borders. + val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG) + borderPaint.style = Paint.Style.STROKE + canvas.drawRect(0f, 0f, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor, + borderPaint) + + // Draw the tile position text. val tileCoords = "($x, $y)" val zoomLevel = "zoom = $zoom" - /* Paint is not thread safe. */ val mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) mTextPaint.textAlign = Paint.Align.CENTER mTextPaint.textSize = 18 * scaleFactor @@ -73,7 +78,8 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback { TILE_SIZE_DP * scaleFactor / 2, mTextPaint) canvas.drawText(zoomLevel, TILE_SIZE_DP * scaleFactor / 2, TILE_SIZE_DP * scaleFactor * 2 / 3, mTextPaint) - return copy + + return tile } companion object { @@ -84,13 +90,6 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback { /* Scale factor based on density, with a 0.6 multiplier to increase tile generation * speed */ scaleFactor = context.resources.displayMetrics.density * 0.6f - val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG) - borderPaint.style = Paint.Style.STROKE - borderTile = Bitmap.createBitmap((TILE_SIZE_DP * scaleFactor).toInt(), - (TILE_SIZE_DP * scaleFactor).toInt(), Bitmap.Config.ARGB_8888) - val canvas = Canvas(borderTile) - canvas.drawRect(0f, 0f, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor, - borderPaint) } } }