Skip to content

Commit 55c6297

Browse files
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.
1 parent 66db0fa commit 55c6297

File tree

3 files changed

+53
-54
lines changed

3 files changed

+53
-54
lines changed

ApiDemos/java/app/src/main/java/com/example/mapdemo/TileCoordinateDemoActivity.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import android.content.Context;
2626
import android.graphics.Bitmap;
27+
import android.graphics.Bitmap.Config;
2728
import android.graphics.Canvas;
2829
import android.graphics.Paint;
2930
import android.os.Bundle;
@@ -59,49 +60,48 @@ private static class CoordTileProvider implements TileProvider {
5960

6061
private final float scaleFactor;
6162

62-
private final Bitmap borderTile;
63-
6463
public CoordTileProvider(Context context) {
6564
/* Scale factor based on density, with a 0.6 multiplier to increase tile generation
6665
* speed */
6766
scaleFactor = context.getResources().getDisplayMetrics().density * 0.6f;
68-
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
69-
borderPaint.setStyle(Paint.Style.STROKE);
70-
borderTile = Bitmap.createBitmap((int) (TILE_SIZE_DP * scaleFactor),
71-
(int) (TILE_SIZE_DP * scaleFactor), android.graphics.Bitmap.Config.ARGB_8888);
72-
Canvas canvas = new Canvas(borderTile);
73-
canvas.drawRect(0, 0, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor,
74-
borderPaint);
7567
}
7668

7769
@Override
7870
public Tile getTile(int x, int y, int zoom) {
79-
Bitmap coordTile = drawTileCoords(x, y, zoom);
71+
Bitmap coordTile = createTile(x, y, zoom);
8072
ByteArrayOutputStream stream = new ByteArrayOutputStream();
8173
coordTile.compress(Bitmap.CompressFormat.PNG, 0, stream);
8274
byte[] bitmapData = stream.toByteArray();
8375
return new Tile((int) (TILE_SIZE_DP * scaleFactor),
8476
(int) (TILE_SIZE_DP * scaleFactor), bitmapData);
8577
}
8678

87-
private Bitmap drawTileCoords(int x, int y, int zoom) {
88-
// Synchronize copying the bitmap to avoid a race condition in some devices.
89-
Bitmap copy = null;
90-
synchronized (borderTile) {
91-
copy = borderTile.copy(android.graphics.Bitmap.Config.ARGB_8888, true);
92-
}
93-
Canvas canvas = new Canvas(copy);
79+
private Bitmap createTile(int x, int y, int zoom) {
80+
Bitmap tile =
81+
Bitmap.createBitmap(
82+
(int) (TILE_SIZE_DP * scaleFactor),
83+
(int) (TILE_SIZE_DP * scaleFactor),
84+
Config.ARGB_8888);
85+
Canvas canvas = new Canvas(tile);
86+
87+
// Draw the tile borders.
88+
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
89+
borderPaint.setStyle(Paint.Style.STROKE);
90+
canvas.drawRect(0, 0, TILE_SIZE_DP * scaleFactor,
91+
TILE_SIZE_DP * scaleFactor, borderPaint);
92+
93+
// Draw the tile position text.
9494
String tileCoords = "(" + x + ", " + y + ")";
9595
String zoomLevel = "zoom = " + zoom;
96-
/* Paint is not thread safe. */
9796
Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
9897
mTextPaint.setTextAlign(Paint.Align.CENTER);
9998
mTextPaint.setTextSize(18 * scaleFactor);
10099
canvas.drawText(tileCoords, TILE_SIZE_DP * scaleFactor / 2,
101100
TILE_SIZE_DP * scaleFactor / 2, mTextPaint);
102101
canvas.drawText(zoomLevel, TILE_SIZE_DP * scaleFactor / 2,
103102
TILE_SIZE_DP * scaleFactor * 2 / 3, mTextPaint);
104-
return copy;
103+
104+
return tile;
105105
}
106106
}
107107
}

ApiDemos/java/app/src/v3/java/com/example/mapdemo/TileCoordinateDemoActivity.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import android.content.Context;
2626
import android.graphics.Bitmap;
27+
import android.graphics.Bitmap.Config;
2728
import android.graphics.Canvas;
2829
import android.graphics.Paint;
2930
import android.os.Bundle;
@@ -58,49 +59,48 @@ private static class CoordTileProvider implements TileProvider {
5859

5960
private final float mScaleFactor;
6061

61-
private final Bitmap mBorderTile;
62-
6362
public CoordTileProvider(Context context) {
6463
/* Scale factor based on density, with a 0.6 multiplier to increase tile generation
6564
* speed */
6665
mScaleFactor = context.getResources().getDisplayMetrics().density * 0.6f;
67-
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
68-
borderPaint.setStyle(Paint.Style.STROKE);
69-
mBorderTile = Bitmap.createBitmap((int) (TILE_SIZE_DP * mScaleFactor),
70-
(int) (TILE_SIZE_DP * mScaleFactor), android.graphics.Bitmap.Config.ARGB_8888);
71-
Canvas canvas = new Canvas(mBorderTile);
72-
canvas.drawRect(0, 0, TILE_SIZE_DP * mScaleFactor, TILE_SIZE_DP * mScaleFactor,
73-
borderPaint);
7466
}
7567

7668
@Override
7769
public Tile getTile(int x, int y, int zoom) {
78-
Bitmap coordTile = drawTileCoords(x, y, zoom);
70+
Bitmap coordTile = createTile(x, y, zoom);
7971
ByteArrayOutputStream stream = new ByteArrayOutputStream();
8072
coordTile.compress(Bitmap.CompressFormat.PNG, 0, stream);
8173
byte[] bitmapData = stream.toByteArray();
8274
return new Tile((int) (TILE_SIZE_DP * mScaleFactor),
8375
(int) (TILE_SIZE_DP * mScaleFactor), bitmapData);
8476
}
8577

86-
private Bitmap drawTileCoords(int x, int y, int zoom) {
87-
// Synchronize copying the bitmap to avoid a race condition in some devices.
88-
Bitmap copy = null;
89-
synchronized (mBorderTile) {
90-
copy = mBorderTile.copy(android.graphics.Bitmap.Config.ARGB_8888, true);
91-
}
92-
Canvas canvas = new Canvas(copy);
78+
private Bitmap createTile(int x, int y, int zoom) {
79+
Bitmap tile =
80+
Bitmap.createBitmap(
81+
(int) (TILE_SIZE_DP * mScaleFactor),
82+
(int) (TILE_SIZE_DP * mScaleFactor),
83+
Config.ARGB_8888);
84+
Canvas canvas = new Canvas(tile);
85+
86+
// Draw the tile borders.
87+
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
88+
borderPaint.setStyle(Paint.Style.STROKE);
89+
canvas.drawRect(0, 0, TILE_SIZE_DP * mScaleFactor,
90+
TILE_SIZE_DP * mScaleFactor, borderPaint);
91+
92+
// Draw the tile position text.
9393
String tileCoords = "(" + x + ", " + y + ")";
9494
String zoomLevel = "zoom = " + zoom;
95-
/* Paint is not thread safe. */
9695
Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
9796
mTextPaint.setTextAlign(Paint.Align.CENTER);
9897
mTextPaint.setTextSize(18 * mScaleFactor);
9998
canvas.drawText(tileCoords, TILE_SIZE_DP * mScaleFactor / 2,
10099
TILE_SIZE_DP * mScaleFactor / 2, mTextPaint);
101100
canvas.drawText(zoomLevel, TILE_SIZE_DP * mScaleFactor / 2,
102101
TILE_SIZE_DP * mScaleFactor * 2 / 3, mTextPaint);
103-
return copy;
102+
103+
return tile;
104104
}
105105
}
106106
}

ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TileCoordinateDemoActivity.kt

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,32 +48,38 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback {
4848

4949
private class CoordTileProvider(context: Context) : TileProvider {
5050
private val scaleFactor: Float
51-
private val borderTile: Bitmap
5251
override fun getTile(x: Int, y: Int, zoom: Int): Tile {
53-
val coordTile = drawTileCoords(x, y, zoom)
52+
val coordTile = createTile(x, y, zoom)
5453
val stream = ByteArrayOutputStream()
5554
coordTile!!.compress(Bitmap.CompressFormat.PNG, 0, stream)
5655
val bitmapData = stream.toByteArray()
5756
return Tile((TILE_SIZE_DP * scaleFactor).toInt(),
5857
(TILE_SIZE_DP * scaleFactor).toInt(), bitmapData)
5958
}
6059

61-
private fun drawTileCoords(x: Int, y: Int, zoom: Int): Bitmap? {
62-
// Synchronize copying the bitmap to avoid a race condition in some devices.
63-
var copy: Bitmap? = null
64-
synchronized(borderTile) { copy = borderTile.copy(Bitmap.Config.ARGB_8888, true) }
65-
val canvas = Canvas(copy!!)
60+
private fun createTile(x: Int, y: Int, zoom: Int): Bitmap? {
61+
val tile = Bitmap.createBitmap((TILE_SIZE_DP * scaleFactor).toInt(),
62+
(TILE_SIZE_DP * scaleFactor).toInt(), Bitmap.Config.ARGB_8888)
63+
val canvas = Canvas(tile)
64+
65+
// Draw the tile borders.
66+
val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
67+
borderPaint.style = Paint.Style.STROKE
68+
canvas.drawRect(0f, 0f, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor,
69+
borderPaint)
70+
71+
// Draw the tile position text.
6672
val tileCoords = "($x, $y)"
6773
val zoomLevel = "zoom = $zoom"
68-
/* Paint is not thread safe. */
6974
val mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
7075
mTextPaint.textAlign = Paint.Align.CENTER
7176
mTextPaint.textSize = 18 * scaleFactor
7277
canvas.drawText(tileCoords, TILE_SIZE_DP * scaleFactor / 2,
7378
TILE_SIZE_DP * scaleFactor / 2, mTextPaint)
7479
canvas.drawText(zoomLevel, TILE_SIZE_DP * scaleFactor / 2,
7580
TILE_SIZE_DP * scaleFactor * 2 / 3, mTextPaint)
76-
return copy
81+
82+
return tile
7783
}
7884

7985
companion object {
@@ -84,13 +90,6 @@ class TileCoordinateDemoActivity : AppCompatActivity(), OnMapReadyCallback {
8490
/* Scale factor based on density, with a 0.6 multiplier to increase tile generation
8591
* speed */
8692
scaleFactor = context.resources.displayMetrics.density * 0.6f
87-
val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
88-
borderPaint.style = Paint.Style.STROKE
89-
borderTile = Bitmap.createBitmap((TILE_SIZE_DP * scaleFactor).toInt(),
90-
(TILE_SIZE_DP * scaleFactor).toInt(), Bitmap.Config.ARGB_8888)
91-
val canvas = Canvas(borderTile)
92-
canvas.drawRect(0f, 0f, TILE_SIZE_DP * scaleFactor, TILE_SIZE_DP * scaleFactor,
93-
borderPaint)
9493
}
9594
}
9695
}

0 commit comments

Comments
 (0)