日常的 iOS 开发中,不知道大家有没有留意下图这个小东东?就是 Wi-Fi 图标右边的“小菊花”。
它的官方名称叫做NetworkActivityIndicator
,我一直觉得这个图标真的很有用,你可以通过这个图标判断出当前的 App 是不是在使用网络,是否在持续产生流量。我记得很早的魅族 M8 系统也有这个东西,现在换成安卓就没有了。然而这个指示器的显示与否并不是系统控制的,而是靠开发者自觉调用 UIApplication
的 networkActivityIndicatorVisible
属性来实现。(可惜现在好多开发者都不遵守,都没看见这个图标在他们的 Apps 里出现过...)
At The Very Beginning
如果说直接使用这个属性倒是十分简单,如果你用 NSOperation
来处理网络操作,你完全可以在这个 operation 的 main()
中将这个属性设为 true
,然后在请求结束时将属性再设为 false
。可是当遇到一些大批量粒度较小的请求时,你会发现用这种方法会使那个“小菊花”闪个不停,而且当多个请求并发进行时我们还需要一个类似引用计数的东西来控制何时要隐藏掉这个图标,我们今天就是来解决这些问题的。
Requirements
我们希望当一个请求开始时,等待这个请求的执行,如果这个请求持续了超过 100ms,那么说明这个请求流量就可以计算了,所以我们这时显示图标来提示用户现在正在使用网络,如果这个请求在 100ms 之内完成了,则说明这个请求所产生的流量可以忽略不计,我们就不显示这个图标了。
同样,我们还希望,在请求结束后,延时 300ms 的显示,以便让用户能够看到这个图标,也可以避免闪烁的现象。这样我们的图标看起来就很有条不紊。
The Solution
到这里,你可能会说 WTF,需求这么复杂??(这个需求并不是产品经理想的,而是我觉得应该是这样的,所以直接当成需求去做了)怎么实现啊,为了简化逻辑,我采用了状态机,如果把它画出来,整个流程就一目了然,十分清晰了:
这样一来我们就可以通过 switch
语句来进行各个状态的跳转了。
首先,我们将这些状态声明在一个枚举中:
为了处理多线程中的临界访问和简单的计时器,我顺手写了下面两个辅助函数:
这两个函数具体细节不在本文讨论范围内,就不多说了,后面我们就直接使用它们了。
然后我们实现从 Invisible -> Visible 之间的跳转逻辑:
首先我们在临界区内把相关数值进行修改,然后我们看状态机,由于 show 动作只作用于 Invisible 和 WillHide 这两个状态上,所以我们就处理这两个分支。
这里分析一下从 Invisible 到 Visible 的跳转逻辑,另外一个大家自行分析。
首先我们将状态设置为 WillShow,这也是 Invisible 状态接受到 show 动作时应该跳转到的状态,然后我们将现在正在进行的计时器 cancel 掉,并设置新计时器,也就是 100ms 后跳转到 Visible,这时图标就显示了。
Ok,同样,下面是从 Visible -> Invisible 之间的跳转逻辑:
开始这里我简单处理了 count 的越界,然后其他的基本与上面是相反的,就不解释了。
Keep Going
我们其实可以发现,这段代码其实还有可以优化的空间,就是将相关逻辑与状态真正绑定在一起,通过 Swift 的特性,我们可以为一个属性加一个简单的 Observer 函数,我们可以修改代码如下:
然后原来的跳转逻辑就真正变成状态跳转了,而实际的操作确实这个 state
变量来完成的:
以上。