16200
1. 使用场景
2. webview打开窗口优势
3. 接口列表
4. APP接入注意事项
5. 示例代码
当您在app中使用webview的方式打开一洽的对话窗口时,可使用此交互API做特定的业务逻辑处理
app接入客服咨询有两种途径,一种是使用一洽提供的SDK将其整合到自己的APP中用native的方式打开对话,一种是通过webview直接将一洽的对话窗口地址打开接入对话。
使用SDK接入对话需要考虑多种机型的适配和测试,工作量繁重,更新复杂不能及时使用一洽新的创新
使用webview的形式打开接入相对于SDK来说简单快速,适配问题不用考虑,同时又能在APP不更新的情况下使用最新的一洽功能,在遇到问题后不影响已上线的APP。
3.1 APP注入对象
使用交互API需要app提供一洽调用的对象注入给webview。
提供对象:EchatJsBridge
提供方法:callEchatNative
方法定义:EchatJsBridge.callEchatNative(functionInfo)
参数定义:functionInfo为一个json字符串,json字符串包含的key列表如下:
属性名 | 类型 | 说明 |
---|---|---|
functionName | String | 调用的方法名称(事件名称) |
value | String | 不同的方法(事件)值的类型不一致,具体见详细的事件定义列表 |
示例:
EchatJsBridge.callEchatNative(JSON.stringify({"functionName": "newMsg",value:"你好,请问有什么可以帮您?"}));
此对象是由NavtiveApp通过WebView的方法创建交给JavaScript环境。当访客端的事件触发时会主动调用下面列表的中的事件。
功能 | 事件名称 | 事件值类型 | 事件值说明 |
---|---|---|---|
访客对话状态变更 | chatStatus | String | 对话状态事件 waiting:等待接入对话 chatting:对话中 robot:机器人对话中 leaveDisabled:客服离线、留言禁用 leaveToService:客服离线进入留言 leaveToUrl:客服离线跳转至url end-[0-8]-0/1:对话结束 0-8含义为结束原因: 0:对话结束原因未知 1:访客结束对话 2:客服结束对话 5:客服退出结束对话 6:系统关闭对话 8:访客长时间为响应结束对话 0/1含义为是否需要评价 0:不需要评价 1:需要评价 |
接入对话的客服信息 | chatStaffInfo | String | 对话的客服信息,json字符串 属性描述如下: staffId:客服ID staffNickName:客服的对外昵称 staffPhone:客服的对外电话 staffHead:客服的对外头像 staffInfo:客服的对外信息介绍 |
访客排队位置 | queuePostion | int | 当前访客的排队位置,如果没有排队则不会触发此方法调用 |
收到新客服消息 | newMsg | String | 访客收到的新客服消息text/[file]/[image] text:收到的文本消息,消息中会有标签代表表情可直接拼接路径后获取表情图标地址如:表情的基础路径为http[s]://es.echatsoft.com [file]:收到了新的文件 [image]:收到了新的图片 |
收到新系统消息 | newSysMsg | String | 访客收到的新的系统消息text/[file]/[image] text:收到的文本消息,消息中会有标签代表表情可直接拼接路径后获取表情图标地址如:表情的基础路径为http[s]://es.echatsoft.com [file]:收到了新的文件 [image]:收到了新的图片 |
访客评价反馈 | visitorEvaluate | String | 客服的评价结果 0/1-1/2 0/1含义为是否提交了评价 0:访客取消评价 1:访客提交了评价 1/2含义为提交评价时的对话状态 1:访客对话中 2:对话已结束 |
通知APP隐藏对话窗口 | visitorHide | 无 | 如果打开的web对话样式有标题栏,且支持访客最小化对话窗口,那么在访客点击最小化是会调用此方法 |
接收当前对话的公司ID | sendCompnayId | int | 当前对话的账号ID。调用callEchatJs的getCompanyId方法后会触发此方法调用。 |
接收当前访客的访客ID | sendVisitorId | jsonString | 在H5获取到当前访客的ID后发送给APP。详见下方访客ID json字段解释。 |
预览图片 | previewImage | jsonString | 将当前对话中的图片列表发送给app进行预览。详见下方预览图片json字段解释。调用callEchatJs的setMediaPlayer方法接管图片预览后会触发此方法调用。 |
视频播放 | video | jsonString | 将选择的视频信息送给app进行播放。详见下方视频播放json字段解释。调用callEchatJs的setMediaPlayer方法接管视频播放后会触发此方法调用。 |
打开链接(已废弃) | openLink | String | 将要打开的链接通知app打开。调用callEchatJs的setLinkOpener方法接管链接打开后会触发此方法调用。 |
打开链接 | openLinkV2 | jsonString | 将要打开的链接通知app打开。调用callEchatJs的setLinkOpener方法接管链接打开后会触发此方法调用 详见下方链接打开json字段解释。 |
接管访客发送消息 | visitorPreSendMsg | String | 访客准备发送的消息内容。如果app设置了接管访客发送消息,则访客要发送的文本消息在发送时会提交给APP做处理,APP处理完成后调用callEchatJs的visitorSendMsg来通知H5将消息发送出去。 设置接管访客消息的方法: 加载对话窗口地址时追加fvMsg参数来他通知H5是否接管消息 1:接管发送消息 2:接管接收消息 3:接收发送和接收消息 |
接管访客接收消息 | visitorPreReceiveMsg | String | 访客接收到的文本消息内容(可能包含html标签)。如果APP设置了接管访客接收消息,则访客在收到了文本消息后将消息内容发送给APP处理,APP处理完成后调用callEchatJs的visitorReceiveMsg来通知H5将消息内容显示在页面上。 |
页面状态 | echatPageStatus | jsonString | 一洽页面对话事件通知,APP根据此信息来进行标题栏的标题更换以及是否离开一洽页面等操作,在app触发echat页面的返回操作(echatBackEvent)或者H5的返回操作触发时会触发此事件通知。数据定下详见下方页面状态json字段解释 |
平台客户新消息 | platformNewMsg | jsonString | 当前未在对话页面的客服消息通知,仅在平台版账户中有效,会将所有未在对话页面的客服消息通过此事件通知给app,以便做APP内的消息通知。比如访客正在和A商户聊天B商户的客服发来消息会收到此事件,详见平台客户新消息json字段解释 |
上传文件凭证 | uploadFileInfo | jsonString | 通知APP上传文件的凭证,如果获取上传文件凭证报错会返回错误信息,具体的返回信息请参照一洽文件上传接口 |
访客接收消息json示例:
{
"receiveMsg":"客服发送给访客的消息",
"msgId":"23421"
}
字段 | 类型 | 说明 |
---|---|---|
receiveMsg | string | 客服发送给访客的消息息。 |
msgId | string | 消息ID,此值在APP处理完客服发送的消息后必须原值返回给H5。 |
访客ID json字段解
示例:
{visitorId:"web2025192",encryptVId:"kU6+npAXUd0="}
字段 | 解释 |
---|---|
visitorId | 当前访客的唯一身份ID,例如:web123 sdk456 |
encryptVId | 加密的访客ID,用来和明文ID一起验证访客身份,在部分http接口中需要使用 |
预览图片json字段解释:
示例:
{urls:[{“bigImg":"http://tqiniu.echatsoft.com/913eb471-fbcf-46c2-8456-378bd1d55899","smallImg":"http://tqiniu.echatsoft.com/913eb471-fbcf-46c2-8456-378bd1d55899?imageView2/1/w/200/h/150","sourceImg":"http://tqiniu.echatsoft.com/913eb471-fbcf-46c2-8456-378bd1d55899","fileName":"3.png”}\],current:1}
字段 | 属性 | 解释 |
---|---|---|
urls | 可预览的图片信息列表 | |
bigImg | 图片的大图网络地址 | |
smallImg | 图片的缩略图网络地址 | |
sourceImg | 图片的原图网络地址 | |
fileName | 图片的文件名 | |
current | 当前需要播放的图片索引位置。索引位置从1开始计数。 |
视频播放json字段解释:
示例:
{"type":"play","url":"https://***videosrc url","thumbUrl":"https://video"}
字段 | 解释 |
---|---|
type | 请求视频操作的类型,目前固定为:play |
url | 要播放视频资源的网络地址 |
thumbUrl | 要播放视频资源的封面网络地址 |
链接打开json字段解释
示例:
{url:"https://www.echatsoft.com",openType:"inner",position:1}
字段 | 解释 |
---|---|
url | 要打开链接的地址 |
openType | 一洽H5页面未接管时处理此页面的打开方式。 inner:不跳转页面在对话窗口页面的内部打开url blank:新窗口打开url |
position | 此属性待支持,支持后可通知给APP链接所在位置 |
页面状态json字段解释:
在一洽页面发生变化时会触发此事件调用,在load一洽页面的webview要做返回操作时,可通知一洽页面有返回操作,一洽页面会根据自己的状态向app发送echatPageStatus来通知页面状态。
示例:
{"event":1,"eventValue":"{\"companyId\":500029,\"companyName\":\"一洽客服专卖店\",\"chatUrl\":\"https://ps.echatsoft.com/visitor/mobile/chat.html?companyId=500029&platformSign=3rfef3ffsfsdwerwjljl\"}"}
字段 | 类型 | 说明 |
---|---|---|
event | int | 当前对话的事件 1:请求对话,消息盒子点击某个商户或者平台时触发此事件(平台版用户会有此事件) 2:隐藏对话 消息盒子正在进行的对话隐藏后触发(平台版用户会有此事件) 3:返回主页面 ,在访客请求离开一洽页面时触发. 4:进入消息盒子页面(平台版用户会有此事件) 5:进入浏览页面 6:单独对话窗口打开 7:浏览页面对话窗口打开 8:浏览页面隐藏对话窗口 |
eventValue | JsonString | 对话事件信息。 event=1,6,7时: {"companyId":500029,"companyName":"一洽客服专卖店","chatUrl":"https://ps.echatsoft.com/visitor/mobile/chat.html?companyId=500029&platformSign=3rfef3ffsfsdwerwjljl"} companyId:当前打开对话的账号ID companyName:当前打开对话的账号名称 event= 2时:空 event=3时:空 event=4时:空 event=5时:空 event=8时:空 |
平台客户新消息json字段解释:
示例:
{
"chatUrl":"https://ps.echatsoft.com/visitor/mobile/chat.html?companyId=500029&platformSign=3rfef3ffsfsdwerwjljl",
"companyId":"500029",
"companyLogo":"http://pffile.echatsoft.com/group1/M00/00/00/wKgcPFzae_uAA112AAAZUUyrnC0829.png",
"companyName":"Nike旗舰店",
"msgContent":"[图片]",
"msgId":"123321",
"unreadMsgCount":5
}
字段 | 类型 | 说明 |
---|---|---|
companyId | string | 新消息所属账号ID |
companyName | string | 新消息所属账号名称 |
companyLogo | string | 新消息所属账号的logo |
msgContent | string | 新消息内容(可能为空,如果为空,说明只通知了未读消息数量) |
msgId | string | 消息ID 唯一值 |
chatUrl | string | 对话窗口地址,打开即可进入当前未读消息的对话页面 |
unreadMsgCount | int | 当前消息盒子的未读消息数量(次访客在平台以及平台所有商户的未读消息数量和) |
3.2 JS端
在打开一洽的对话窗口后,native可以直接通过webview的方法调用一洽提供的方法
提供方法:callEchatJs(functionInfo)
参数定义:functionInfo为一个json字符串,json字符串包含的key列表如下:
属性名 | 类型 | 说明 |
---|---|---|
functionName | String | 调用的方法名称 |
value | String | 不同的方法值的类型不一致,具体见详细的事件定义列表 |
示例:
callEchatJs("{\"functionName\":\"closeChat\",\"value\":\"\"}");
此对象在页面加载后即可提供给native调用
提供的方法/事件列表:
功能 | 调用方法 | 参数类型 | 说明 |
---|---|---|---|
设置访客端声音提示 | switchSoundTip | String | 0/1 0:关闭对话网页新消息提示音 1:开启对话页面新消息提示音,关闭后可根据需要在收到js通知APP有新消息时通过native方法播放声音 |
APP关闭对话 | closeChat | 无 | 通知一洽关闭对话,在收到一洽的对话结束状态后可销毁webview |
获取对话账号ID | getCompanyId | 无 | H5收到此消息后会调用Native提供的sendCompanyId方法将公司ID通知给APP |
设置图片视频播放方式 | setMediaPlayer | jsonString | json字符串。通知H5,Native是否接管对话过程中产生的图片和视频的播放,如果Native接管H5会调用native的方法来进行图片和视频的播放。 {"video":1,"image":1} video:0或1 是否接管视频的播放,image:0或1 是否接管图片的播放。0:不接管 1:接管 |
设置链接打开方式 | setLinkOpener | jsonString | {native:1,positions:"1,2,3"}在指定位置需要打开链接时,调用Native方法打开; {native:0}:H5页面内自行处理链接 positions:链接所在位置-此位置属性待支持,支持后可设置接管部分位置的链接,目前接口设置后将接管所有位置的链接 |
设置键盘高度 | setKeyboardHeight | int | APP获取的键盘高度通知给H5,解决app不用webview、以及不同操作系统版本出现的键盘 输入框推起的问题,多用在IOS APP中。 |
通知H5发送访客消息 | visitorSendMsg | jsonString | 将处理完的消息发送给H5通知H5发送消息。 jsonString示例见:访客发送消息json示例 |
通知H5接收客服消息 | visitorReceiveMsg | jsonString | 将处理完的消息发送给H5通知H5显示接收消息。 jsonString示例见:访客接收消息json示例 |
一洽页面返回 | echatBackEvent | 无 | 在访客点击标题栏中的返回键时app通知H5进行返回操作,H5根据对话的状态来通知Native具体的状态(echatPageStatus)来确认是否需要返回主界面或者更改标题栏标题 |
获取上传文件凭证 | getFileUploadInfo | 无 | 获取窗口外上传文件的上传凭证,js会将结果通过uploadFileInfo返回给APP,具体的返回信息请参照一洽文件上传接口 |
代访客发送文本消息 | sendTextMsg | String | 代替访客发送一个文本消息给客服(机器人客服、人工客服),如果当前不在会话中,此方法调用会失效,可通过监听会话状态做个性化提示处理。 |
互动窗口打开链接 | openLink | String | 通知对话窗口在互动窗口打开页面,在接管了URL打开,但是有些URL又需要在对话窗口的iframe中打开时调用此方法打开 |
visitorSendMsg方法访客发送消息json示例(注:只有接管了访客发送消息的APP调用此方法才生效):
{
"sendMsg":"发送给客服的消息",
"localMsg":"访客本地显示的消息"
}
字段 | 类型 | 说明 |
---|---|---|
sendMsg | string | 发送给客服的消息,此消息可为空如果为空的话则不向客服发送消息。 |
localMsg | string | 客服本地显示的消息,此消息不可为空。为空的话此次消息将发送失败。 |
访客接收消息json示例:
{
"receiveMsg":"处理过后的客服发送给访客的消息",
"msgId":"23421"
}
字段 | 类型 | 说明 |
---|---|---|
receiveMsg | string | 客服发送给访客的消息,此消息可为空如果为空的话则访客窗口不显示此消息。 |
msgId | string | 调用app的visitorPreReceiveMsg时发送给APP的msgId。如果返回一个不存在的msgId则此次消息会放弃处理。 |
3.3 调用方法示例
3.3.1 Android示例
//注入对象
/**
* 给Webview提供EchatJsBridge的JavaScript对象
*/
try {
getWebView().addJavascriptInterface(new JavaScriptInterface(), "EchatJsBridge");
} catch (Exception e) {
LogUtils.e(e);
}
/**
*需实现一个class 对象,并在callEchatNative方法上增加@JavascriptInterface注解
*这里创建了一个名为JavaScriptInterface的class,里面有callEchatNative方法
*/
class JavaScriptInterface {
@JavascriptInterface
public void callEchatNative(final String message) {
/**
* 解析前端HTML5 通过EchatBridge传入的数据
*/
JSONObject msg = JsonUtil.fromJson(message);
String functionName = null;
String value = null;
try {
functionName = msg.getString("functionName");
value = msg.getString("value");
} catch (JSONException e) {
LogUtils.e("解析JSON失败", e.getLocalizedMessage());
}
if (functionName == null) return;
//这里根据获得functionName 进行相应的操作
}
}
/**
* 初始化聊天窗口
*/
private void initChatView() {
//初始化Webview设置
WebSettings settings = webview.getSettings();
//开启JavaScript支持
settings.setJavaScriptEnabled(true);
//默认设置为true,即允许在 File 域下执行任意 JavaScript 代码
settings.setAllowFileAccess(true);
//Disable zoom
settings.setSupportZoom(false);
//提高渲染优先级
settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
//建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT
settings.setCacheMode(WebSettings.LOAD_DEFAULT);//网络正常时使用默认缓存策略
// 开启DOM storage API 功能
settings.setDomStorageEnabled(true);
// 开启database storage API功能
settings.setDatabaseEnabled(true);
// 开启Application Cache功能
settings.setAppCacheEnabled(true);
settings.setAppCacheMaxSize(1024 * 1024 * 10);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
settings.setAllowUniversalAccessFromFileURLs(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
//允许iframe与外部域名不一致的时候出现的 请求丢失cookie
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(getWebView(), true);
}
/**
* 给Webview提供EchatJsBridge的JavaScript对象
*/
try {
getWebView().addJavascriptInterface(new JavaScriptInterface(), "EchatJsBridge");
} catch (Exception e) {
LogUtils.e(e);
}
}
//调用js方法关闭对话
public void exceJSFunction(String funName, String content) {
String trigger = "javascript:" + funName + "(" + content + ")";
loadJS(trigger);
}
private void loadJS(String trigger) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mWebView.evaluateJavascript(trigger, null);
} else {
mWebView.loadUrl(trigger);
}
}
//通知H5 关闭对话
exceJSFunction("callEchatJs", "{\"functionName\":\"closeChat\"}");
//支持mail、tel协议、支持上传文件
/**
*创建WebViewClient对象,对shouldOverrideUrlLoading方法进行复写(注意这里区分Android5.0以前和之后),将这个对象设置进webview
*/
webView.setWebViewClient(mWebViewClient);
public WebViewClient mWebViewClient = new WebViewClient() {
//>= Android 5.0
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return WebViewFragment.this.shouldOverrideUrlLoading(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return WebViewFragment.this.shouldOverrideUrlLoading(view, url);
}
}
//这里是统一处理了,在Fragment中创建了一个统一方法。
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return;
}
if (url.startsWith("mailto:")) {
openMail(url);
return;
}
if (url.startsWith("http")) {
view.loadUrl(url);
}
return true;
}
/**
* 跳转打电话界面
*
* @param number
*/
private void callPhone(String number) {
//打开电话窗口需要用户手动点击拨打
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("tel:" + number));
startActivity(intent);
}
/**
* 打开邮件
*
* @param mailUrl
*/
private void openMail(String mailUrl) {
try {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse(mailUrl));
startActivity(intent);
} catch (Exception e) {
}
}
//上传文件(图片/视频)
//创建WebChromeClient对象,对openFileChooser方法进行复写,注意这里不同系统大版本,使用的方法参数不一样
//这里将Android 3.0-4.4、4.4.1、4.4.3、4.4.4进行统一处理,Android 4.4之后的再进行统一处理(注Android 4.4.2 因系统BUG,不会调用openFileChooser)
public WebChromeClient mWebChromeClient = new WebChromeClient() {
// For Android >= 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType) {
WebViewFragment.this.openFileChooser(valueCallback, acceptType, null);
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
WebViewFragment.this.openFileChooser(valueCallback, acceptType, capture);
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return WebViewFragment.this.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
return false;
}
}
private ValueCallback<Uri> uploadMessage;
private ValueCallback<Uri[]> uploadMessageAboveL;
/**
* 3.0-4.4, 4.4.4 input type=file选择文件时调用 用于上传
*
* @param valueCallback
* @param acceptType
* @param capture
*/
@Override
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
//全局保存回调接口
uploadMessage = valueCallback;
//调用图库 或 拍照 或 文件
openCameraOrGalleryOrFile();
}
/**
* 5.0之后 input type=file选择文件时调用 用于上传
*
* @param webView
* @param filePathCallback
* @param fileChooserParams
* @return
*/
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
//全局保存回调接口
uploadMessageAboveL = filePathCallback;
//调用图库 或 拍照 或 文件
openCameraOrGalleryOrFile();
return true;
}
public void openCameraOrGallery() {
//注意Android 6.0新增权限管理,对文件操作要申请读权限(请自行申请)
//这里通过开源图库选择器 知乎图库作为示例
Matisse.from(this)
.choose(MimeType.ofAll(), false)
.countable(true)
.capture(true)
.captureStrategy(
new CaptureStrategy(true, getmActivity().getPackageName() + ".fileprovider"))//用于Android7.0 拍照
.maxSelectable(1)//目前HTML5只支持单文件上传
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
.gridExpectedSize(
getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.thumbnailScale(0.85f)
.imageEngine(new GlideEngine())
.forResult(REQUEST_CODE_CHOOSE);
}
private Uri result;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
/**
* 处理知乎图库
*/
if (requestCode == REQUEST_CODE_CHOOSE) {
if (resultCode == RESULT_OK) {
List<String> list = Matisse.obtainPathResult(data);
if (list.size() > 0) {
//这里可以考虑对图片进行压缩在上传
result = Uri.fromFile(new File(list.get(0)));
LogUtils.w(result.toString());
}
}
/**
*
* openFileChooser
* onShowFileChooser
* 回调对象执行onReceiveValue
*/
endToUpload();
}
}
/**
* 选择文件/照片回调结束
*/
private void endToUpload() {
if (uploadMessage != null) {
uploadMessage.onReceiveValue(result);
uploadMessage = null;
result = null;
}
if (uploadMessageAboveL != null) {
Uri[] results = result != null ? new Uri[]{result} : null;
uploadMessageAboveL.onReceiveValue(results);
uploadMessageAboveL = null;
result = null;
}
}
3.3.2 Ios-UIWebview示例
//注入对象
// - 获取上下文
self.context = [self.webViewvalueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// - 注入对象
self.context[@"EchatJsBridge"] = self.jsBridge;
//注入的对象引入#import <JavaScriptCore/JavaScriptCore.h>并声明如下协议
@protocol JsBridgeJSExport <JSExport>
- (void)callEchatNative:(id)json;
@end
//调用js方法关闭对话
//注入对象遵守此协议<JsBridgeJSExport>并实现- (void)callEchatNative:(id)json;
NSDictionary *dic = @{@"functionName”:@“closeChat”,@“value":@""};
NSString *jsonStr = [self convertToJsonData:dic];//dict->jsonString
JSValue *allback = self.context[@“callEchatJs”];
//oc->js
[allback callWithArguments:@[jsonStr]];
- (NSString *)convertToJsonData:(NSDictionary *)dict{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString;
if (!jsonData) {
NSLog(@"%@",error);
}else{
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
NSRange range = {0,jsonString.length};
//去掉字符串中的空格
[mutStr replaceOccurrencesOfString:@" " withString:@"" options:NSLiteralSearch range:range];
NSRange range2 = {0,mutStr.length};
//去掉字符串中的换行符
[mutStr replaceOccurrencesOfString:@"\n" withString:@"" options:NSLiteralSearch range:range2];
return mutStr;
}
//接收js的事件通知
- (void)callEchatNative:(id)json{
//传入的是jsonString进行JsonString转字典
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData
options:kNilOptions
error:&err];
//dict样式形如{“functionName":"chatStatus","value":"robot"} functionName对应方法名,value对应传递的参数,如下粗略写法
if(方法名isEqual){调用自定义方法并传递参数}
}
//支持mail、tel协议
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
//tel或者mail协议在此拦截
// request.URL.absoluteString 样例tel:188XXXX1234
return YES;
}
3.3.3 Ios-WKWebview示例
//注入对象
WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc]init];
config.userContentController = [[WKUserContentController alloc]init];
//self.jsBridge为接受消息对象callEchatNative是JSPostMessage的对象
[config.userContentController addScriptMessageHandler:self.jsBridge name:@"callEchatNative"];
//调用js方法关闭对话
NSDictionary *dic = @{@"functionName”:@“closeChat”,@“value":@""};
NSString *jsonStr = [self convertToJsonData:dic];//dict->jsonString
//oc->js
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"window.callEchatJs(%@)",closeChatString] completionHandler:^(id _Nullable result, NSError * _Nullable error) {
if (error) {
NSLog(@"CALLJS error = %@---%@\n%@",error,result,functionName);
}
}];
//接收js的事件通知
//->消息接受self.jsBridge 实现 引入#import <WebKit/WebKit.h>并遵守<WKScriptMessageHandler>协议
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//在里面解析JS传递给native的数据
}
//支持mail、tel协议
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//navigationAction.request.URL.absoluteString 与UIWeb处理类似
decisionHandler(WKNavigationActionPolicyAllow);
}
4.1 申请权限
Android:
//文件读写
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
//非系统相机增加权限
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
Ios:
Privacy - Camera Usage Description
Privacy - Microphone Usage Description
Privacy - Photo Library Additions Usage Description
Privacy - Photo Library Usage Description
4.2 webView设置
以下设置必须支持,如不支持可能会导致聊天业务部分功能不可用。
android:
WebSettings settings = webview.getSettings();
//开启JavaScript支持
settings.setJavaScriptEnabled(true);
//允许读取和写入文件
settings.setAllowFileAccess(true);
//禁用zoom,不支持缩放
settings.setSupportZoom(false);
//支持localstorage来保存客户端的聊天记录
settings.setDomStorageEnabled(true);
// 开启Application Cache功能
settings.setAppCacheEnabled(true);
settings.setAppCacheMaxSize(1024 * 1024 * 10);
//设置支持MixedContent,快捷回复或者其他的消息中可能包含http的图片,如不支持聊天内容中的http图片资源会显示加载失败
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//支持文件选择,具体实现示例见 3.3.1 Android示例
public WebChromeClient mWebChromeClient = new WebChromeClient() {
// For Android >= 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType) {
WebViewFragment.this.openFileChooser(valueCallback, acceptType, null);
}
//For Android >= 4.1
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
WebViewFragment.this.openFileChooser(valueCallback, acceptType, capture);
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return WebViewFragment.this.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
return false;
}
}
//支持tel mail协议 ,具体实现示例见 3.3.1 Android示例
webView.setWebViewClient(mWebViewClient);
public WebViewClient mWebViewClient = new WebViewClient() {
//>= Android 5.0
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return WebViewFragment.this.shouldOverrideUrlLoading(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return WebViewFragment.this.shouldOverrideUrlLoading(view, url);
}
}
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return;
}
if (url.startsWith("mailto:")) {
openMail(url);
return;
}
if (url.startsWith("http")) {
view.loadUrl(url);
}
return true;
}
Ios:
//UIWebView 支持mail、tel协议
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
//tel或者mail协议在此拦截
// request.URL.absoluteString 样例tel:188XXXX1234
return YES;
}
//WKWebView 支持mail、tel协议
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//navigationAction.request.URL.absoluteString 与UIWeb处理类似
decisionHandler(WKNavigationActionPolicyAllow);
}
4.3 UI适配
APP集成后如果Native设置了标题栏,可在样式中心隐藏掉H5的标题栏
手机端显示效果:
Android Demo 源码下载。
Android QuickStart 详细文档。