解决问题的思路很简单,在 onDestroy() 的时候先保留 handler 对象,然后找个时机清除一下 。采用 viewIdSet 自己维护一份 View 的数据 。在 creat 方法中 disposeArgs.get("id") 执行过 dispose 方法的就删除掉 viewIdSet.remove(viewId) 。setPlatformViewsHandler 为空的情况判断一下,有没有执行 dispose 的 view handler 先不回收 。如下:
public void setPlatformViewsHandler(@Nullable PlatformViewsHandler handler) {if(handler == null && viewIdSet != null && viewIdSet.size() > 0) {needReset = true;return;}this.handler = handler;}目前是执行 dispose 的时候 needReset 为 true 时会将 handler 设置为 null 。为什么官方的 Demo 是没有问题的呢?主要原因还是我们接入了 FlutterBoost 默认是单引擎的,官方 Demo 是的纯 Flutter 项目多引擎 。页面结束,通过销毁 engine 把问题覆盖了,所以内存回收表现的很平滑 。
五、自定义文本 BitMap Marker地图业务中自定义 marker 是比较常见的需求,由于地图是通过 PlatformView 实现的,最容易想到的做法是,通过 Channel 传入 marker 对应的样式 Id 和展示所需数据,在各端绘制 marker,这种做法会增加人工成本,样式也可能存在不一致的情况,失去了 flutter 框架的优势 。
地图插件在 v3.0(v3.0 之前需要自己实现)提供了 iconData 参数传入图片 data 信息,在 flutter 侧将文本、图片绘制出来生成一张图,将生成图片 Data 传递给原生,该实现并不需要改动各端代码,绘制时要注意视图大小是物理像素点,而不是逻辑像素点 。
Future<Uint8List?> customMark(String name, BuildContext context) async {final scale = MediaQuery.of(context).devicePixelRatio;final recorder = PictureRecorder();final canvas = Canvas(recorder);final paint = Paint();final textPainter = TextPainter(textDirection: TextDirection.ltr);...final path = Path();canvas.drawPath(path, paint);// 绘制图片final imageInfo = await UIImageLoader.imageInfoByAsset(HotelListImage.mapPoiMark);paintImage(canvas: canvas,rect: rect,image: imageInfo.image);// 生成绘制图片final image = await recorder.endRecording().toImage(width.toInt(), (textBgHeight + arrowHeight + iconHeight + 2).toInt());final data = https://www.isolves.com/it/cxkf/bk/2023-07-07/await image.toByteData(format: ImageByteFormat.png);return data?.buffer.asUint8List();}从 flutter 2 升级到 flutter 3 出现了小插曲,iOS debug 环境调用 toImage 进程会被终止 。flutter 升级之后对弱引用指针调用做了线程检查,创建和使用不是在同一线程在 debug 环境进程会被终止 。toImage() 方法内使用了 fml::WeakPtr<SnapshotDelegate> snapshot_delegate 弱引用指针,由于 snapshot_delegate 在 raster 线程中被创建,正常调用也应该是在 raster 线程,当在 flutter 页面中嵌入 PlatformView 时,为了保证渲染的一致性,会将 raster 线程与主线程合并,造成了 snapshot_delegate 在主线程调用的情况,触发了线程检查终止进程,但并不影响 release 环境 。
class WeakPtr {T* operator->() const {CheckThreadSafety();return get();}}if (0 == pthread_getname_np(current_thread, actual_thread,buffer_length) &&0 == pthread_getname_np(self_, expected_thread, buffer_length)) {FML_DLOG(ERROR) << "IsCreationThreadCurrent expected thread: '"<< expected_thread << "' actual thread:'" // Object被创建的线程<< actual_thread << "'";// 实际执行线程}六、自定义让 Marker 展示在可见范围在地图上添加 marker 之后,将已添加的 marker 全部展示在可视范围内也是常见的需求 。插件提供了支持 iOS 的 showmarkers 方法,这显然不能够满足需求 。我们思考通过 setVisibleMapRectWithPadding 指定显示地图地理范围,该方法要求我们传入参数 visibleMapBounds,设置地理范围的东北坐标、西南坐标 。由于右上角、左下角经纬度分为可视地理范围最大、最小,即可拿到东北、西南坐标 。
BMFCoordinateBounds? getMarkersVisibleMapBounds(List<BMFMarker> markers) {if (markers.isEmpty) return null;final firstPosition = markers.first.position;double maxLatitude = firstPosition.latitude;double minLatitude = firstPosition.latitude;double maxLongitude = firstPosition.longitude;double minLongitude = firstPosition.longitude;for (final marker in markers) {final lat = marker.position.latitude;final lon = marker.position.longitude;maxLatitude = max(maxLatitude, lat);minLatitude = min(minLatitude, lat);maxLongitude = max(maxLongitude, lon);minLongitude = min(minLongitude, lon);}return BMFCoordinateBounds(northeast: BMFCoordinate(maxLatitude, maxLongitude),southwest: BMFCoordinate(minLatitude, minLongitude));}随着业务的迭代,需要将大地图融合到列表中 。为了将大地图与小地图切换动画更加流畅,当小地图被加载时,地图 size 实际已经渲染成和大地图同样大小,下半部分被列表遮挡 。这意味小地图需要设置可见范围的偏移量,但 inserts 参数 iOS、Android 计算方式不一样,iOS 是根据 point 计算,Android 是通过 pixel 计算,要区分平台做一次转换 。
推荐阅读
- 今天来了解一下什么是导热硅脂吧
- 算力正在狂飙,未来需更多联想
- 夏季钓鲫鱼,钓位不能胡乱选,大鲫鱼就在2种位置
- 何洁参加《天赐4》,在台上开怼评委,不接受对方意见遭吐槽
- 袁泉树立新标杆,无视网友大胆开麦,背后究竟是谁在撑腰?
- 闷竿高手的方法,持续诱鱼,不断上鱼,学会也是高手
- 大清铜币户部现在什么价位 最高成交价500多万
- DNF7.06夏日版本终于在体验服露脸了
- 事实证明,“飞扬跋扈”的周冬雨,已经在另一个圈子里如鱼得水
- 在职场,叫苦不如吃苦,生气不如争气
