移动 H5 唤起 APP

场景

业务上有这样一个需求:内部对象链接在第三方 H5 页面(移动浏览器、微信、钉钉等)打开后,跳转到一个 H5 中转页面, 用户可以通过点击页面上的 “打开 APP 查看” 按钮来唤起 APP 并进入 APP 内的目标对象。

思路

  • 实现 H5 页面唤起手机 APP,需要借助于 APP 的 URL Schema 协议(应用间的通信方式),这个是由 APP 端开发同事提供;
  • 我们判断运行环境来区分是 Android / iOS 端,调用对应的 Schema 协议来唤起 APP(有的公司两端提供的 Schema 格式相同);
  • 由于技术上无法监测 Schema 是否成功唤起了 APP,可通过计时器在指定时间后跳转至下载界面,指引用户去下载(视为唤起失败、本地未安装此 APP);
  • 另外,比如第三方如 在微信浏览器下使用 Schema 会不起作用(可能微信现在提供了相关标签可以实现),因此需要判断微信环境,去提示指引用户点击右上角”...“在默认浏览器打开。

URL Schema 协议格式

URL Schema 是一种页面内跳转协议,通过这个协议可以比较方便的跳转到 APP 某一个对象页面。

一个简单的 Schema 协议格式如下:

[scheme]://[path]?[query]
scheme: 协议名称(由开发人员自定义)(必要,其他都是可选)
path: 页面路径
query: 请求参数
复制代码

例如,可以这样调用 Schema:

<a href="tenx://file?type=folder&id=ESC">打开 APP 查看</a>
复制代码

应用

1、内部应用(理解为企业员工账户登录的应用)跳到 H5 中转页: 在内部应用可以判断运行环境是否在移动 H5 应用,将内部应用链接作为 query 参数,跳转至 H5 中转页做唤起 APP 处理;

这里我们可以使用高阶组件做处理,比如 React 可以这样使用:

<Route exact path='xxx' render={props => <MobileInterceptor {...props} component={App} />} />


function isRunMobile() {
  return /Android|webOS|iPhone|iPad|BlackBerry/i.test(window.navigator.userAgent);
}

const isMobile: boolean = isRunMobile();

type TProps = {
  component: React.ComponentType<any>;
} & RouteComponentProps;

const MobileInterceptor = ({ component, ...otherProps }: TProps) => {
  if (isMobile) {
    const { origin, href } = window.location;
    window.location.href = 'H5 唤起APP 页面 url + 当前页面信息作为参数传递';
    return null;
  }
  return React.createElement(component, otherProps);
}
复制代码

2、H5 中转页通过按钮唤起 APP:
当点击按钮时:

  • 首先获取运行环境:在微信、钉钉等内置浏览器打开时,指引在手机默认浏览器打开;
  • 当处于手机独立浏览器时,首先设置唤起 APP 的加载动画;
  • 通过 window.location.href 去访问协议唤起 APP;
  • 定义计时器在合适的时间去跳转至下载 APP 界面(视为未唤起成功)。
export function isRunThirdPartyApp() {
  const userAgent = navigator.userAgent.toLowerCase();
  return {
    isWechat: /MicroMessenger/i.test(userAgent),
    isQQ: /QQ/i.test(userAgent) && !userAgent.toLowerCase().includes('mqqbrowser'),
    isWebo: /WeiBo/i.test(userAgent),
    isDing: /DingTalk/i.test(userAgent),
    isMail: /Mail/i.test(userAgent),
  }
}

const [status, setStatus] = useState<'default' | 'openInBrowser' | 'loading'>('default');

const openApp = () => {
  const runEnvs = isRunThirdPartyApp();
  let isOpenInBrowser = Object.keys(runEnvs).some(envKey => runEnvs[envKey as keyof typeof runEnvs]);

  if (isOpenInBrowser) {
    setStatus('openInBrowser');
    return;
  }

  if (status === 'loading') return;
  setStatus('loading'); 

  window.location.href = 'Schema url';
  setTimeout(() => {
    if (!docHidden.current) {
      window.location.href = 'Download url';
    }
    docHidden.current = false;
    setStatus('default');
  }, 3000);
}
复制代码

需要注意一处:上面我们使用 docHidden.current 来判断是否要跳转,当成功唤起 APP 时,页面会被隐藏,此时视为成功打开 APP,不再跳转至下载界面。

const docHidden = useRef<boolean>(false); // 页面是否切换,假设成功唤起 APP 后,视为页面被隐藏,则不跳转下载页面

useEffect(() => {
    const handleVisibilityChange = () => {
      setStatus('default');
      if (document.hidden) docHidden.current = true;
    }
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    }
  });
复制代码

问题记录

在 Android 小米手机浏览器内进行唤起 APP 操作时,遇到这样一个问题:

  • 当手机后台没有运行 APP 时,在小米浏览器可以正常唤起 APP 并打开目标对象;
  • 当手机后台运行了 APP 时,在小米浏览器进行唤起时,会发现没有打开 APP,而是在小米浏览器上新开了一个窗口来打开目标对象;
  • 经过排查后发现,相同的调用 Schema 方式,京东的 Schema 唤起没有问题,而我们要唤起的 APP 却存在问题;
  • 最后安卓同事那边排查问题,在代码和配置上兼容处理了小米浏览器通过 Schema 发起唤起 APP 的动作支持。

Guess you like

Origin juejin.im/post/7077520049963008013