Skip to content

Commit cccec69

Browse files
authored
feat: android maplibre 12.0, remove Helpers, static context and activity in favor of provided solution by jni, use using-pattern to avoid memory leaks (#386)
1 parent 19175ea commit cccec69

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+18920
-881
lines changed

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ buildscript {
1212
}
1313

1414
dependencies {
15-
classpath("com.android.tools.build:gradle:8.13.0")
15+
classpath("com.android.tools.build:gradle:8.12.3")
1616
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
1717
// noinspection GradleDynamicVersion
1818
classpath("org.jlleitschuh.gradle:ktlint-gradle:13.1.+")
@@ -67,7 +67,7 @@ android {
6767

6868
dependencies {
6969
// jnigen picks up api dependencies
70-
api 'org.maplibre.gl:android-sdk:11.13.+'
70+
api 'org.maplibre.gl:android-sdk:12.0.+'
7171
testImplementation "org.jetbrains.kotlin:kotlin-test"
7272
testImplementation "org.mockito:mockito-core:5.20.+"
7373
}

android/gradle/wrapper/gradle-wrapper.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
#Fri Oct 31 09:53:25 CET 2025
12
distributionBase=GRADLE_USER_HOME
23
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
45
networkTimeout=10000
56
validateDistributionUrl=true
67
zipStoreBase=GRADLE_USER_HOME

android/src/main/kotlin/com/github/josxha/maplibre/Helpers.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.

android/src/main/kotlin/com/github/josxha/maplibre/MapLibrePlugin.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.josxha.maplibre
22

33
import PermissionManagerHostApi
4+
import android.app.Activity
45
import android.content.Context
56
import androidx.lifecycle.Lifecycle
67
import androidx.lifecycle.LifecycleOwner
@@ -23,10 +24,10 @@ class MapLibrePlugin :
2324
PermissionManagerHostApi {
2425
private var lifecycle: Lifecycle? = null
2526

27+
private lateinit var activity: Activity
2628
private lateinit var flutterAssets: FlutterPlugin.FlutterAssets
2729

2830
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
29-
MapLibreRegistry.context = binding.applicationContext
3031
PermissionManagerHostApi.setUp(binding.binaryMessenger, this)
3132
binding
3233
.platformViewRegistry
@@ -43,7 +44,7 @@ class MapLibrePlugin :
4344
}
4445

4546
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
46-
MapLibreRegistry.activity = binding.activity
47+
activity = binding.activity
4748
binding.addRequestPermissionsResultListener(this)
4849
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding)
4950
}
@@ -91,7 +92,7 @@ class MapLibrePlugin :
9192
}
9293
},
9394
)
94-
permissionsManager?.requestLocationPermissions(MapLibreRegistry.activity)
95+
permissionsManager?.requestLocationPermissions(activity)
9596
}
9697
}
9798

android/src/main/kotlin/com/github/josxha/maplibre/MapLibreRegistry.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.github.josxha.maplibre
22

33
import android.annotation.SuppressLint
4-
import android.app.Activity
5-
import android.content.Context
64
import androidx.annotation.Keep
75
import org.maplibre.android.maps.MapLibreMap
86

@@ -21,10 +19,4 @@ object MapLibreRegistry {
2119
) {
2220
mapRegistry[viewId] = map
2321
}
24-
25-
// TODO: Storing the Activity in a static field is a potential memory leak.
26-
public var activity: Activity? = null
27-
28-
// TODO: Storing the Context in a static field is a potential memory leak.
29-
public var context: Context? = null
3022
}

example/android/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugins {
88
android {
99
namespace = "com.github.josxha.maplibre_example"
1010
compileSdk = flutter.compileSdkVersion
11-
ndkVersion = "27.0.12077973" // flutter.ndkVersion
11+
ndkVersion = "28.2.13676358" // flutter.ndkVersion
1212

1313
compileOptions {
1414
sourceCompatibility = JavaVersion.VERSION_11

jnigen.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ source_path:
1414
- 'android/src/main'
1515
classes:
1616
# https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android
17+
- 'android.app.Activity'
18+
- 'android.content.Context'
1719
- 'android.graphics.PointF'
1820
- 'android.graphics.RectF'
1921
- 'android.location.Location'
20-
- 'com.github.josxha.maplibre.Helpers'
2122
- 'com.github.josxha.maplibre.MapLibreRegistry'
2223
- 'java.net.URI'
2324
- 'java.net.URL'
@@ -39,6 +40,7 @@ classes:
3940
- 'org.maplibre.android.offline.OfflineRegionDefinition'
4041
- 'org.maplibre.android.offline.OfflineRegionError'
4142
- 'org.maplibre.android.offline.OfflineRegionStatus'
43+
- 'org.maplibre.android.offline.OfflineTilePyramidRegionDefinition'
4244
- 'org.maplibre.android.storage'
4345
- 'org.maplibre.android.style.layers'
4446
- 'org.maplibre.android.style.light'

lib/src/platform/android/extensions.dart

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ extension OffsetExt on Offset {
5151
pigeon.Offset toOffset() => pigeon.Offset(x: dx, y: dy);
5252

5353
/// Convert an [Offset] to an [jni.PointF].
54-
jni.PointF toPointF() => jni.PointF.new$3(dx, dy);
54+
jni.PointF toJPointF({required Arena arena}) =>
55+
jni.PointF.new$3(dx, dy)..releasedBy(arena);
5556
}
5657

5758
/// Extension methods for the [LngLatBounds] class. Not exported publicly.
@@ -65,12 +66,13 @@ extension LngLatBoundsExt on LngLatBounds {
6566
);
6667

6768
/// Convert an [LngLatBounds] to an internal [jni.LatLngBounds].
68-
jni.LatLngBounds toLatLngBounds() => jni.LatLngBounds.from(
69-
latitudeNorth,
70-
longitudeEast,
71-
latitudeSouth,
72-
longitudeWest,
73-
);
69+
jni.LatLngBounds toJLatLngBounds({required Arena arena}) =>
70+
jni.LatLngBounds.from(
71+
latitudeNorth,
72+
longitudeEast,
73+
latitudeSouth,
74+
longitudeWest,
75+
)..releasedBy(arena);
7476
}
7577

7678
/// Extension methods for the [jni.LatLngBounds] class. Not exported publicly.
@@ -83,7 +85,7 @@ extension LatLngBounds on jni.LatLngBounds {
8385
latitudeSouth: latitudeSouth,
8486
latitudeNorth: latitudeNorth,
8587
);
86-
release();
88+
if (releaseOriginal) release();
8789
return bounds;
8890
}
8991
}
@@ -102,20 +104,18 @@ extension EdgeInsetsExt on EdgeInsets {
102104
/// Extension methods for the [EdgeInsets] class. Not exported publicly.
103105
extension OfflineRegionExt on jni.OfflineRegion {
104106
/// Convert an [EdgeInsets] to an internal [pigeon.Padding].
105-
OfflineRegion toOfflineRegion() {
106-
final jDefinition = getDefinition();
107+
OfflineRegion toOfflineRegion() => using((arena) {
108+
final jDefinition = getDefinition()..releasedBy(arena);
107109
// TODO add getMetadata();
108-
final region = OfflineRegion(
110+
return OfflineRegion(
109111
id: getId(),
110112
bounds: jDefinition.getBounds()!.toLngLatBounds(releaseOriginal: true),
111113
minZoom: jDefinition.getMinZoom(),
112114
maxZoom: jDefinition.getMaxZoom(),
113115
pixelRatio: jDefinition.getPixelRatio(),
114116
styleUrl: jDefinition.getStyleURL()!.toDartString(releaseOriginal: true),
115117
);
116-
jDefinition.release();
117-
return region;
118-
}
118+
});
119119
}
120120

121121
/// Extension methods on [Object].
@@ -125,7 +125,9 @@ extension ObjectExt on Object {
125125
return switch (this) {
126126
final List<Object?> value => JArray.of(
127127
JObject.nullableType,
128-
value.map((e) => e?.toJObject(arena)).toList(growable: false),
128+
value
129+
.map((e) => e?.toJObject(arena)?..releasedBy(arena))
130+
.toList(growable: false),
129131
)..releasedBy(arena),
130132
final String value => value.toJString()..releasedBy(arena),
131133
final double value => value.toJDouble()..releasedBy(arena),
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:jni/jni.dart';
2+
import 'package:maplibre/src/platform/android/jni.dart';
3+
4+
/// Get the Android [Context]. Requires an [arena] parameter by whom it can
5+
/// get released.
6+
Context getJContext(Arena arena) =>
7+
Context.fromReference(Jni.getCachedApplicationContext())..releasedBy(arena);
8+
9+
/// Get the Android [Activity]. Requires an [arena] parameter by whom it can
10+
/// get released.
11+
Activity getJActivity(Arena arena) =>
12+
Activity.fromReference(Jni.getCurrentActivity())..releasedBy(arena);

lib/src/platform/android/jni.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
export 'jni/android/app/_package.dart';
2+
export 'jni/android/content/_package.dart';
13
export 'jni/android/graphics/_package.dart';
24
export 'jni/android/location/_package.dart';
35
export 'jni/com/github/josxha/maplibre/_package.dart';
4-
// export 'jni/io/flutter/embedding/engine/plugins/activity/_package.dart';
5-
// export 'jni/io/flutter/plugin/common/_package.dart';
66
export 'jni/java/net/_package.dart';
77
export 'jni/org/maplibre/android/_package.dart';
88
export 'jni/org/maplibre/android/attribution/_package.dart';

0 commit comments

Comments
 (0)