前端 Notes | H5 打开 App 并跳转指定页(Android/iOS)最 low 实现

封谞
• 阅读 4317
还是技术的世界简单,除了 0 就是 1。

前端 Notes | H5 打开 App 并跳转指定页(Android/iOS)最 low 实现

前言

之前接到一个任务,大概细分如下:

  • H5 调起 App(Android/iOS) 并打开对应页面;
  • 如果应用未安装,则提示用户进行下载;
  • 微信打开该链接分享好友展示卡片样式,不使用微信 SDK 实现;
  • 通过调用微信 SDK 实现分享好友卡片形式;

忐忑的内心,又要开始前端之旅,咋整呢?

干呗。

个人工作主要偏向于 Android,所以此篇内容主要以 Android 为例,毕竟鸡老大也曾经说过,不对没涉及的领域做太多评价。

之前项目中曾经使用 scheme 来打开过指定的页面,而此时,同样打算以 scheme 入手,对于一些新奇的玩意,私下有空再去研究咯。

由于项目特殊性,这里暂时不放置动态效果图了。

一、H5 调起 App(Android/iOS) 并打开对应页面

关键的点在于移动端以及前端协定对应的协议名称以及 host 即可。

例如我们现在协定如下:

  • com.test.app://topic?id=196&code=50c20872

当然,协议名随便,不一定非要是域名,比如说,我指定个 schemeName 也可以。

而对于 Android 的小伙伴只需要在指定打开的页支持此 scheme 即可:

<activity
    android:name=".ui.activity.module.topic.TopicActivity"
    android:exported="true"
    android:launchMode="standard" 
    android:screenOrientation="portrait"> 

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="topic"
            android:scheme="com.test.app" />
    </intent-filter>
</activity>

而获取 H5 传递的参数也是贼 easy,如下:

if (Intent.ACTION_VIEW == intent.action && intent.data != null) {
    mAdvisoryId = intent.data.getQueryParameter("id").toInt()
    mPrivateCode = intent.data.getQueryParameter("code")
}

最后针对 H5 只需要跳转此 url 即可:

// 通用协议地址
var commSchemeUrl = "com.test.app://topic?id=196&code=50c20872";
window.location.href = commSchemeUrl;

二、如果应用未安装,则提示用户进行下载

最好的情况就是所有用户都安装咱开发的应用,可惜啊,又不是微信。

所以不得不考虑一个问题,如果目标用户未安装该应用,又该如何?能否间接提升产品下载安装率?

思来想去,还是觉得如果没下载直接跳转下载页 (这就是一句废话😂),毕竟你点击 H5 的目的也无非是想使用该应用罢了。

改造下 H5,其实就是抽离出个 JS 而已:

<script type="application/javascript">
    // app 下载器
    var appDownload;
    // 通用协议地址
    var commSchemeUrl = "com.test.app://topic?id=196&code=50c20872";
    // iOS 下载地址
    var iOSDownloadUrl = "https://itunes.apple.com/cn/app/id你的 iOS App ID";
    // Android 下载地址
    var androidDownloadUrl = "Android 下载地址"

    function openApp() {
        let u = navigator.userAgent;
        let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //判断是否是 android终端
        let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //判断是否是 iOS终端
        // 首次尝试打开 App 并跳转
        if (isAndroid || isIOS) {
            window.location.href = commSchemeUrl;
        }
        // 3ms 后没打开,直接跳转对应下载页面
        appDownload = setTimeout(function() {
            if (isAndroid) {
                window.location.href = androidDownloadUrl;
            } else if (isIOS) {
                window.location.href = iOSDownloadUrl;
            }
        }, 3000);
    }

    document.addEventListener('visibilitychange webkitvisibilitychange', function() {
        // 如果页面隐藏,认为打开 app,清除下载任务
        if (document.hidden || document.webkitHidden) {
            clearTimeout(appDownload)
        }
    })
    window.addEventListener('pagehide', function() {
        clearTimeout(appDownload)
    })
</script>

这里有个好玩的,就是怎么判断当前用户手机未安装当前应用呢?

请教了一番,感觉比较靠谱的回答是:

  • 添加页面对应的监听以及 3 秒后的一个定时下载任务,如果当前页面隐藏则认定为正常打开目标 App,清理定时任务,反之则跳转下载页。

对于一个小 Android 而言,贼佩服大神的思路,特意记录下。

三、微信打开该链接分享好友展示卡片样式,不使用微信 SDK 实现

坑坑巴巴写出了这个小网页,本以为开开心心提交任务开始下个任务,不料,突然接到该 H5 在微信中打开并分享好友需要显示卡片样式,而且还不能使用微信 SDK,我的天。

先附上一个现有 H5 在微信中打开并分享微信好友的展示样式:

前端 Notes | H5 打开 App 并跳转指定页(Android/iOS)最 low 实现

喏,title 是一串地址,内容也是,图片别提了。这样子肯定不行那,最起码 title 得变变吧。

鸡老大说过,翻翻其它大厂官网,看看人是怎么搞得。

哎,太菜了,没看明白,最后直接从微信公众号截取了一部分,如下:

<head>
    <meta charset="utf-8">
    <!-- 主要是强制让文档的宽度与设备宽度保持 1:1,最大宽度 1.0,禁止屏幕缩放。 -->
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <!-- 这个也是 iPhone 私有标签,允许全屏浏览。 -->
    <meta content="yes" name="apple-mobile-web-app-capable">
    <!-- iPhone 的私有标签,iPhone 顶端状态条的样式。 -->
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <!-- 禁止数字自动识别为电话号码,这个比较有用,因为一串数字在 iPhone 上会显示成蓝色,样式加成别的颜色也是不生效的。 -->
    <meta content="telephone=no" name="format-detection">
    <!-- 禁止 email 识别 -->
    <meta content="email=no" name="format-detection">

    <link rel="icon" href="https://x.png" type="image/x-icon" />

    <meta name="description" content="测试测试" />
    <meta name="author" content="HLQ" />

    <meta property="og:title" content="嗨,你的好友为您推荐了一篇不错的专题~" />
    <meta property="og:url" content="http://mp.weixin.qq.com/s?__biz=MzU2NTI4Mjc0Ng==&amp;mid=2247484817&amp;idx=1&amp;sn=f4a8758d77a9e308ac4126e30c3b41d1&amp;chksm=fcbf5744cbc8de528a75b04ae9919bc1345800601cf4201af9ec1333e118593b75eed10fdf2a#rd" />
    <meta property="og:image" content="http://mmbiz.qpic.cn/mmbiz_jpg/IZdjxwpBrBY8RsXe4Huyuibl8k3FEad3MqbdbdqJDtXM894R5N1xgHy5ZibuPYYP8BOAxE5hhYNOEhuZMcwx3Y0A/0?wx_fmt=jpeg" />
    <meta property="og:description" content="" />
    <meta property="og:site_name" content="" />
    <meta property="og:type" content="article" />
    <meta property="og:article:author" content="" />

    <meta property="twitter:card" content="summary" />
    <meta property="twitter:image" content="http://mmbiz.qpic.cn/mmbiz_jpg/IZdjxwpBrBY8RsXe4Huyuibl8k3FEad3MqbdbdqJDtXM894R5N1xgHy5ZibuPYYP8BOAxE5hhYNOEhuZMcwx3Y0A/0?wx_fmt=jpeg" />
    <meta property="twitter:title" content="嗨,你的好友为您推荐了一篇不错的专题~" />
    <meta property="twitter:creator" content="" />
    <meta property="twitter:site" content="" />
    <meta property="twitter:description" content="" />

    <title>嗨,你的好友为您推荐了一篇不错的专题~</title>
</head>

测试感觉还是直接设置 title,分享微信好友卡片 title 自动匹配了。

最后附上测试结果:

前端 Notes | H5 打开 App 并跳转指定页(Android/iOS)最 low 实现

  • 图一为正常在微信中打开并分享好友样式;
  • 图二为在 Safari 中打开并分享好友样式。

期间也尝试过网上说的一些方案,比如设置 300x300 像素 img,结果还是不尽人意。

例如下面这样:

<div style="display: none;">
    <img src="http://adv-share.oss-cn-shanghai.aliyuncs.com/0.jpg" alt="">
</div>

图片的的确确 300x300 像素了,尴尬的是,没卵用。

四、通过调用微信 SDK 实现分享好友卡片形式

这块后期移交别的小伙伴负责了,这里简单记录下,方便然后分分钟搞定~

(小伙伴记得以官网为主哈)

这里忽略微信后台配置域名以及接口内容,未参与,不做未实践的记录。

关键代码如下:

<!-- 这个需要自己下载个 js 文件 -->
<script src="/jquery.min.js"></script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>

<script type="application/javascript">
    $.ajax({
        async: true, //这里参数 true 和 false 在微信中打开会有不同效果,实际用的时候用 false,用 true 时候测试会有提示性内容。
        url: '请求域名',
        type: "POST",
        dataType: "json", // 返回的数据类型,设置为 JSONP 方式       
        data: {
            url: encodeURIComponent(window.location.href.split("#")[0])
        },
        success: function(response, status, xhr) {
            wx.config({
                debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以 在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
                appId: response.appId, // 必填,公众号的唯一标识
                timestamp: response.timestamp, // 必填,生成签名的时间戳
                nonceStr: response.nonceStr, // 必填,生成签名的随机串
                signature: response.signature, // 必填,签名
                jsApiList: [ // 必填,需要使用的 JS 接口列表
                    '需要使用的 JS 接口列表',
                ], // 需要检测的 JS 接口列表,所有 JS 接口列表见附录 2,
            });
            //ready
            wx.ready(function() { //需在用户可能点击分享按钮前就先调用
                shareData = {
                    title: "测试标题", // 分享标题
                    desc: "测试描述", // 分享描述
                    link: window.location.href, // 分享链接
                    imgUrl: "http://fa-res.oss-cn-shanghai.aliyuncs.com/images/02aKfbKPkdEwZyBr6zJbNXEjK3i3y3MfG.jpg", // 分享图标
                    success: function() {
                        // 设置成功
                    }
                };
                // 1.4.0 新接口 (只调用这个接口在安卓下是无效的)
                wx.updateAppMessageShareData(shareData);
                wx.updateTimelineShareData(shareData);
            });
        }
    });
</script>

哎,怎么说呢,扎心。

番外篇 - 关于 scheme 的简单了解

毕竟鸡老大也说过,多去深挖,不要停留表面。正好好好看看这块内容,查漏补缺。

提到 scheme,不得不提 intent-filter,这里就拿之前 Android 配置为例:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
        android:host="topic"
        android:scheme="com.test.app" />
</intent-filter>

intent-filter 解释 (摘自官方文档)

指定 Activity、服务或广播接收器可以响应的 Intent 类型。Intent 过滤器声明其父组件的功能 Activity 或服务可执行哪些操作,以及接收器可处理哪些类型的广播。它让组件可以接收所通告类型的 Intent,同时过滤掉对组件没有意义的 Intent。

过滤器的大部分内容由它的 action、category 和 data 子元素进行描述说明。

大白话意思就是,根据你指定的某种规则去执行特定的某些操作。

例如,此例子中,支持外部通过打开 URL(例如:com.test.app://topic?id=196&code=50c20872) 的形式去打开此 App 对应指定页面并执行对应操作一样。

在 intent-filter 中,包含如下三个属性:

  • android:icon: 表示父 Activity、服务或广播接收器的图标,在将该组件以具备过滤器所描述功能的形式呈现给用户时显示。默认值为父组件的 icon 属性设置的图标。如果父组件未指定图标,则默认值为 application 元素设置的图标。
  • android:label: 父组件的用户可读标签。将相应组件以具备过滤器所描述功能的形式呈现给用户时,将使用此标签(而不是父组件设置的标签)。 默认值为父组件设置的标签。如果父组件未指定标签,则默认值为 application 元素的 label 属性设置的标签。
  • android:priority: 就处理过滤器所描述类型的 intent 而言,应该为父组件指定的优先级。此属性对 Activity 和广播接收器都有意义:

    • 它说明了某个 Activity 对与过滤器匹配的 Intent 的响应能力,这是相对于也可以响应该 Intent 的其他 Activity 的响应能力。当 Intent 可由优先级不同的多个 Activity 处理时,Android 只会将优先级值较高的 Activity 视为 Intent 的潜在目标。
    • 它控制按什么顺序执行广播接收器以接收广播消息。优先级值越高,调用顺序越靠前。(该顺序仅适用于同步消息;对于异步消息,系统会忽略该顺序。)
    • 注意⚠️:值必须是一个整数,如“100”。数值越高,优先级也就越高。默认值为 0

在过滤器中必须包含以下属性:

  • action: 在 name 属性中,声明接受的 Intent 操作。例如这个例子中我们通过隐式启动了我们目标 Activity。

以及如下可选参数:

  • category: 在 name 属性中,声明接受的 Intent 类别。例如这个例子中我们设置了默认支持浏览器打开该页面。
  • data: 使用一个或多个指定数据 URI(scheme、host、port、path)各个方面和 MIME 类型的属性,声明接受的数据类型。例如这里我们声明了打开的规则。

    • URI 的每个部分都是一个单独的属性:scheme、host、port 和 path。例如 content://com.example.project:200/folder。在此 URI 中,scheme 是 content,host 是 com.example.project,端口是 200,路径是 folder。

啊哈,拖了好久好久,终于完善啦~

端午快乐呀~

Thanks

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
梦
4年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Android原生和H5交互;Android和H5混合开发;WebView点击H5界面跳转到Android原生界面。
当时业务的需求是这样的,H5有一个活动商品列表的界面,IOS和Android共用这一个界面,点击商品可以跳转到Android原生的商品详情界面并传递商品ID; 大概就是点击H5界面跳转到Android原生界面;好了,需求已经分析完毕了,Android只需要获取H5的点击事件和传递的参数;来,上代码:/启用支持javascript
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这