网页调用 iOS/Android 客户端

简介: 无论 iOS 还是 Android 都不约而同地支持 URI Scheme(扫盲帖)来作为页面与客户端的通讯协议。这里的 URI Scheme 前缀不是一般的 http://,而是由客户端开发者定义的,一般在写程序的时候就会设置的了。

无论 iOS 还是 Android 都不约而同地支持 URI Scheme(扫盲帖)来作为页面与客户端的通讯协议。这里的 URI Scheme 前缀不是一般的 http://,而是由客户端开发者定义的,一般在写程序的时候就会设置的了。然后剩下的部分就像普通的 URL 地址一样,需要大家来约定 URI Scheme 具体如何,例如参数是什么等等,好比网易新闻客户端的是以 newsapp:// 为前缀:

<a href="newsapp://"> 打开网易新闻客户端</a>

如此便可以从网页打开客户端了。这只是最简单的 Scheme 部分,除此之外,应该还有更多的参数,一般要约定好,让网页开发者和客户端开发者两者之间可以相互调用。

这种情况,当然是假设用户已经安装了客户端,进而才会自动打开。那么,问题就来了,没有安装怎么办?以及最关键的一点——如何判断客户端是否已经安装呢?如果没有安装的话,我们可以提示提示用户去下载(跳到相应的下载页面)这个比较简单;但是,如何判断客户端是否已经安装呢?遗憾的是,浏览器并没有一个方法可以告诉你哪个客户端安装了,哪个没有安装!因此,如果按照上述 a 链接直接跳转而用户又没有安装客户端的话,浏览者看到的是一张空白的页面,——用户体验很差哦~于是我们一般采用“障眼法”,也就是把“隐藏”的 iframe 派上场,用 iframe.src 指向 URI Scheme 地址就可以了,既可以尝试打开客户端,又可以停留在当前页面上。

不过,我们也可以不停留也页面上——因为我们总是鼓励使用客户端的,所以这时候可跳转到下载客户端的页面。这是在没有安装的情况下的做法。如果客户端已经安装了,那就可以不跳转。

具体逻辑如下:

<iframe class="openApp hide" src="about:blank"></iframe>
<script>
	function openClient(el, downloadUrl){
		var iframe = arguments.callee.iframe;
		
		if(!iframe){
			iframe = document.querySelector('.openApp')
			arguments.callee.iframe = iframe;
		}
		var startTime = +new Date();
		iframe.src = el.getAttribute('data-scheme');
		
		// 如果已知安装的了,则无须跳转下载页
		setTimeout(function() { // 如果不能打开客户端,跳到下载页面
	        if (Date.now() - startTime < 500) // 通过判断触发的时间与执行settimeout的时间差值是否小于设置的定时时间加上一个浮动值(一般设为100)。
	        	window.location.href = downloadUrl;
	    }, 400);
	}
	openClient = openClient.delegate(null, 'http://g.3gtv.net');
</script>
稍有点不足的就是,iOS 上面,如果没安装客户端,那么这段自定义的 URL Scheme(如 newsapp://) 就被视为"无效的链接“,弹对话框出来!(貌似 腾讯视频可以解决该问题哦,点击这里看页面——无奈的就是代码经过混淆的,不易察觉) 更新代码后,没有发现这个问题了。如果还是会这样的话,可以尝试用 script tag 发起请求,看能不能规避这个弹出框。

最后我们遇到一个下载页面的问题,由于微信内部浏览器限制的原因,不允许下载 apk 文件或者访问 app store,除非腾讯旗下的“应用宝”才可以。我们对腾讯这一封闭的做法十分不齿,却又无可奈何。于是只能默默地“归化”应用宝。好了,用就用呗——本来跳到下载页面是没有问题的。但令人觉得蛋疼的是,应用宝页面居然自己又会调起客户端(如果安装过的话)!这样势必就是“二次打开”客户端!要规避这个问题,一、不直接跳掉应用宝下载页面;二、改 UI,提供两个按钮,一是打开客户端的,另外一个是下载。

后记,还是兼容性问题:

  • 发现三星 note3 系统浏览器竟然不能 iframe 调起!
  • QQ 浏览器杯具了,竟然对 URI Scheme 一点都不支持!如下图所示,对此,优酷的做法是干脆把 打开客户端 的按钮隐藏掉。



<%@tag pageEncoding="UTF-8" import="com.ajaxjs.bigfoot.*" %>
<%@ attribute name="download_url" required="true" description="应用的下载页面,for Android used"%> 
<%@ attribute name="app_store_id" required="true" description="苹果商店的之 id,当前为 iphone 版,有 ipad 版吗?"%> 
<%@ attribute name="URI_Scheme_iOS" required="true" description="iOS 的 URI_Scheme"%> 
<%@ attribute name="URI_Scheme_Android" required="true" description="安卓的 URI_Scheme"%>
<!-- 按钮 单击事件 -->
<!-- <a class="openBtn">打开客户端</a> -->

<iframe id="ifr" style="display: none;border:0;" src="javascript:void(0)"></iframe>
<script>
	;(function(window, doc){
		
		/**
		 * 读取 search 和 hash 的参数
		 */
	    function localParam(search, hash){
	        search	= search	|| window.location.search;
	        hash	= hash		|| window.location.hash;
	        
	        var fn = function(str, reg){
	            if(str){
	                var data = {};
	                str.replace(reg,function( $0, $1, $2, $3 ){
	                    data[ $1 ] = $3;
	                });
	                return data;
	            }
	        }
	        
	        return {
	        	search: fn(search,	new RegExp( "([^?=&]+)(=([^&]*))?", "g" ))||{},
	        	hash:   fn(hash,	new RegExp( "([^#=&]+)(=([^&]*))?", "g" ))||{}
	        };
	    }
		
	    // 应用注册的URI Scheme 
	    // 当发现没有安装应用的时候,跳转到 WAP 的下载页面
	    
	    window.scrollTo(0,1);
	    var params = localParam(),
	        isIDevice = (/iphone|ipod/gi).test(navigator.platform),
	        isIDeviceIpad = (/ipad/gi).test(navigator.platform),
	        isAndroid = (/Android/gi).test(navigator.userAgent),
	        isWeixin = (/MicroMessenger/ig).test(navigator.userAgent),
	        isappinstalled = params.search['isappinstalled'],
	        appinstall = params.search['appinstall'],
	        wxLink = 'weixinfallback.html',
	        iDownload = 'itms-apps://itunes.apple.com/cn/app/<%=app_store_id%>?mt=8',
	        openAppLink = params.hash['url'] || params.search['url'],
	        iframe = document.getElementById('ifr');
	        
	    if( (isIDevice || isIDeviceIpad) && !isAndroid){
// 	    	openAppLink = openAppLink || 'smcyuetv://';
	    	openAppLink = openAppLink || '<%=URI_Scheme_iOS%>';
	    }else if(isAndroid){
// 	    	openAppLink = openAppLink || 'yuetv://';
	    	openAppLink = openAppLink || '<%=URI_Scheme_Android%>';
	    }
		
	    if(isappinstalled!==undefined){
			wxLink += '?isappinstalled='+isappinstalled+'&openurl='+openAppLink;
		}else if(appinstall!==undefined){
			wxLink += '?appinstall='+appinstall+'&openurl='+openAppLink;
		}else{
			wxLink += '?openurl='+openAppLink;
		}
		
		if(isIDeviceIpad){
	        //iDownload = 'itms-apps://itunes.apple.com/cn/app/id425349261?mt=8';
	    }	
	    function download(){
	        // if(isAndroid){
	            // window.location = 'http://3g.163.com/m/android/software/2vbrks.html';
	        // }else{
	            window.location ='<%=download_url%>';
	        // }
	    }
	    /*
	     * iOS点击打开:
		1.如果是微信就去引导图页面
		2.如果不是微信就走安装就打开不安装就去app store
		3.如果微信用户按引导图从浏览器打开就能走通第2条
		
		android点击打开:
		1.如果是微信就在打开的时候同时跳转到有图的引导页
		2.如果不是微信就同时跳转到公公共下载页
		3.如果微信用户按引导图从浏览器打开就能走通第2条
	     */
	    function open(){
	    	if(isWeixin){
	    		window.location = wxLink;
	    	}else if((isIDevice || isIDeviceIpad) && !isAndroid){
	            window.location = openAppLink;
	            setTimeout(function(){
	            	window.location = iDownload;
	            }, 50);
	    	}else{
	    		iframe.src = openAppLink;
	            download();
	    	}
	    }
	    
	    document.querySelector(".openBtn").addEventListener("click", open, false);
	    
	    // 自动打开
	    var autoopen = params.search['autoopen'] || params.hash['autoopen'];
	    autoopen == 1 && open();
	})(window,document);
</script>
其实,只要是在页面,就应该可以通过 URI Scheme 调用客户端。那么,在 WebApp / Hyper App 也能如是作法吧!

另外,我这里只是着重讨论的网页的部分。调用客户端还是比较简单的,如果需要调用客户端里面的某个一个模块呢?客户端可不想网页那样,天生就是有 URL 定位,传个地址或参数就可以了。这里涉及的客户端原生程序的部分比较多,就是说,如果直接跳到某个模块里面去,——这涉及到客户端模块是否已经合理解耦了,以及所依赖的上下文参数等等诸多问题,——需要和客户端开发者好好商量才行。

参考:

目录
相关文章
|
1月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
22 1
|
30天前
|
搜索推荐 Android开发 iOS开发
安卓与iOS操作系统的发展与比较
在移动互联网时代,安卓和iOS两大操作系统在智能手机市场竞争激烈。本文将从技术架构、生态系统、用户体验等方面对安卓和iOS进行比较分析,探讨它们各自的特点和发展趋势。
|
1月前
|
Web App开发 前端开发 网络安全
前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用
【2月更文挑战第21天】前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用
58 1
前端分析工具之 Charles 录制 Android/IOS 手机的 https 应用
|
1月前
|
人工智能 算法 Android开发
探索未来:Android与iOS在人工智能时代的融合与创新
【2月更文挑战第13天】 在数字化时代的快速发展下,Android与iOS作为两大主流移动操作系统,它们在人工智能(AI)领域的融合与创新已成为推动科技进步的关键力量。本文将从操作系统的核心功能拓展、AI技术的集成应用,以及开发者生态系统的演变三个维度,深入探讨Android和iOS如何在AI时代实现协同发展,以及这一进程对用户体验、应用开发和行业趋势产生的深远影响。通过对比分析和案例研究,我们旨在揭示两大平台在AI驱动下的创新路径,及其对未来科技格局的塑造作用。
|
1月前
|
人工智能 自然语言处理 语音技术
探索未来:安卓与iOS在人工智能领域的竞争与合作
【2月更文挑战第12天】本文深入探讨了安卓和iOS两大操作系统在人工智能(AI)领域的发展现状、竞争态势及未来合作可能性。通过对比分析两系统在AI技术集成、开发者支持、用户体验优化等方面的表现,揭示了它们各自的优势与挑战。文章最终展望了一个既有竞争又充满合作的未来,认为安卓和iOS的共同进步将推动整个人工智能技术向前发展,为用户带来更加智能、便捷的生活体验。
|
1月前
|
搜索推荐 Android开发 iOS开发
探索未来:安卓与iOS双系统的融合与创新
【2月更文挑战第12天】 在数字化时代,智能手机操作系统的发展不仅代表了技术的进步,更是用户体验革新的前沿。本文深入探讨了安卓和iOS这两大主流操作系统的未来走向,特别是它们在技术融合与创新方面的可能性。通过分析当前的市场需求、技术挑战和潜在的发展机会,我们将展望一个可能出现的未来场景:一个结合了安卓开放性和iOS优雅体验的双系统融合平台。这不仅仅是对技术极限的挑战,更是对用户体验极致追求的一次探索。
37 2
|
1月前
|
人工智能 搜索推荐 Android开发
探索未来:安卓与iOS在人工智能时代的融合趋势
【2月更文挑战第12天】 在这篇探索性文章中,我们将深入分析安卓和iOS两大移动操作系统在人工智能(AI)时代的融合趋势。随着技术的飞速发展,AI已成为推动智能手机进化的关键力量。本文通过对安卓和iOS各自在AI领域的最新进展进行比较,揭示了两大平台如何在保持各自特色的同时,也在向着更加智能、更加个性化的方向发展。我们不仅聚焦于当前的技术现状,而且还将展望未来,探讨这一趋势对用户体验、应用开发以及整个科技生态的深远影响。
|
1月前
|
人工智能 安全 搜索推荐
探索未来:安卓与iOS在人工智能时代的融合与创新
【2月更文挑战第12天】随着人工智能(AI)技术的飞速发展,安卓和iOS系统作为智能手机操作系统的两大巨头,正面临着前所未有的融合与创新机遇。本文将从AI技术对移动操作系统的影响、两大系统在AI领域的创新应用,以及未来可能的融合方向三个方面进行深入探讨。通过对比分析,我们旨在揭示安卓和iOS在人工智能时代如何共同推进技术革新,为用户提供更加智能、便捷的服务体验。
|
1月前
|
机器学习/深度学习 人工智能 算法
探索未来:Android与iOS在人工智能时代的融合与创新
【2月更文挑战第11天】 在数字化浪潮和人工智能技术的加速发展下,Android和iOS两大移动操作系统正面临前所未有的挑战与机遇。本文将深入探讨这两大平台如何在人工智能领域进行融合与创新,以及这些变革对消费者、开发者和整个科技生态的影响。我们将从操作系统的智能化升级、应用生态的变革、用户体验的革新三个方面入手,展望Android和iOS在人工智能时代的未来走向。
|
6天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库