Bug fix: setIcon leaking memory on Android / iOS
MODIFY
plugin.xml
plugin.xml
<?xml version='1.0' encoding='utf-8'?>
-<plugin id="cordova-plugin-googlemaps" version="2.0.0-beta3-20170827-1706" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
+<plugin id="cordova-plugin-googlemaps" version="2.0.0-beta3-20170827-2151" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
<name>cordova-plugin-googlemaps</name>
<js-module name="BaseClass" src="www/BaseClass.js">
<runs />

MODIFY
src/android/plugin/google/maps/PluginMarker.java
src/android/plugin/google/maps/PluginMarker.java
@@ -1004,6 +1004,20 @@ public class PluginMarker extends MyPlugin implements MyPluginInterface {
return;
}
+ String currentCacheKey = (String) pluginMap.objects.get("marker_icon_" + marker.getTag());
+ synchronized (iconCacheKeys) {
+ if (iconCacheKeys.containsKey(currentCacheKey)) {
+ int count = iconCacheKeys.get(currentCacheKey);
+ count--;
+ if (count < 1) {
+ AsyncLoadImage.removeBitmapFromMemCahce(currentCacheKey);
+ iconCacheKeys.remove(currentCacheKey);
+ } else {
+ iconCacheKeys.put(currentCacheKey, count);
+ }
+ }
+ }
+
icons.add(result.image);
//-------------------------------------------------------

MODIFY
src/ios/GoogleMaps/PluginMarker.m
src/ios/GoogleMaps/PluginMarker.m
-
-
-
-
-
if (self.mapCtrl.debuggable) {
NSLog(@"iconPath = %@", iconPath);
}
NSString *iconCacheKey = [NSString stringWithFormat:@"%lu/%d,%d", (unsigned long)iconPath.hash, (int)width, (int)height];
UIImage *image = [[UIImageCache sharedInstance] getCachedImageForKey:iconCacheKey];
+
+
//---------------------------------------
// If the image is cached, return it
//---------------------------------------
if (image != nil) {
NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData];
- [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey];
+ // Cache the icon image
+ if ([self.mapCtrl.objects objectForKey:iconKey]) {
+ NSString *currentCacheKey = [self.mapCtrl.objects objectForKey:iconKey];
+ if (currentCacheKey && ![iconCacheKey isEqualToString:currentCacheKey]) {
+
+ int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:currentCacheKey] intValue];
+ count--;
+ if (count < 1) {
+ [[UIImageCache sharedInstance] removeCachedImageForKey:currentCacheKey];
+ [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:currentCacheKey];
+ } else {
+ [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:currentCacheKey];
+ }
+ //NSLog(@"---> currentKey = %@, count = %d", iconKey, count);
+ }
+
+ }
+
+ [self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey];
int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey] intValue];
count++;
[[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:iconCacheKey];
- //NSLog(@"---> iconCacheKey = %@, count = %d", iconCacheKey, count);
+ //NSLog(@"--->iconCacheKey = %@, count = %d", iconCacheKey, count);
if ([iconProperty objectForKey:@"label"]) {
image = [self drawLabel:image labelOptions:[iconProperty objectForKey:@"label"]];
// Cache the icon image
NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData];
+ if ([self.mapCtrl.objects objectForKey:iconKey]) {
+ NSString *currentCacheKey = [self.mapCtrl.objects objectForKey:iconKey];
+ if (currentCacheKey && ![iconCacheKey isEqualToString:currentCacheKey]) {
+
+ int count = [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:currentCacheKey] intValue];
+ count--;
+ if (count < 1) {
+ [[UIImageCache sharedInstance] removeCachedImageForKey:currentCacheKey];
+ [[UIImageCache sharedInstance].iconCacheKeys removeObjectForKey:currentCacheKey];
+ } else {
+ [[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:count] forKey:currentCacheKey];
+ }
+ //NSLog(@"---> currentKey = %@, count = %d", iconKey, count);
+ }
+
+ }
+
[[UIImageCache sharedInstance] cacheImage:image forKey:iconCacheKey];
[self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey];
[[UIImageCache sharedInstance].iconCacheKeys setObject:[NSNumber numberWithInt:1] forKey:iconCacheKey];
- //NSLog(@"--->confirm: key: %@, iconCacheKey : %@", iconKey, [self.mapCtrl.objects objectForKey:iconKey]);
+ //NSLog(@"--->(save)iconCacheKey: key: %@ = %d", iconCacheKey, [[[UIImageCache sharedInstance].iconCacheKeys objectForKey:iconCacheKey] intValue]);
+
+
+ //NSString *iconKey = [NSString stringWithFormat:@"marker_icon_%@", marker.userData];
+ //[self.mapCtrl.objects setObject:iconCacheKey forKey:iconKey];
+
// The `visible` property
- if (iconProperty[@"visible"]) {
+ if ([iconProperty[@"visible"] boolValue]) {
marker.map = self.mapCtrl.map;
}

MODIFY
src/ios/GoogleMaps/UIImageCache.m
src/ios/GoogleMaps/UIImageCache.m
@@ -19,16 +19,20 @@ static UIImageCache *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[UIImageCache alloc] init];
+ sharedInstance.imageCache = [[NSCache alloc] init];
+ sharedInstance.imageCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image
+ sharedInstance.iconCacheKeys = [[NSMutableDictionary alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
- if (self) {
+ /*
+ if (self && self.imageCache == nil) {
self.imageCache = [[NSCache alloc] init];
self.imageCache.totalCostLimit = 3 * 1024 * 1024 * 1024; // 3MB = Cache for image
self.iconCacheKeys = [NSMutableDictionary dictionary];
- }
+ }*/
return self;
}

Bug fix: setIcon leaking memory on Android / iOS #1649