expo-gaode-map 2.2.41 → 2.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,2 @@
1
+ #Wed Jul 15 10:15:05 CST 2026
2
+ gradle.version=8.9
File without changes
@@ -217,7 +217,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
217
217
 
218
218
  // 辅助监听器列表
219
219
  private val cameraChangeListeners = mutableListOf<AMap.OnCameraChangeListener>()
220
- private val pendingMarkerRemovalTasks = mutableMapOf<MarkerView, Runnable>()
221
220
 
222
221
  fun addCameraChangeListener(listener: AMap.OnCameraChangeListener) {
223
222
  if (!cameraChangeListeners.contains(listener)) {
@@ -651,7 +650,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
651
650
  mainHandler.removeCallbacksAndMessages(null)
652
651
  pendingCameraMoveData = null
653
652
  pendingCameraMoveDispatch = null
654
- pendingMarkerRemovalTasks.clear()
655
653
 
656
654
  // 清理所有地图监听器
657
655
  aMap.setOnMapClickListener(null)
@@ -757,7 +755,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
757
755
  @SuppressLint("UseKtx")
758
756
  override fun addView(child: View?, index: Int) {
759
757
  if (child is MarkerView) {
760
- cancelPendingMarkerRemoval(child)
761
758
  child.setMap(aMap)
762
759
  // MarkerView 需要保留可测量尺寸,否则 Android 无法正确处理
763
760
  // Text / View 的 maxWidth 等布局约束,最终会被测成整行宽度。
@@ -789,13 +786,10 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
789
786
  }
790
787
  }
791
788
 
792
- /**
793
- * 移除子视图
794
- * 延迟移除 Marker,让它们跟随地图一起延迟销毁
795
- */
789
+ /** 移除子视图,并同步移除对应的原生 Marker。 */
796
790
  override fun removeView(child: View?) {
797
791
  if (child is MarkerView) {
798
- scheduleMarkerRemoval(child)
792
+ child.removeMarker()
799
793
  super.removeView(child)
800
794
  return
801
795
  }
@@ -807,10 +801,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
807
801
  }
808
802
  }
809
803
 
810
- /**
811
- * 按索引移除视图
812
- * 延迟移除 Marker,让它们跟随地图一起延迟销毁
813
- */
804
+ /** 按索引移除视图,并同步移除对应的原生 Marker。 */
814
805
  override fun removeViewAt(index: Int) {
815
806
  try {
816
807
  val child = super.getChildAt(index)
@@ -820,7 +811,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
820
811
  }
821
812
 
822
813
  if (child is MarkerView) {
823
- scheduleMarkerRemoval(child)
814
+ child.removeMarker()
824
815
  }
825
816
 
826
817
  super.removeViewAt(index)
@@ -830,24 +821,6 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
830
821
  }
831
822
  }
832
823
 
833
- private fun scheduleMarkerRemoval(markerView: MarkerView) {
834
- cancelPendingMarkerRemoval(markerView)
835
-
836
- val task = Runnable {
837
- pendingMarkerRemovalTasks.remove(markerView)
838
- if (markerView.parent == null) {
839
- markerView.removeMarker()
840
- }
841
- }
842
-
843
- pendingMarkerRemovalTasks[markerView] = task
844
- mainHandler.postDelayed(task, 500)
845
- }
846
-
847
- private fun cancelPendingMarkerRemoval(markerView: MarkerView) {
848
- pendingMarkerRemovalTasks.remove(markerView)?.let(mainHandler::removeCallbacks)
849
- }
850
-
851
824
  private fun checkDeclarativePolylinePress(latLng: LatLng): Boolean {
852
825
  for (i in 0 until childCount) {
853
826
  val child = getChildAt(i)
@@ -224,7 +224,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
224
224
  private val mainHandler = Handler(Looper.getMainLooper())
225
225
  private var isRemoving = false // 标记是否正在被移除
226
226
  private var pendingMarkerIconUpdate: Runnable? = null
227
- private var pendingDetachRemovalTask: Runnable? = null
227
+ private var contentInvalidationGeneration: Long = 0
228
228
  private var lastAppliedCustomMarkerKey: String? = null
229
229
 
230
230
  // 缓存属性,在 marker 创建前保存
@@ -338,14 +338,9 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
338
338
 
339
339
  flushPendingShowIfNeeded()
340
340
 
341
- // 🔑 修复:需要延迟更新图标,等待 children 完成布局
342
- // 使用 post 延迟到下一帧,确保 children 完成测量和布局
341
+ // 等待 children 在同一渲染帧内稳定后再生成一次快照。
343
342
  if (isNotEmpty() && marker != null) {
344
- mainHandler.post {
345
- if (!isRemoving && marker != null && isNotEmpty()) {
346
- updateMarkerIcon()
347
- }
348
- }
343
+ markCustomMarkerContentDirty()
349
344
  }
350
345
  }
351
346
 
@@ -484,9 +479,9 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
484
479
  cacheKey = key
485
480
  if (isNotEmpty()) {
486
481
  // cacheKey 通常和 selected/zoom level 同帧变化。先淘汰当前 key 可能存在的旧截图,
487
- // 再延后一帧重绘,避免切换选中态时命中 stale bitmap。
482
+ // 等待 children 完成当前帧的 props/layout 更新后再重绘。
488
483
  invalidateCurrentSnapshotCache()
489
- markCustomMarkerContentDirty(32)
484
+ markCustomMarkerContentDirty()
490
485
  } else {
491
486
  pendingIconUri?.let { iconUri ->
492
487
  marker?.let { loadAndSetIcon(iconUri, it) }
@@ -981,29 +976,54 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
981
976
  marker?.let { showMarker(it) }
982
977
  }
983
978
 
984
- private fun scheduleMarkerIconUpdate(delayMs: Long = 16L) {
979
+ private fun scheduleMarkerIconUpdateAfterContentSettles() {
985
980
  if (isRemoving || marker == null || isEmpty()) {
986
981
  return
987
982
  }
988
983
 
989
- pendingMarkerIconUpdate?.let { mainHandler.removeCallbacks(it) }
990
- val task = Runnable {
984
+ pendingMarkerIconUpdate?.let { removeCallbacks(it) }
985
+ val scheduledGeneration = contentInvalidationGeneration
986
+ val settleTask = Runnable {
991
987
  pendingMarkerIconUpdate = null
992
- if (!isRemoving && marker != null && isNotEmpty()) {
993
- updateMarkerIcon()
988
+ if (isRemoving || marker == null || isEmpty()) {
989
+ return@Runnable
990
+ }
991
+
992
+ if (scheduledGeneration != contentInvalidationGeneration) {
993
+ scheduleMarkerIconUpdateAfterContentSettles()
994
+ return@Runnable
994
995
  }
996
+
997
+ scheduleMarkerIconUpdateForStableGeneration(scheduledGeneration)
995
998
  }
996
- pendingMarkerIconUpdate = task
999
+ pendingMarkerIconUpdate = settleTask
1000
+ postOnAnimation(settleTask)
1001
+ }
997
1002
 
998
- if (delayMs <= 0L) {
999
- mainHandler.post(task)
1000
- } else {
1001
- mainHandler.postDelayed(task, delayMs)
1003
+ private fun scheduleMarkerIconUpdateForStableGeneration(expectedGeneration: Long) {
1004
+ if (isRemoving || marker == null || isEmpty()) {
1005
+ return
1006
+ }
1007
+
1008
+ val renderTask = Runnable {
1009
+ pendingMarkerIconUpdate = null
1010
+ if (isRemoving || marker == null || isEmpty()) {
1011
+ return@Runnable
1012
+ }
1013
+
1014
+ if (expectedGeneration != contentInvalidationGeneration) {
1015
+ scheduleMarkerIconUpdateAfterContentSettles()
1016
+ return@Runnable
1017
+ }
1018
+
1019
+ updateMarkerIcon()
1002
1020
  }
1021
+ pendingMarkerIconUpdate = renderTask
1022
+ postOnAnimation(renderTask)
1003
1023
  }
1004
1024
 
1005
1025
  private fun cancelPendingMarkerIconUpdate() {
1006
- pendingMarkerIconUpdate?.let { mainHandler.removeCallbacks(it) }
1026
+ pendingMarkerIconUpdate?.let { removeCallbacks(it) }
1007
1027
  pendingMarkerIconUpdate = null
1008
1028
  }
1009
1029
 
@@ -1022,17 +1042,19 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1022
1042
  IconBitmapCache.remove(key)
1023
1043
  }
1024
1044
 
1025
- private fun markCustomMarkerContentDirty(delayMs: Long = 16L) {
1045
+ private fun markCustomMarkerContentDirty() {
1026
1046
  if (isRemoving || isEmpty()) {
1027
1047
  return
1028
1048
  }
1029
1049
 
1050
+ contentInvalidationGeneration += 1
1051
+
1030
1052
  // children(尤其是 Image)异步加载完成后,需要强制淘汰当前 marker 已应用的位图缓存,
1031
1053
  // 否则同一个 cacheKey 会一直命中“占位态”的旧 bitmap,表现为灰块/蒙层不更新。
1032
1054
  // 这里仅清理当前 marker 已应用的 key,不会影响其它 marker。
1033
1055
  invalidateAppliedCustomMarkerCaches(clearGlobalCache = true)
1034
1056
  if (marker != null) {
1035
- scheduleMarkerIconUpdate(delayMs)
1057
+ scheduleMarkerIconUpdateAfterContentSettles()
1036
1058
  }
1037
1059
  }
1038
1060
 
@@ -1043,8 +1065,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1043
1065
  if (child != null && contains(child)) {
1044
1066
  super.removeView(child)
1045
1067
  // 不要在这里恢复默认图标
1046
- // 如果 MarkerView 整体要被移除,onDetachedFromWindow 会处理
1047
- // 如果只是移除 children 并保留 Marker,应该由外部重新设置 children
1068
+ // 如果 MarkerView 整体被移除,父 MapView / OnViewDestroys 会清理原生 Marker。
1069
+ // 如果只是替换 children,后续 add/layout invalidation 会生成新快照。
1048
1070
  }
1049
1071
  } catch (_: Exception) {
1050
1072
  // 忽略异常
@@ -1057,10 +1079,10 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1057
1079
  super.removeViewAt(index)
1058
1080
  // 只在还有子视图时更新图标
1059
1081
  if (!isRemoving && isNotEmpty() && marker != null) {
1060
- markCustomMarkerContentDirty(50)
1082
+ markCustomMarkerContentDirty()
1061
1083
  }
1062
- // 如果最后一个子视图被移除,什么都不做
1063
- // onDetachedFromWindow 处理完整的清理
1084
+ // 最后一个子视图被移除时保留 last-good 快照;新 child 加入后原子替换,
1085
+ // MarkerView 真正销毁则由父 MapView / OnViewDestroys 负责清理。
1064
1086
  }
1065
1087
  } catch (_: Exception) {
1066
1088
  // 忽略异常
@@ -1344,8 +1366,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1344
1366
  * 移除标记
1345
1367
  */
1346
1368
  fun removeMarker() {
1347
- cancelPendingDetachRemoval()
1348
1369
  cancelPendingMarkerIconUpdate()
1370
+ contentInvalidationGeneration += 1
1349
1371
 
1350
1372
  // 停止平滑移动
1351
1373
  stopSmoothMove()
@@ -1361,44 +1383,18 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1361
1383
 
1362
1384
  override fun onAttachedToWindow() {
1363
1385
  super.onAttachedToWindow()
1364
- cancelPendingDetachRemoval()
1365
1386
  isRemoving = false
1366
1387
 
1367
1388
  if (isNotEmpty() && marker != null) {
1368
- scheduleMarkerIconUpdate(16)
1389
+ markCustomMarkerContentDirty()
1369
1390
  }
1370
1391
  }
1371
1392
 
1372
1393
  override fun onDetachedFromWindow() {
1373
1394
  super.onDetachedFromWindow()
1374
-
1375
- // 🔑 关键修复:使用 post 延迟检查
1376
- cancelPendingDetachRemoval()
1377
-
1378
- // 延迟检查 parent 状态。React 更新 children 时可能会短暂 detach/attach,
1379
- // 过早删除会导致点击后其它 Marker 消失。
1380
- val task = Runnable {
1381
- pendingDetachRemovalTask = null
1382
- if (parent == null) {
1383
- // 标记正在移除
1384
- isRemoving = true
1385
- cancelPendingMarkerIconUpdate()
1386
-
1387
- // 🔑 修复:不要清空全局缓存
1388
- // 理由:会影响其他 Marker 的性能
1389
- // 缓存应该由 LruCache 自动管理,或在合适的时机(如内存警告)统一清理
1390
-
1391
- // 移除 marker
1392
- removeMarker()
1393
- }
1394
- }
1395
- pendingDetachRemovalTask = task
1396
- mainHandler.postDelayed(task, 500)
1397
- }
1398
-
1399
- private fun cancelPendingDetachRemoval() {
1400
- pendingDetachRemovalTask?.let { mainHandler.removeCallbacks(it) }
1401
- pendingDetachRemovalTask = null
1395
+ // Window detach 可能只是页面转场或 Fabric 重挂载,不代表 Marker 已销毁。
1396
+ // MapView removeView/removeViewAt 与 OnViewDestroys 才负责真实清理。
1397
+ cancelPendingMarkerIconUpdate()
1402
1398
  }
1403
1399
 
1404
1400
  }
@@ -12,6 +12,10 @@ class MarkerViewModule : Module() {
12
12
 
13
13
  View(MarkerView::class) {
14
14
  Events("onMarkerPress", "onMarkerDragStart", "onMarkerDrag", "onMarkerDragEnd")
15
+
16
+ OnViewDestroys { view: MarkerView ->
17
+ view.removeMarker()
18
+ }
15
19
 
16
20
  // 纬度
17
21
  Prop<Double>("latitude") { view, lat ->
@@ -114,8 +114,6 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
114
114
  private var overlayContainer: UIView!
115
115
  /// 显式跟踪所有覆盖物视图(新架构下 subviews 可能不可靠)
116
116
  private var overlayViews: [UIView] = []
117
- /// MarkerView 短暂 detach 时延迟注销,避免 annotation 暂时找不到自定义 view 而回退默认样式
118
- private var pendingMarkerOverlayUnregisterTasks: [ObjectIdentifier: DispatchWorkItem] = [:]
119
117
 
120
118
  // MARK: - 事件节流控制
121
119
 
@@ -253,7 +251,11 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
253
251
  }
254
252
 
255
253
  private func registerOverlayView(_ view: UIView) {
256
- cancelPendingMarkerOverlayUnregister(for: view)
254
+ if let markerView = view as? MarkerView {
255
+ markerView.onPermanentDetach = { [weak self] detachedMarkerView in
256
+ self?.unregisterOverlayView(detachedMarkerView)
257
+ }
258
+ }
257
259
 
258
260
  guard !overlayViews.contains(where: { $0 === view }) else {
259
261
  return
@@ -262,29 +264,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
262
264
  }
263
265
 
264
266
  private func unregisterOverlayView(_ view: UIView) {
265
- cancelPendingMarkerOverlayUnregister(for: view)
266
267
  overlayViews.removeAll { $0 === view }
267
- }
268
-
269
- private func scheduleMarkerOverlayUnregister(_ markerView: MarkerView) {
270
- cancelPendingMarkerOverlayUnregister(for: markerView)
271
-
272
- let identifier = ObjectIdentifier(markerView)
273
- let task = DispatchWorkItem { [weak self, weak markerView] in
274
- guard let self = self, let markerView = markerView else { return }
275
- self.pendingMarkerOverlayUnregisterTasks[identifier] = nil
276
- guard markerView.superview == nil else { return }
277
- self.unregisterOverlayView(markerView)
278
- }
279
-
280
- pendingMarkerOverlayUnregisterTasks[identifier] = task
281
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: task)
282
- }
283
-
284
- private func cancelPendingMarkerOverlayUnregister(for view: UIView) {
285
- let identifier = ObjectIdentifier(view)
286
- pendingMarkerOverlayUnregisterTasks[identifier]?.cancel()
287
- pendingMarkerOverlayUnregisterTasks[identifier] = nil
268
+ (view as? MarkerView)?.onPermanentDetach = nil
288
269
  }
289
270
 
290
271
  private func prepareOverlayViewForHosting(_ view: UIView) {
@@ -362,8 +343,9 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
362
343
  // 🔑 处理所有覆盖物 - 从跟踪数组中移除并确保 native 对象也从地图移除
363
344
  // 🔑 关键修复:先从数组移除,再调用 super,防止 super 触发的事件回调中引用已卸载的视图
364
345
  if let markerView = subview as? MarkerView {
365
- scheduleMarkerOverlayUnregister(markerView)
366
- // MarkerView 内部也会延迟确认 annotation 移除,避免 transient detach 闪烁。
346
+ // 立即解除强引用。短暂重挂载会在 didAddSubview 中重新注册,
347
+ // 真正卸载则允许 MarkerView deinit 及时清理 annotation
348
+ unregisterOverlayView(markerView)
367
349
  } else if let circleView = subview as? CircleView {
368
350
  unregisterOverlayView(circleView)
369
351
  if let mapView, let circle = circleView.circle {
@@ -924,8 +906,6 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
924
906
  appleMapView?.removeOverlays(appleMapView?.overlays ?? [])
925
907
 
926
908
  // 清空覆盖物数组
927
- pendingMarkerOverlayUnregisterTasks.values.forEach { $0.cancel() }
928
- pendingMarkerOverlayUnregisterTasks.removeAll()
929
909
  overlayViews.removeAll()
930
910
 
931
911
  // 移除所有子视图
@@ -1,5 +1,6 @@
1
1
  import ExpoModulesCore
2
2
  import MAMapKit
3
+ import QuartzCore
3
4
  import UIKit
4
5
 
5
6
  /**
@@ -58,6 +59,8 @@ class MarkerView: ExpoView {
58
59
  var cacheKey: String?
59
60
  /// 标记是否正在被移除(防止重复移除)
60
61
  private var isRemoving: Bool = false
62
+ /// 旧架构物理移除完成后通知 Map 容器解除 overlay 强引用
63
+ var onPermanentDetach: ((MarkerView) -> Void)?
61
64
 
62
65
  // 平滑移动相关
63
66
  var smoothMovePath: [[String: Double]] = []
@@ -75,12 +78,8 @@ class MarkerView: ExpoView {
75
78
  private var pendingUpdateTask: DispatchWorkItem?
76
79
  /// 子视图变化后的延迟刷新任务
77
80
  private var pendingSubviewRefreshTask: DispatchWorkItem?
78
- /// cacheKey 变化后的延迟刷新任务
79
- private var pendingCacheKeyRefreshTask: DispatchWorkItem?
80
- /// 视图 detach 后延迟确认移除 annotation,避免 React 更新时短暂 detach 导致闪烁
81
- private var pendingAnnotationRemovalTask: DispatchWorkItem?
82
- /// cacheKey 刷新代次,用于丢弃过期截图任务
83
- private var cacheKeyRefreshGeneration: Int = 0
81
+ /// 下一次主队列确认物理 detach 是否仍然成立
82
+ private var pendingDetachCheckTask: DispatchWorkItem?
84
83
  /// 最近一次应用到 annotationView 的 children 结构签名
85
84
  private var lastRenderedChildrenSignature: String?
86
85
  /// 最近一次成功渲染的 children 图片,用于缓存未命中时避免闪烁
@@ -113,7 +112,19 @@ class MarkerView: ExpoView {
113
112
  // 始终返回 false,表示点击不在此视图内
114
113
  return false
115
114
  }
116
-
115
+
116
+ override func layoutSubviews() {
117
+ super.layoutSubviews()
118
+
119
+ guard !isRemoving,
120
+ !subviews.isEmpty,
121
+ currentChildrenAnnotationView() != nil else {
122
+ return
123
+ }
124
+
125
+ scheduleChildrenImageRefresh(invalidateChildrenCache: true)
126
+ }
127
+
117
128
  /**
118
129
  * 检查地图是否已连接
119
130
  */
@@ -165,7 +176,7 @@ class MarkerView: ExpoView {
165
176
  guard cacheKey != key else { return }
166
177
  self.cacheKey = key
167
178
  if !subviews.isEmpty {
168
- scheduleCacheKeyChildrenImageRefresh()
179
+ scheduleChildrenImageRefresh(invalidateChildrenCache: true)
169
180
  } else {
170
181
  refreshAnnotationAppearance()
171
182
  }
@@ -365,7 +376,7 @@ class MarkerView: ExpoView {
365
376
  }
366
377
 
367
378
  // 图片内容可能尚未就绪,保留旧图并延迟重试,避免透明闪烁。
368
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self, weak annotationView] in
379
+ DispatchQueue.main.async { [weak self, weak annotationView] in
369
380
  guard let self = self, let annotationView = annotationView else { return }
370
381
  guard self.isAnnotationView(annotationView, boundTo: annotation) else { return }
371
382
  // 再次检查缓存(避免重复渲染)
@@ -533,11 +544,9 @@ class MarkerView: ExpoView {
533
544
  // 强制子视图使用指定尺寸布局
534
545
  firstSubview.frame = CGRect(origin: .zero, size: size)
535
546
 
536
- // 🔑 多次强制布局,确保 React Native Text 完全渲染
537
- for _ in 0..<3 {
538
- forceLayoutRecursively(view: firstSubview)
539
- RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01))
540
- }
547
+ // React Native 完成当前挂载批次后执行一次确定性布局。
548
+ // 后续布局变化会再次合并触发快照,不需要阻塞主 RunLoop 猜测内容何时就绪。
549
+ forceLayoutRecursively(view: firstSubview)
541
550
 
542
551
  if containsPendingImageContent(in: firstSubview) {
543
552
  return nil
@@ -567,10 +576,16 @@ class MarkerView: ExpoView {
567
576
  }
568
577
 
569
578
  private func applyChildrenImage(_ image: UIImage, to annotationView: MAAnnotationView, signature: String? = nil) {
570
- annotationView.image = image
571
- applyCenterOffset(to: annotationView, defaultOffset: .zero)
572
- annotationView.canShowCallout = false
573
- annotationView.isDraggable = draggable
579
+ CATransaction.begin()
580
+ CATransaction.setDisableActions(true)
581
+ UIView.performWithoutAnimation {
582
+ annotationView.image = image
583
+ applyCenterOffset(to: annotationView, defaultOffset: .zero)
584
+ annotationView.canShowCallout = false
585
+ annotationView.isDraggable = draggable
586
+ annotationView.layoutIfNeeded()
587
+ }
588
+ CATransaction.commit()
574
589
  lastRenderedChildrenImage = image
575
590
  if let resolvedSignature = signature {
576
591
  lastRenderedChildrenSignature = resolvedSignature
@@ -645,8 +660,7 @@ class MarkerView: ExpoView {
645
660
 
646
661
  private func scheduleChildrenImageRefresh(
647
662
  invalidateChildrenCache: Bool = false,
648
- cacheImage: Bool = true,
649
- delay: TimeInterval = 0.02
663
+ cacheImage: Bool = true
650
664
  ) {
651
665
  pendingSubviewRefreshTask?.cancel()
652
666
 
@@ -655,35 +669,7 @@ class MarkerView: ExpoView {
655
669
  }
656
670
 
657
671
  pendingSubviewRefreshTask = task
658
- DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: task)
659
- }
660
-
661
- private func scheduleCacheKeyChildrenImageRefresh() {
662
- pendingSubviewRefreshTask?.cancel()
663
- pendingSubviewRefreshTask = nil
664
- pendingCacheKeyRefreshTask?.cancel()
665
-
666
- cacheKeyRefreshGeneration += 1
667
- let generation = cacheKeyRefreshGeneration
668
-
669
- lastRenderedChildrenSignature = nil
670
- invalidateCurrentChildrenCache()
671
-
672
- let uncachedTask = DispatchWorkItem { [weak self] in
673
- guard let self = self, !self.isRemoving, self.cacheKeyRefreshGeneration == generation else { return }
674
- self.refreshChildrenImageInPlace(invalidateChildrenCache: true, cacheImage: false)
675
-
676
- let cachedTask = DispatchWorkItem { [weak self] in
677
- guard let self = self, !self.isRemoving, self.cacheKeyRefreshGeneration == generation else { return }
678
- self.refreshChildrenImageInPlace(invalidateChildrenCache: true, cacheImage: true)
679
- }
680
-
681
- self.pendingCacheKeyRefreshTask = cachedTask
682
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.08, execute: cachedTask)
683
- }
684
-
685
- pendingCacheKeyRefreshTask = uncachedTask
686
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.02, execute: uncachedTask)
672
+ DispatchQueue.main.async(execute: task)
687
673
  }
688
674
 
689
675
  private func iconCacheKey(for iconUri: String) -> String {
@@ -849,18 +835,14 @@ class MarkerView: ExpoView {
849
835
  forceLayoutRecursively(view: subview)
850
836
  }
851
837
  }
852
-
853
-
854
- /**
855
- * 当视图即将从父视图移除时调用
856
- */
838
+
857
839
  override func willMove(toSuperview newSuperview: UIView?) {
858
840
  super.willMove(toSuperview: newSuperview)
859
841
 
860
842
  if newSuperview == nil {
861
- scheduleAnnotationRemovalFromMap()
843
+ scheduleDetachCheck()
862
844
  } else {
863
- cancelPendingAnnotationRemoval()
845
+ cancelPendingDetachCheck()
864
846
  }
865
847
  }
866
848
 
@@ -868,47 +850,39 @@ class MarkerView: ExpoView {
868
850
  super.didMoveToSuperview()
869
851
 
870
852
  if superview != nil {
871
- cancelPendingAnnotationRemoval()
853
+ cancelPendingDetachCheck()
872
854
  }
873
855
  }
874
856
 
875
- private func scheduleAnnotationRemovalFromMap() {
876
- cancelPendingAnnotationRemoval()
857
+ private func scheduleDetachCheck() {
858
+ cancelPendingDetachCheck()
877
859
 
878
860
  let task = DispatchWorkItem { [weak self] in
879
- guard let self = self, self.superview == nil else { return }
880
- self.pendingAnnotationRemovalTask = nil
861
+ guard let self = self else { return }
862
+ self.pendingDetachCheckTask = nil
863
+ guard self.superview == nil else { return }
881
864
  self.removeAnnotationFromMap()
882
865
  }
883
866
 
884
- pendingAnnotationRemovalTask = task
885
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: task)
867
+ pendingDetachCheckTask = task
868
+ DispatchQueue.main.async(execute: task)
886
869
  }
887
870
 
888
- private func cancelPendingAnnotationRemoval() {
889
- pendingAnnotationRemovalTask?.cancel()
890
- pendingAnnotationRemovalTask = nil
871
+ private func cancelPendingDetachCheck() {
872
+ pendingDetachCheckTask?.cancel()
873
+ pendingDetachCheckTask = nil
891
874
  }
892
-
893
- /**
894
- * 从地图移除标记点
895
- */
875
+
896
876
  private func removeAnnotationFromMap() {
897
877
  guard !isRemoving else { return }
898
878
  isRemoving = true
899
- cancelPendingAnnotationRemoval()
879
+ cancelPendingDetachCheck()
900
880
  pendingAddTask?.cancel(); pendingAddTask = nil
901
881
  pendingUpdateTask?.cancel(); pendingUpdateTask = nil
902
882
  pendingSubviewRefreshTask?.cancel(); pendingSubviewRefreshTask = nil
903
- pendingCacheKeyRefreshTask?.cancel(); pendingCacheKeyRefreshTask = nil
904
-
905
- if Thread.isMainThread {
906
- cleanupAnnotationFromMap()
907
- } else {
908
- DispatchQueue.main.sync {
909
- cleanupAnnotationFromMap()
910
- }
911
- }
883
+ cleanupAnnotationFromMap()
884
+ onPermanentDetach?(self)
885
+ onPermanentDetach = nil
912
886
  }
913
887
 
914
888
  private func cleanupAnnotationFromMap() {
@@ -975,7 +949,7 @@ class MarkerView: ExpoView {
975
949
  }
976
950
 
977
951
  pendingSubviewRefreshTask = task
978
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.02, execute: task)
952
+ DispatchQueue.main.async(execute: task)
979
953
  }
980
954
 
981
955
  private func refreshAnnotationForSubviewChanges(allowFallbackToDefault: Bool) {
@@ -1438,16 +1412,15 @@ class MarkerView: ExpoView {
1438
1412
  }
1439
1413
 
1440
1414
  /**
1441
- * 析构函数 - 不执行任何清理
1442
- * 清理工作已在 willMove(toSuperview:) 中完成
1415
+ * MarkerView React Native 注册表释放时清理原生 annotation。
1416
+ * Map 容器会在移除子视图时立即解除 overlayViews 的强引用。
1443
1417
  */
1444
1418
  deinit {
1445
1419
  // 取消待处理的任务
1446
1420
  pendingAddTask?.cancel()
1447
1421
  pendingUpdateTask?.cancel()
1448
1422
  pendingSubviewRefreshTask?.cancel()
1449
- pendingCacheKeyRefreshTask?.cancel()
1450
- pendingAnnotationRemovalTask?.cancel()
1423
+ pendingDetachCheckTask?.cancel()
1451
1424
 
1452
1425
  if Thread.isMainThread {
1453
1426
  cleanupAnnotationFromMap()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-gaode-map",
3
- "version": "2.2.41",
3
+ "version": "2.2.42",
4
4
  "description": "Expo Modules AMap (Gaode Map) stack for Expo/React Native: Config Plugin, New Architecture support, maps, location, search, offline maps, and react-native-amap3d migration guidance.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",