Skip to content

Commit

Permalink
fix: stop memory leak in CoordTileProvider
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
stanfchristina committed Dec 18, 2024
1 parent 66db0fa commit 8489e78
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,49 +60,48 @@ 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();
return new Tile((int) (TILE_SIZE_DP * scaleFactor),
(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);
canvas.drawText(tileCoords, TILE_SIZE_DP * scaleFactor / 2,
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,49 +59,48 @@ 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();
return new Tile((int) (TILE_SIZE_DP * mScaleFactor),
(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);
canvas.drawText(tileCoords, TILE_SIZE_DP * mScaleFactor / 2,
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,38 @@ 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()
return Tile((TILE_SIZE_DP * scaleFactor).toInt(),
(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
canvas.drawText(tileCoords, TILE_SIZE_DP * scaleFactor / 2,
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 {
Expand All @@ -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)
}
}
}

0 comments on commit 8489e78

Please sign in to comment.