app中的webview通识篇(上)

前言

如果你还是第一次与app合作开发webview的页面,那么对于如何调试,可能有哪些问题可能是不够了解的。本文尝试性的根据自己的经验给大家一个入门级别的了解,如果是大佬级别的,可以绕路了。

webview协议约定

为了更好的在app中调试开发我们的移动页面(h5),我们需要与app开发人员约定一些基本的原则,来保证我们的页面可以很好的进行调试,包括调试工具、灵活的模拟上线时的app环境、测试交互过程中的问题、方便自定义的修改为自己的h5地址等。

以下的方案仅供参考,每一条都是有实际用途的,如果公司里的webview需要进行准确的调试和后续开发,必要性的需要考虑以下的问题。

主题

方案

备注

统一确定的ua标识

比如ua结尾加入【xxx】

h5公用的app头

app端提供统一的app头,参考支付宝以及微信的ua交互,提供显示页面标题,返回,关闭的简单操作,默认页面可滚动

之后其他的h5默认在这个类浏览器外壳中,针对前端一些固定布局的方案,需要优化完善这个外壳,前端技改时间允许的话,最好给出完整确定的方案可以在webview中无缝对接和展示

h5与app定制头

针对产品以及交互特殊需求,提供的特殊页面,比如单页,强交互逻辑页定制专门的头

需要产品明确说明特殊性,不是浏览器的返回,比如返回需要加确认框,就需要定制

h5与app功能性交互

约定常规交互方法的格式,并给出相互通讯的一些固定的可用的方法,比如获取用户信息,获取app网络状态

这个是双向的功能性交互,h5的一些方法也可以设置app的状态,页面跳转,数据存储等

h5与app纯交互性方法

调取相应app的加载框,加载失败,相册控件,扫码控件

需要与产品,交互统一确定是否使用app原生控件还是h5效果。

h5与app不同场景的分享互通

比如:用户在不同app中:app分享到h5详情页,h5详情页也可以对应到app中打开

需要约定规则

app提供webview的外壳

可以通过app外壳扫码进入webview场景,模拟交互,开发阶段暴露解决一些app中问题

以上的app解决方案集成在这个app外壳中

关于 app内webiew与h5通讯情况

作为常识我们知道,一般情况下webview的页面是包括两种情况的,一种是本来就可能限定于只有app会嵌入的h5页面,这部分在与app进行通讯的时候,我们更多的是通过约定jsBridge的方式。一种是普通的h5页面。

jsBridge说的更直白一点,就是网页在载入时,向页面内注入一个指定的js文件,然后页面内就会有一个前端和app都知道的方法,通过这个方法前端可以唤起app的交互控件,甚至是跳转到其他的app页面,也可以知道app此时的一些设备状态、网络状态、用户信息等。而app也可以通过h5知道此时页面的状态,进而根据需要做可以在必要时唤起想要的操作。

而另一方面,webview也可看做一个普通的浏览器,可以载入任何的页面,所以我们非app的内嵌页的h5也可以在app内通过webview进行打开; 而app外的h5可以通过app自定义的协议码来唤起app。

相关的通讯技术点可见下面的简陋的图说明。

jsBridge参考文档

iOS与H5交互说明(ios)

iOS与H5交互,采用是JavaScriptCore方式。原理是iOS端在WebView加载完一个URL链接的时候,手动向H5页面绑定一个JSContext对象。利用这个JSContext对象,可以实现OC与JS间的双向交互。注意:JSContext对象是在iOS的webViewDidFinishLoad:回调里完成绑定的,在完成绑定前无法使用JS与OC的任何交互。

JS调用OC接口

JS开始调用OC接口前,有几个前提条件:

  • js的window.isReady方法已经触发过了,JS才能调用OC提供的方法。原因是iOS会在JSContext对象绑定成功后,才会向js端调用isReady方法,所以js只有等isReady触发了,才能通过JSContext调用OC方法。
  • iOS在绑定JSContext对象的时候,要约定好一个字段,然后OC会将原生方法注册到网页window对象的这个字段上。比如window.app
  • js端如果要异步接收原生方法的返回结果,需要在全局作用域内定义好回调方法 ​

JS示例代码:

js调用OC原生方法,同步获取用户基本信息

// 约定好获取用户信息接口注册到window的app属性上
// getUserInfo方法是一个同步方法,可以js端可以直接获取到返回值
// 返回的对象可以是json字符串
var info = window.app.getUserInfo()

js调用OC原生方法,拍照上传作业图片,并异步获取上传结果

// 假设约定好作业相关的OC接口都注册到window的homework属性上
window.homework.uploadHomeworkPicture(questionID)

// 在全局作用域内定义好回调方法,用于接收返回值
// 原生方法会在上传完作业图片的时候,间接调用该回调方法
function homeworkPictureDidUploaded(questionID, picUrl) {
    // do something...
}

JS里调用通用原生接口:

/**
 跳转到课程详情
 @param productId 商品ID(string类型)
 */
app.gotoCourseDetail(productId);

/**
 关闭当前页面
 */
app.finish();

/**
 获取用户信息,已json字符串形式返回。主要字段如下:
   memberId:     用户id
   token:        用户登录唯一标识
   memberType:   用户类型
 */
app.getUserInfo();

/**
 toast提示
 @param msg 提示语(string类型)
 */
app.toast(msg);

/**
 显示对话框
 @param title   标题(string字符串)
 @param msg     消息(string字符串)
 @param actions 点击事件(一个json数组字符串),每个数组元素字段如下:
    title:      事件标题(string字符串,比如“取消”)
    callback:   事件的js回调方法(string字符串)    
 示例:
    var actions = "[{'title': '取消', 'callback': 'cancelPay'},
                    {'title': '确定', 'callback': 'confirmPay'}]";
    app.confirm("温馨提示", "是否支付订单?", actions);
*/
app.confirm(title, msg, actions);

OC调用JS接口

OC在调用JS方法时的注意事项:

  • js方法应该申明到全局作用域内,否则OC获取不到该方法
  • 如果在webViewDidFinishLoad:直接用过JSContext调用js方法,可能会出现调用无效的请求。为了避免此类问题,推荐以setTimeout方式调用js方法

示例代码:

OC在webViewDidFinishLoad:中调用js的isReady方法

// setTimeout是JS的自带方法
// 这里使用setTimeout的目的是为了将isReady方法放到js调用队列的最后
JSValue *isReadyFunc = self.jsContext[@"isReady"];
if (isReadyFunc) {
    [self.jsContext[@"setTimeout"] callWithArguments:@[isReadyFunc, @100]];
}

OC在JS发起的原生方法中调用js的setUserInfo方法

// 注意,JavaScriptCore支持NSDictionary、NSArray类型作参数传给js方法
NSDictionary *userInfo = ...;
[self.jsContext[@"setUserInfo"] callWithArguments:@[userInfo]];

JS提供给原生调用的通用接口定义:

/**
 iOS原生初始化完成后调用本方法,告诉js已经准备好了
*/ 
function isReady();

/**
* return boolean 类型返回值:
    true h5已经处理了返回,native不处理; 
    false h5没有处理返回,native直接返回上级原生页面
*/
function gobackIfNeeded();

APP唤醒

定义scheme: com.xxx.app

UserAgent

WebView的默认UserAgent为:”xxxx XXX/1.3.0″, 其中xxxx为系统默认UserAgent。”/”后为app版本号

内嵌H5页面的加载(安卓)

1.原生提供一个框架页面给H5页面。框架只提供一个叫InnerWeb的类(这点js不需要知晓).如何需要在本地加载一个纯H5的内嵌页面,请使用IntentHelper.startWeb(Context context, String url)方法去载入一个内嵌H5页面。具体内部只是加载这个url。之后的逻辑都交给H5处理。

Android本地通过Java调用HTML页面中的JavaScript方法

原生调用js方法分一下两种类型的方法:

  1. 无返回值方法
  2. 有返回值方法

调用js中无返回值方法

很简单,我们直接调用即可具体代码示例如下:

/**
* f1 为js中声明的函数
*/
mWebView.loadUrl("javascript:f1()");

这样就可以调用js的方法了。

调用js中有返回值的方法

稍微复杂一点,如下:

/**
* sum 为js中定义的函数
*/
mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            Log.e(TAG, "onReceiveValue value=" + value);
        }
    });

js调用Android本地Java方法

本地提供给js调用的映射对象,这需要注入,我们同一使用一个叫app的对象。js如要调用本地方法。都以此开头来代表我们原生方法。

具体如下:

<script type="text/javascript">
     function s(){
     // 注意下面的‘app’
    var result = window.app.gotoLogin();
    document.getElementById("p").innerHTML = result;
    }
</script>

本地代码如下:

public calss AppJavascriptInterface {

    @JavascriptInterface
    public void gotoLogin() {
        if (mContext.get() == null) {
            Log.w("web", "页面已关闭");
            return;
        }
        LoginActivity.start(mContext.get());
    }
}

产品协议

协议的主要以原生提供给H5的为主, 下面是我详细罗列的:

/**
* 跳转登录
*/
@JavascriptInterface
public void gotoLogin();
/**
* 跳转课程详情
*/
@JavascriptInterface
public void gotoCourseDetail();
/**
* 关闭当前页面
*/
@JavascriptInterface
public void finish();
/**
* 关闭当前页面获取当前用户信息,如果为空,说明用户未登录
* 
* 目前有如下信息(以json格式返回给H5):
*   memberId:   用户id 
*   token:      用户登录唯一标识
*   memberType: 用户类型
*/
@JavascriptInterface
public String getUserInfo();

/**
* 返回
* 
* 目前有如下信息(以json格式返回给H5):
*   memberId:   用户id 
*   token:      用户登录唯一标识
*   memberType: 用户类型
*/
public void back();

/**
* 
*/
public void toast(String msg);

public void confirm(String title, String msg, String positiveFunctionName, String negativeFunctionName);
/**
* return boolean 类型返回值: true h5已经处理了返回,native不处理; false h5没有处理返回,native返回上一个非H5页面
*/
function gobackIfNeeded();

app的唤醒方式方案:

1.定义scheme: com.xxx.app 2.另外具体页面的打开待定

约定ua: “xxxx XXX/1.3.0”, 其中xxxx为系统默认ua。iOS与Android不一样。”/”后为app版本号

h5唤起app

h5唤起app已经变成了目前不可或缺的功能之一,作为前端技术栈的必备技术栈之一,我们需要知道如何在非app环境内唤起app,以及正确识别是系统中是否安装了app.

系统相关

应用名称

URL Scheme

短信

sms://

app store

itms-apps://

电话

tel://

无线局域网

App-Prefs:root=WIFI

蓝牙

App-Prefs:root=Bluetooth

蜂窝移动网络

App-Prefs:root=MOBILE_DATA_SETTINGS_ID

个人热点

App-Prefs:root=INTERNET_TETHERING

运营商

App-Prefs:root=Carrier

通知

App-Prefs:root=NOTIFICATIONS_ID

通用

App-Prefs:root=General

通用-关于本机

App-Prefs:root=General&path=About

通用-键盘

App-Prefs:root=General&path=Keyboard

通用-辅助功能

App-Prefs:root=General&path=ACCESSIBILITY

通用-语言与地区

App-Prefs:root=General&path=INTERNATIONAL

通用-还原

App-Prefs:root=Reset

墙纸

App-Prefs:root=Wallpaper

Siri

App-Prefs:root=SIRI

隐私

App-Prefs:root=Privacy

Safari

App-Prefs:root=SAFARI

音乐

App-Prefs:root=MUSIC

音乐-均衡器

App-Prefs:root=MUSIC&path=com.apple.Music:EQ

照片与相机

App-Prefs:root=Photos

FaceTime

App-Prefs:root=FACETIME

应用

应用名称

URL Scheme

微博

weibo://

QQ

mqq://

QQ群组

mqqapi://card/show_pslcard?src_type=internal&version=1&card_type=group&uin={QQ群号}

QQ联系人

mqqapi://card/show_pslcard?src_type=internal&version=1&uin={QQ号码}

支付宝

alipay://

微信

weixin://

微信

wechat://

微信-扫一扫

weixin://dl/scan

微信-反馈

weixin://dl/feedback

微信-朋友圈

weixin://dl/moments

微信-设置

weixin://dl/settings

微信-消息通知设置

weixin://dl/notifications

微信-聊天设置

weixin://dl/chat

微信-通用设置

weixin://dl/general

微信-公众号

weixin://dl/officialaccounts

微信-游戏

weixin://dl/games

微信-帮助

weixin://dl/help

微信-个人信息

weixin://dl/profile

微信-功能插件

weixin://dl/features

虾米音乐

xiami://

chrome

googlechrome://

微博国际版

weibointernational://

摩拜单车

mobike://

ofo

ofoapp://

有道云笔记

youdaonote://

印象笔记

evernote://

今日头条

snssdk141://

网易新闻

newsapp://

网易云音乐

orpheuswidget://

QQ音乐

qqmusic://

QQ音乐最近播放

qqmusic://today?mid=31&k1=2&k4=0

美团外卖

meituanwaimai://

美团

imeituan://

Gmail

googlegmail://

网易邮箱

neteasemail://

QQ邮箱

qqmail://

腾讯视频

tenvideo://

爱奇艺

iqiyi://

12306

cn.12306://

有道词典

yddict://

钉钉

dingtalk://

参考文章

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏HaHack

comment.js:一个纯JS实现的静态站点评论系统

2424
来自专栏SDNLAB

SDNLAB技术分享(五):浅谈Open vSwitch移植

好了我们开始今天的主题吧!! 前一段时间自己私下一直学习Open vSwitch。起初学习Open vSwitch的目的,只是为了更好的学习OpenFlow协议...

38015
来自专栏有趣的Python

Scrapy分布式爬虫打造搜索引擎-(六)scrapy进阶开发Python分布式爬虫打造搜索引擎

Python分布式爬虫打造搜索引擎 基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站 六、scrapy进阶开发 ...

3664
来自专栏非著名程序员

基于开源项目搭建属于自己的技术堆栈

? 在技术面试的时候肯定都会问到使用了哪些第三方框架,为什么使用它而不用其他的。身边朋友就有这样的亲身经历: 面试官:你们项目中加载图片都是用的什么框架? 面...

2627
来自专栏欧阳大哥的轮子

论MVVM伪框架结构和MVC中M的实现机制

一直都有人撰文吹捧MVVM应用开发框架,文章把MVVM说的天花乱坠并且批评包括iOS和android所用的MVC经典框架。这篇文章就是想给那些捧臭脚的人们泼泼冷...

963
来自专栏北京马哥教育

Linux 新手必会的21条命令合集

2027
来自专栏腾讯Bugly的专栏

【Dev Club分享】iOS黑客技术大揭秘

Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师。每周都会举行嘉宾分享,话题讨论等活动。 本期,我们邀请了腾...

3786
来自专栏云瓣

用 Node.js 把玩一番 Alfred Workflow

作为 Mac 上常年位居神器榜第一位的软件来说,Alfred 给我们带来的便利是不言而喻的,其中 workflow(工作流) 功不可没,在它上面可以轻松地查找任...

4383
来自专栏更流畅、简洁的软件开发方式

分页解决方案 之 QuickPager的使用方法(目录)

      QuickPager asp.net 2.0 分页控件,基本告一段落。现在把使用方法、源码、Demo公布一下,感兴趣的可以下载看看。 一、从提取数...

2029
来自专栏Python中文社区

Python 3.6实现单博主微博文本、图片及热评爬取

文章简介 经常刷微博的同学肯定会关注一些有比较意思的博主,看看他们发的文字、图片、视频和底下评论,但时间一长,可能因为各种各样的原因,等你想去翻看某个博主的某...

6086

扫码关注云+社区

领取腾讯云代金券