App-Callback-Mark

App 之间回调是常有的事, 特别是一些提供第三方登录/第三方支付的 App, 更是需要提供调起, 登录/支付, 回调原 App 的功能. 在实现的过程中遇到一些问题, 所以记录一下.

接下来调起界面称为 CallActivity, 调起 App 称为 Call App , 被调起页面称为 PayActivity. 被调起 App 称为 Pay App.

常见方案: startActivityForResult

最常见的当然是 通过 startActivityForResult() 使用隐式 Intent 调起, 然后在 onActivityResult() 中捕捉回调并处理成功失败逻辑了. 这种方法大部分人都会, 说点遇到的问题吧:

  • 问题来了

    • 问题一:
      • 打开 Pay Activity 后可以切到后台, 再切回来, 这样做一方面安全性不够, 另一方面不符合支付工具的特性. 第三方应用调起支付应用后, 对用户的感觉不应该是完整的打开了一个应用, 而应该是仅仅启动了支付的一个功能, 切到后台后应该无法再切回该支付页面, 最近打开的应用页面也不该展示 Pay App.
      • AndroidManifest.xml 中加上 android:excludeFromRecents="true"
    • 问题二:
      • 支付成功后, 按 Home 键切到后台, 再切回 Call App, 这时候没有触发 onActivityResult()
      • 这是系统问题, 正常逻辑, 只能在 onResume() 里面查询后台是否成功.
    • 问题三:
      • 如果已经打开了 Pay App, 然后切到后台, 打开到 Call App, 调起 Pay Activity 后按返回键, 返回到 Pay App 的界面了.
      • 需要指定 PayActivity 的 luanchMode 为 singleInstance
  • 大坑来了

    坑就在于这个 singleInstance, 在 Android 5.0 上一切正常, 但是在 Android 4.4 及以下版本, Call Activity 调用 startActivityForResult() 后, 直接回调了 onActivityResult(), 然后才打开 Pay Activity.

    为此我记录了一下不同 luanchMode 对 Android 4.4 及以下版本回调的影响.

    正常回调 -> Y

    直接回调 -> N

       | Call Activity  | Standard | SingleTop | SingleTask | SingleInstance 
      :---: | :---:  | :---: | :---: | :---: | :---: 
      Pay Activity  |
      Standard | | Y | Y | Y | N 
      SingleTop | | Y | Y | Y | N 
      SingleTask | | N | N | N | N 
      SingleInstance | | N | N | N | N 
  • startActivityForResult 方案不可行

    对于 startActivityForResult() 来说, 想实现对 singleInstance 的回调是不可能了, 同时还有问题二也需要优化, 所以最好还是换个方案来执行.

透明中间页 + 广播方案

之前版本由于已经发布, 需要兼容旧版本, 同时为了优化问题二, 考虑在接入 sdk 中提供一个中间页面 Entry Activity. 另外对于 singleInstance 的问题, 考虑用广播来替代.

Call Activity -> Entry Activity -> Pay Activity

  • 各页面功能如下:

    Call Activity

    • 依旧执行 startActivityForResult 方法, sdk 内部直接调起 Pay Activity 改为调起透明 Entry Activity.

    • 在 onActivityResult 中处理回调逻辑.

      Entry Activity

    • 设置 theme 为透明, 同时取消进入和退出的动画. 设置 luanchMode 为 singleTop

    • 注册广播, 监听 Pay Activity 发来的支付成功的广播

    • 直接通过 startActivity 调起 PayActivity

    • 在第二次进入 onResume() 时判断是否收到了支付成功的广播, 否则当做支付失败处理.

    • 成功及支付都通过 setResult() 的方式回调

      Pay Activity

    • 支付成功使用广播通知.