移动端前后台状态通知机制实现

背景

为了支持端上网络库内部组件获取App状态,以便使用合适的策略,例如如果当前App在后台,网络库便不再去主动解析DNS结果,这样可以节省对移动端设备的资源消耗。
因此项目需要做一个移动端前后台状态判断及通知机制。由于我们支持iOS和Android,因此这个特性也分别针对双端做,上层用一个抽象类封装。

概要设计

  1. 初始化:网络库启动时,会初始化Notifier实例,但会根据不同的端实例化不同的子类。Notifier用于探测当前App状态并通过回调方式通知下游用户。
  2. 用户注册:我们提供了标准的用户回调抽象类,下游用户通过继承并实现具体的抽象方法生成新的实例,并将该实例注册进Notifier。
  3. 在状态发生变更时,Notifier会轮询一遍所有等待回调的用户(第2步注册进来的)触发用户自定义动作。

逻辑架构图

运行架构图(以Android为例)
new_front_back_thread

详细设计

前后台状态分类

1
2
3
4
5
6
7
enum AppStatusType {
APP_STATUS_UNKNOWN = 0, // Uncertain state
APP_STATUS_FOREGROUND = 1, // iOS(DidBecomeActive); Android(DidBecomeActive)
APP_STATUS_BACKGROUND = 2, // iOS(DidEnterBackgroun); Android(DidEnterBackgroun)
APP_STATUS_TERMINATING = 3, // iOS(WillTerminate); Android();
APP_STATUS_LAST = APP_STATUS_TERMINATING
};

前后台状态获取方式

  1. iOS
    使用iOS的消息通知组件NSNotification,以回调方法的形式注册进去。
  2. Android
    安卓的情况比较复杂,Android前后台 - EFFMX.COM上对获取安卓前后台状态做了一些可行性方法的介绍。
    通过一些调研分析,我们可以得出的一些结论是:
    a.安卓在前后台状态方面没有一个像iOS那样统一的消息组件。
    b.由于我们是作为一个基础组件SDK提供给App方,在安卓上能操作的能力很有限,比如文章中介绍的Activity回调方式就无法在SDK上做。

    除此之外,在跟我们的客户(Android端 App研发团队)接触后发现,我们的客户已经在App里通过Activity回调实现了前后台状态的通知机制。

    因此在Android上,最终我们选择的方式是:在SDK接入层提供一个通知接口,由App负责将状态变更通过API传递给我们。

安全策略
这里的安全策略主要是考虑到,安卓端可能会长时间收不到正确的状态变更通知,这种情况可能是由于上游App出现异常或漏推送状态导致。
因此我们做了一个兜底机制,用来防止上游App出现这种情况。

策略设计

首先,策略是基于Android前后台 - EFFMX.COM里的方法5,主动去扫描进程信息,从而获取当前App状态。
这个方法不被官方承认,并且对资源消耗会比较大(涉及循环扫描进程),因此应该尽量少用,这里只使用它用于兜底,正常状态不会被触发。

在java层实现进程扫描方法,可通过主动获取系统锁屏及进程信息方法获取到当前的app是否为前台。在Notifier中内置定时器,默认定时时间为10分钟,即如果超过10分钟未收到状态变更通知则触发保护机制。

计时器到点了以后,将会出现四种可能的情况。

  • 如果当前已经处于后台状态了,考虑到这种情况已经不会额外消耗用户的资源了,不做处理了。以下三种都是当前已经在前台的情况下。
  • 发起扫描,如果结果是前台状态,则认为当前确实是在前台,不做处理,重新计时。
  • 发起扫描,如果结果是在后台,则表明端上出现异常,则推送后台状态给用户。
  • 发起扫描,如果扫描进程的过程发生java层可捕获的异常,则当前无法确认准确状态,推送unknown状态给用户。

接口及样例

注册回调

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass : AppStatusTypeObserver {
public:
// Inherit the callback function.
void OnAppStatusTypeChanged(AppState type) {
// code in callback
}
};
MyClass* my_instance = new MyClass();
// Register the instance to notifier.
Notifier::AddAppStatusTypeObserver(my_class);

主动拉取状态

1
2
3
4
5
6
7
// Get status directly.
AppStatusType status = Notifier::GetAppStatusType();
// Get the answer that if it is the foreground status.
if (Notifier::IsAppInForeground()) {
// ...
}