1、前言
Flutter 所使用的 dart 语言具有垃圾回收机制,有垃圾回收就避免不了会内存泄漏。在 Android 平台上有个内存泄漏检测工具 LeakCanary ,它可以方便的在 debug 环境下检测当前页面是否泄漏。本文将会带你实现一个 flutter 可用的 LeakCanary,并讲述我是怎么用该工具检测出了 1.9.1 framework 上的两个泄漏。
Flutter 所使用的 dart 语言具有垃圾回收机制,有垃圾回收就避免不了会内存泄漏。在 Android 平台上有个内存泄漏检测工具 LeakCanary ,它可以方便的在 debug 环境下检测当前页面是否泄漏。本文将会带你实现一个 flutter 可用的 LeakCanary,并讲述我是怎么用该工具检测出了 1.9.1 framework 上的两个泄漏。
本文不讨论混合栈,只讨论纯 flutter 栈,代码基于 1.12.13,1.17 上 navigator 有做大量改动,不适用于在文章里面分析(源码量大),原理上相同。阅读本文前请先阅读 谈谈 flutter 的 build
一般来说,flutter 的页面都是由 Navigator
这个组件来组织的,一个页面对应一个 Route<T>
。不过我看到许多业务方同学对于这个页面的理解有些偏差,比如以下几个问题:
PageRoute<T>
的 build 函数是否会多次执行?(比如常用的 MaterialPageRoute<T>
里面的 build 函数)本文分为两篇,讲述 flutter 中的指针事件和手势处理逻辑
Listener 和 GestureDetecotor 是 flutter 中和手势事件相关常用的两个 widget 了,不过这两个还是有很大的差别的。
Listener 是一个纯粹的 指针事件(pointer event) 消费者,它能消费的事件有:onPointerDown, onPointerMove, onPointerUp, onPointerCancel..(这里忽略鼠标事件和不连续事件)*。它不识别任何手势,所以不存在两个 *Listener 竞争同一个指针事件的情况。
GestureDetector 就是处理手势的一个小部件了,它内部其实也是先通过 Listener 来监听基础的指针事件,然后把指针事件转化为不同的手势。因为手势会存在冲突,所以 GestureDetector 有个处理冲突的逻辑,比 Listener 复杂一些。
接下来我将会详细解析下 Listener 的一些处理逻辑,GestureDetector 的讲解放到下一篇文章中。
Flutter 是一个跨平台开发的工具,它极速的开发方式、Native 的表现性能、开源的代码等特点吸引了业界众多开发者的注意。不过由于 Flutter 处于萌芽发展阶段,还不是很完善,比如本文需要探讨的混合栈实现就是 Flutter 其中的一块短板,目前没有一套官方的解决方案,都是业界开发者在试水
这里简单介绍下 Flutter 的混合栈问题:Flutter 的界面是需要 Native 的容器来承载显示的,比如 Android 里面的 Activity,iOS 里面的 ViewController。现有的 app 不可能全部改成 flutter 实现,所以需要将 flutter 接入现有的 app 中,如果此时从 Native 页面跳转到 Flutter 页面,默认的方式会重新初始化一个 Flutter 实例运行,不会复用之前创建的 Flutter 实例
具体细节在之前的一篇混合栈文章 Flutter混合栈管理 里面讲述了
我之前开发过一个混合栈插件来解决 flutter 的混合开发问题,最后在使用的时候会碰到以下问题:
Navigator
为了解决上述问题,需要重新设计 Flutter 混合栈插件,下面来探讨下怎么解决上述两个问题
Flutter 是谷歌开发的一款可以跨平台开发的 UI 框架,它的原理接近于游戏引擎,目的在于统一 Android/iOS 两端开发,Flutter 页面有自己的栈,正常情况下,如果一个 app 完全由 Flutter 构成,那么只需要一个 FlutterView
即可。
上述方案只适用于一些新构建的 app,对于一些已有的 app,是不可能用 flutter 来重构的,成本太大,周期太长,所以这里需要实现一套 Native 页面栈和 Flutter 页面栈的管理方案,混合栈。
Android 的插件化开发,这个坑非常深,其中有一个问题就是 bundle 和 host 的版本不一致性问题,如果 bundle 中 sdk 的版本和 host 中 sdk 版本不一致,就很有可能出现 api 兼容性问题,导致运行时 crash。
一开始会想:”让 host 和 bundle 中的版本号抽离成一个文件不就行了?“
答案肯定是不行,因为这样只能让直接依赖的版本一致,不能让传递依赖的版本一致化
本文探讨下如何在 Android 上实现阻尼动画,首先 wiki 下阻尼的定义:是指任何振动系统在振动中,由于外界作用(如流体阻力、摩擦力等)和/或系统本身固有的原因引起的振动幅度逐渐下降的特性,也就是阻尼系统由两个子系统组成(振子系统、阻力系统),先前写过一篇 如何优雅地在Android上实现iOS的图片预览 ,就是一套阻尼动画的实现
不知道阻尼动画效果的同学可以去玩玩 iOS,iOS 的 UI 系统内置一套物理引擎,几乎所有的滚动界面都有阻尼动画效果(越界回弹)
在讨论阻尼动画之前,我先讲一下本人编写动画的历程,分为 3 个阶段(位置突变动画,位置不突变动画,速度不突变的动画)
此动画是 Android 里面最简单的动画,我们平常写的 ValueAnimator 动画、xml 动画,其实都是位置突变的动画,为什么叫它位置突变的动画呢,比如我要把一个 View 渐变出来,那么 alpha 数值应该是从 0 -> 1,但是假如在渐变动画还没有结束的时候(假如刚好播放到 0.5),我又想把 View 给渐隐掉,此时一般会播放一个 1 -> 0 的动画,这里就产生了位置突变效应(从 0.5 突变成了 1),造成的视觉效果就是视图闪动(等同于游戏里面的跳帧)
在上述动画的基础上,对于第二段的渐隐动画,我不从 1 -> 0 开始变化,我在播放渐隐动画前先获取 View 的透明度(0.5),然后让动画从 0.5 -> 0 开始变化(视觉上没有了闪动现象)
位置不突变动画一般都能满足要求了(系统自带的 ViewPropertyAnimator
就是位置不突变动画),但是为什么 iOS 的动画就看起来这么舒服,而安卓的动画看起来有点死板呢?这边就存在一个速度突变的问题