平台化容器API释放

接上文:(阅读本文前,建议阅读前三篇文章先)

浅谈Hybrid技术的设计与实现

浅谈Hybrid技术的设计与实现第二弹

浅谈Hybrid技术的设计与实现第三弹——落地篇

之前设计Hybrid整块交互的时候,受众都是自己的团队,没有想往“公司化”和“平台化”方向发展,而近期业务的发展逐渐超出预期了,慢慢会有第三方网站接入我们的APP,而且第三方网站还会用一些Native的能力,这个时候之前的使用似乎就不太合适了,所谓JS-SDK就需要存在了。

类似这种需求,做的最完善的当属微信的native容器了,微信这种属于海量容器,对所有的接入方基本一视同仁,就算内部团队会有一些“特权”能力,使用方式与第三方接入都是一套体系,可能只是文档有所不同罢了。微信容器中,最常用的端能力要属:

① 统一登录,获取微信的登录态&使用微信登录,微信方给予第三方应用有限的用户信息(非JS-SDK)

② 分享接口

③ 微信支付

我们今天以微信(中间可能参考其他APP平台)为范本,思考下我们自己的容器如何处理第三方的情况。

我们做这块设计之前,首先需要明确一个定位:

我们的Native容器,应该给第三方网站提供哪些能力?

如果不加限制的提供能力,就属于“内部”项目了,如微信容器一般,对外提供的能力屈指可数,这里是我们一个比较常见的第三方容器:

这个当history过深(不为1时)后也会产生一个关闭按钮,直接回到上一个native页面。

我们这里约定,对于第三方网站,这些Native UI我们都不可定制化(但是能通过释放接口配置增添菜单项目),甚至风格色彩都是不可定制的,如果哪天我们的APP风格变了,那么第三方网站只能适配我们的APP(虽然APP便整体风格这种事情有点操蛋,但是在中小公司还是比较常见)

这里可定制的是:

① 标题展示

② 分享出去的文字、图片等(这块参考微信)

③ 不同的APP这块可能会有定制化需求,这块也最好标准化,不让产品有过多的瞎想

除了登录之外,另一个比较常用的服务就是唤起支付(APP中的支付),然后APP公共页面再引导用户选择微信或者支付宝支付。

其实唤起支付属于H5跳Native(或者Hybrid)的一类,与之类似的有:

① 打开某一个native页面,比较典型的是在网页贴吧中打开一个网址如果安装了贴吧APP,会直接打开那个页面

② 这里也有可能是打开第三方APP,而打开第三方APP这种功能,对于比较大的APP平台会做严格的限制

因为图片上传什么对H5来说是一个比较麻烦的功能,完全依赖H5可能体验不好,于是native方可能会释放一些图片操作的接口如:

① H5调用native的选取相册&图片接口

② 图片预览接口(查看大图)

③ 上传图片

④ 下载图片

有些比较特别的网站也许还会有获取当前网络状态的接口或者地理位置接口或者调用扫一扫功能(场景较少),而对于UI层面的操作又会包含关闭当前窗口(同点击关闭按钮)的接口。

所有的这些功能,我们最初就应该设想清楚,并且清晰的知道每一个接口适用于什么场景,在能力列表出来后,我们就要做另一个事情了:权限限制。

权限限制

微信使用了一个appid,去限制每一个接入方的能力,甚至可以以收费的形式释放某些接口,可以预见,如果微信开放打开第三方App的收费功能,会有很多公司争相埋单。这种appid的行为,相当于一种统一收口的动作,虽然接入方千奇百怪,但是所有的接入方如果要使用容器的能力,甚至想在容器中展示,就必须有一个appid。

另一方面,appid的使用其实成本比较高,前端需要额外的接口访问不说,平台方还需要提供一个第三方网站让第三方网站管理自己的应用,这个对于一些小平台甚至伪平台有一些得不偿失,我们来简单看看一个demo:

 //第三方网站要用什么接口,必须先声明
wx.config({
debug: false,
appId: 'wxf8b4f85f3a794e77',//服务器端读出
timestamp: 1485169627,//服务器端读出
nonceStr: 'jhGQ4jiN4CQpaGPC',//服务器端读出
signature: 'a19573b7f65427a33a96f2a57a4f40075135a5b4',//服务器端读出
jsApiList: [
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'onMenuShareQZone'
]
});
 // 2. 分享接口
// 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
document.querySelector('#onMenuShareAppMessage').onclick = function () {
wx.onMenuShareAppMessage({
title: '分享标题',
desc: '分享描述',
link: 'http://movie.douban.com/subject/25785114/',
imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
trigger: function (res) {
// 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
alert('用户点击发送给朋友');
},
success: function (res) {
alert('已分享');
},
cancel: function (res) {
alert('已取消');
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
alert('已注册获取“发送给朋友”状态事件');
};

如果没有第一段代码的声明,第二段代码就没用;如果appid没有相关接口权限,这里就注册不了,也不能调用接口,比如我们就可以为某一个appid赋予打开第三方app的权限,或者我们定义某个appid具有app,其他应用可以根据打开对应app,没有的话就引导下载:

wx.openApp(appid);

这里的实现方案可以是这样,一般来说,我们对外释放的接口都是比较通用的,像一些私密的接口才会有白名单维护,比如我们自己的app对应的几个图片操作接口:

 //选取图片接口
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
}
});
//上传图片接口
wx.uploadImage({
localId: '', // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
var serverId = res.serverId; // 返回图片的服务器端ID
}
});
//预览图片接口
wx.previewImage({
current: '', // 当前显示图片的http链接
urls: [] // 需要预览的图片http链接列表
});

其中预览我把他作为私密接口不予释放,需要特定的appid才能使用,就可以这样:

 //1代表公共接口,0代表私密接口
var apilist = {
'chooseImage': 1,
'uploadImage': 1,
'previewImage': 0
};
//所有的应用id
var appids = [1, 2, 3, 4];
//白名单
var whitList = [{1: ['previewImage', '其他私密能力']}];

实话实说维护一个appid,这样做的成本比较高,单单做appid和秘钥对于调用者来说也挺麻烦,对于有些比较小的平台来说,可以采取域名白名单的方法,后端维护一个列表:

 //域名白名单
var whitList = [
{'domain.com': ['previewImage', '其他私密能力']},
{'domain2.com': ['previewImage', 'uploadImage', '其他私密能力']}
];

这种方法比较做起来成本较低,一些小一点的平台可以这样做,而这样做的话,需要考虑每个域名对应的App打开协议,可能会有打开需求,我们这里采用的比较简单的方案,域名白名单,表设计大概这样:

 var whitList = [
{id: 'domain.com', apis: ['previewImage', '其他私密能力'], schema: 'xxxx://'},
{id: 'domain2.com', apis: ['previewImage', 'uploadImage', '其他私密能力'], schema: 'xxxx://'}
];

我们明确知道某个域名具有哪些能力,如果不具有这些能力就不予理睬,我们也知道某个域名具有打开某个app的权限。

能力列表

明确了能力,以及能力限制,接下来我们便来整理一下几个核心的对外接口。

header的定义

我们这里做的第一件事情,依旧是header的定义,并且对于第三方,我们要求header只能是这个样子:

针对header我们有以下约定:

① 进入一个页面默认包含,返回+title+功能菜单三个按钮

② 返回按钮默认执行history.back()的操作,如果history.length为1,则退到native上一步操作

③ title默认读取html中的title标签,可使用接口更改

④ 关闭按钮默认不存在,在history比较深并且点击过一次返回按钮后展示出来,防止页面死循环假死

⑤ 功能菜单默认弹出以下菜单项,其中分享文案默认读取当前tdk(title+description)标签,和第一张图片,也可读取页面标签定制(也可以使用接口定制,事实上容器不会做这种业务工作,是业务框架层bridge做的工作),比如:

 <meta name="med-title" content="分享标题">
<meta name="med-description" content="分享内容">
<meta name="med-link" content="http://....">
<meta name="med-img" content="http://....">

我们业务层代码,或者bridge代码会将之翻译为:

 wx.onMenuShareTimeline({
title: medTitle,
link: medLink,
imgUrl: medImg,
trigger: function (res) {
},
success: function (res) {
},
cancel: function (res) {
},
fail: function (res) {
}
});

分享到朋友圈&QQ空间

分享到朋友圈前端代码为:

 MED.origin = MED.origin || {};
//shareTimeline分享到朋友圈;shareAppMessage分享给朋友;shareQQ分享给qq好友;shareQZone分享到空间,设置方面稍作更改即可
MED.origin.medShareXXX = MED.medShareXXX = function (o) {
_.requestHybrid({
tagname: 'shareTimeline',
param: {
title: o.title,
desc: o.desc,
image: o.img,
url: o.link
},
callback: function(data) {
if(data.code === 0) {
o.success && o.success(data.data);
} else {
o.cancel && o.cancel(data.data);
}
}
});
};

H5上传图片方面的体验很差,这块我们在H5情况下依旧使用file上传,但是在容器里面释放几个图片操作接口

图片操作

 //选取图片,最初想把选取和上传合并的,后面想想还是分开合适
_.requestHybrid({
tagname: 'chooseImage',
param: {
//1-9张限制
count: 1,
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
},
callback: function(data) {
if(data.code === 0) {
//这块有一些疑问,选择和上传还是连着一起算了
o.success && o.success(data.data.localIds); // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片;
} else {
o.error && o.error(data.data);
}
}
}); //上传图片
_.requestHybrid({
tagname: 'uploadImage',
param: {
//由chooseImage获取
localId: 1,
isShowProgressTips: 1 // 默认为1,显示进度提示
},
callback: function(data) {
if(data.code === 0) {
o.success && o.success(data.data.url); // 返回src;
} else {
o.error && o.error(data.data);
}
}
}); //图片预览,预览的地方做个图片下载的功能
_.requestHybrid({
tagname: 'previewImage',
param: {
current: '', // 当前显示图片的http链接
urls: [] // 需要预览的图片http链接列表
}
});

获取网络状态

 //获取网络状态
_.requestHybrid({
tagname: 'getNetworkType',
callback: function(data) {
//data.networkType 2g 3g 4g wifi
}
});

地图操作

Native的地理操作一块相对H5体验要好一些,特别是地图展示一块的体验要好得多,所以这两块也需要释放API:

 //获取经纬度信息
_.requestHybrid({
tagname: 'getLocation',
callback: function(data) {
if(data.code !== 0) return;
var res = data.res;
var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed; // 速度,以米/每秒计
var accuracy = res.accuracy; // 位置精度
}
}); //根据经纬度等信息打开native地图
_.requestHybrid({
tagname: 'openLocation',
params: {
latitude: 0, // 纬度,浮点数,范围为90 ~ -90
longitude: 0, // 经度,浮点数,范围为180 ~ -180。
name: '', // 位置名
address: '', // 地址详情说明
scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大
infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
}
});

所谓的name和地址是指下面信息框这一坨:

界面操作

关闭当前webview,回到native上一次操作:

 _.requestHybrid({
tagname: 'closeWindow'
});

native键盘

H5在文字输入一块可以说是弱爆了,比Native体验差远了,所以我们在native键盘这块也做了一个nativeUI,如果需求允许可以使用:

 //唤起输入文字的软键盘
//这块代码有一些业务耦合,需要如何处理下????
_.requestHybrid({
tagname: 'showKeyboard',
param: {
hasImg: 1, //是否需要上传图片区域,如果需要则为1,不需要为0
count: 1, //如果需要图片上传,这里限制图片选择的数量,1-9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
textMin: 20, //文字要求最少输入字符数
textMax: 500 //文字要求最多输入字符数
},
//输入结束的回调或者说点击发送时候的回调
callback: function (data) {
var content = data.content;//文字内容
var urls = data.urls;//图片地址
}
});

结语

今天分析了一下第三方webview需要释放的接口,接下来我这边开始落地,我们真实工作中可能还要考虑新老容器过渡等问题,本文含金量相对较小,各位谨慎阅读吧。

Hybrid容器设计之第三方网站的更多相关文章

  1. 第三方网站返回hybrid app H5页面缓存问题应对策略

    最近负责公司各产品线购买模块的开发,各项功能如期开发完成后测试那边反馈回来一个问题:IOS手机在点击支付宝购买后,跳转到支付宝网站时不输入支付密码,直接点返回,返回到我们自己的APP购买界面发现页面显 ...

  2. JAVA中容器设计的进化史:从白盒到黑盒,再到跻身为设计模式之一的迭代器

    大家好,又见面了. 在我们的项目编码中,不可避免的会用到一些容器类,我们可以直接使用List.Map.Set.Array等类型.当然,为了体现业务层面的含义,我们也会根据实际需要自行封装一些专门的Be ...

  3. Httpster –世界各地最潮的网页设计案例聚合网站

    Httpster 这个网站聚合了世界各地最新最潮的网页设计案例,展示了创意的设计,精心的策划,优秀的排版.这些作品都按月份和类别进行了很好划分,你可以方便的找到自己感兴趣的网站案例. 立即去看看 您可 ...

  4. 第三方网站不能调用微信公众平台里的图片了 显示"此图片来自微信公众号平台未经允许不可引用"

    下午ytkah在自己小博客搜索时看到有几篇文章图片显示不了,再访问一些网站时发现有些图片无法显示出来,显示"此图片来自微信公众号平台未经允许不可引用",如下图所示,这个应该是最近微 ...

  5. 解决MVC Jquery"此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站"的问题

    在ASP.NET MVC项目中,使用AJAX向控制器发送GET请求获取JSON数据时,出现这个错误:"此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站.若要允许 G ...

  6. Spring源码解析一:IOC容器设计

    一.IOC接口设计 IOC容器设计的源码主要在spring-beans.jar.spring-context.jar这两个包中.IOC容器主要接口设计如下: 这里的接口设计有两条主线:BeanFact ...

  7. Docker 案例: 在容器中部署静态网站

    ----------------知识点------------ 容器的端口映射: docker  run  [-P] [-p] -P,–publish-all=true | false,大写的P表示为 ...

  8. 解决"此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站"的问题

    在ASP.NET MVC项目中,使用AJAX向控制器发送GET请求获取JSON数据时,出现这个错误:"此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站.若要允许 G ...

  9. HTML & CSS设计与构建网站 ([美]达科特) PDF原版​

    HTML & CSS 设计与构建网站采用有别于许多传统编程书籍的新颖编排方式,将使您收到事半功倍的学习效果.每一页都在短小精悍的示例代码的引导下,简明直观.直截了当地阐述一个新主题. < ...

随机推荐

  1. linux 细节 问题解决

    Ubuntu 12.04 启动时停在Checking battery state…… 1.在停止的界面按下Ctrl+Alt+F1 2.reboot halt

  2. 想要学习Linux技术,先好好的读一本Linux书籍吧

    忘记你在使用windows时的使用习惯和使用思维.学习Linux,一定要适应Linux的命令行界面,因为命令行才是Linux的真正魅力所在,而X-window或着说桌面环境也只是运行在命令行模式下的一 ...

  3. 解决airserver在Windows下安装失败的问题

    airserver 可以将iphone 实时投影到mac 和 pc.在mac上安装非常简单.但是在Windows上安装时会有很多问题.之前我电脑安装很快就完成了(因为我之前已经在不知情的前提先事先装过 ...

  4. Camera服务之--架构浅析

    Camera服务之--架构浅析 分类: Camera 分析2011-12-22 11:17 7685人阅读 评论(3) 收藏 举报 android硬件驱动框架jnilinux内核平台 一.应用层 Ca ...

  5. (简单) POJ 1797 Heavy Transportation,Dijkstra。

    Description Background Hugo Heavy is happy. After the breakdown of the Cargolifter project he can no ...

  6. CodeForces 652D Nested Segments

    离散化+树状数组 先对坐标离散化,把每条线段结尾所在点标1, 询问某条线段内有几条线段的时候,只需询问这段区间的和是多少,询问结束之后再把这条线段尾部所在点标为0 #include<cstdio ...

  7. iOS设置状态栏的字体颜色

    设置statusBar的[前景色部分] 1.plist设置statusBar 在plist里增加一行 UIStatusBarStyle(或者是“Status bar style”也可以),这里可以设置 ...

  8. Redis详解

    转自:http://blog.csdn.net/eroswang/article/details/7080412 1.  MySql+Memcached架构的问题 1.MySQL需要不断进行拆库拆表, ...

  9. LPC1768的usb使用--硬件篇

    LPC1768芯片带有USB设备控制器,前面写的文章都是在说比较简单的设备驱动,今天来说复杂一点的 首先是硬件层的配置 #ifndef __USBHW_H__ #define __USBHW_H__ ...

  10. iOS开发——判断邮箱格式

    //判断邮箱格式 -(BOOL)isValidateEmail:(NSString *)email { NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@ ...