Cordova|Cordova android平台开发四(支持在新webview中打开网页,并支持cordova其他插件)
前言:
前段时间项目出需求,想要在android应用中打开一个远程并且可以调用cordova插件功能的网页.网上基本没有这个方面的资料,故写此文章记录一下~
首先,这个需求有两个问题要解决:
- 在已是cordova项目网页的基础上,重新创建一个webview实例来加载远程网页;
- 这个远程网页具有调用其他插件的能力.
下面我们来分别实现这两个需求......
干了这一碗.png
InAppBrowser可以使用新的窗口实例打开连接,提供了地址栏的显示隐藏,一些窗口操作。
不能设置地址栏内容、按钮、样式等,如果想更好的操作需要使用cordova-plugin-themeablebrowser插件
官网api:http://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/
- 命令:
cordova plugin add cordova-plugin-inappbrowser
- 重写配置
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
window.open = cordova.InAppBrowser.open;
}
- 使用
$scope.openUrl=function(){
if (!cordova.InAppBrowser) {
return;
}
// toolbar=yes 仅iOS有效,提供关闭、返回、前进三个按钮
// toolbarposition=top/bottom 仅iOS有效,决定toolbar的位置
// closebuttoncaption=关闭 仅iOS有效
window.open('http://www.baidu.com', '_blank',
'location=no,toolbar=yes,toolbarposition=top,closebuttoncaption=关闭');
}
- openUrl是我写的一个方法,在html页面中在相应位置用ng-click去调用这个方法,此时就会触发浏览器跳转的事件,
- 根据open()中的设置,URL参数是百度的网址;
- target参数为"_blank",也就是在App中打开网址的页面;
target的参数有三种:
_self:如果URL地址在WhiteList中,则用Cordova的WhiteList将其打开;
_blank:直接在App中将其地址打开,原理: 创建新的webview在一个全屏的dialog上;
_system:则是用手机默认浏览器将新页面打开 - options参数为iOS系统下会显示toolbar,toolbar的位置在顶部,closebuttoncaption隐藏Done按钮。
二.在远程网页上调用插件功能 这个需求不容易搞定,因为inappbrowser的实现默认是不允许使用cordova的任何资源,只是提供用新webview实例来加载一个纯的网页,所以需要对inAPPbrowser进行改造:
- 在网页中加载cordova.js
要想在新的网页中调用插件功能,必须在网页中注入cordova.js; 我们通过在webview中拦截自定义URL来加载本地cordova.js
改造InAppBrowser$InAppBrowserClient.class类:
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
....
// 在网页加载完毕,根据需要注入 nativeJs
// 自定义URL:private static final String NATIVE_JS_PREFIX = "https://native-js/";
if (nativeJs) {
String jsWrapper = "(function(d) { var c = d.createElement('script');
c.src = https://www.it610.com/article/%s;
d.body.appendChild(c);
})(document)";
//在InAppBrowser WebView中注入一个对象(脚本或样式)。
injectDeferredObject(NATIVE_JS_PREFIX + "cordova.js", jsWrapper);
}
}
重写shouldInterceptRequest方法,对自定义URL拦截处理
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// SDK API < 21 时走这个方法
return processInterceptRequest(view, url);
}
@SuppressLint("NewApi")
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
// SDK API >= 21 时走这个方法
Uri uri = request.getUrl();
String url = uri.toString();
return processInterceptRequest(view, url);
}
public WebResourceResponse processInterceptRequest(WebView view, String url) {
// 对于注入的 nativeJs,从本地读取
if (url.startsWith(NATIVE_JS_PREFIX) && url.endsWith(".js")) {
String path = url.substring(NATIVE_JS_PREFIX.length());
String assetPath = "www/" + path;
try {
//打开并返回本地js文件资源
InputStream inputStream = webView.getContext().getAssets().open(assetPath);
return new WebResourceResponse("application/javascript", "UTF-8", inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 支持inappbrowser调用cordova插件功能
inappbrowser默认只能触发"gap-iab"协议的回调,其他回调不能被触发,为了支持其他协议,我们要对InAppChromeClient.class修改:
/**
* Tell the client to display a prompt dialog to the user.
* If the client returns true, WebView will assume that the client will
* handle the prompt dialog and call the appropriate JsPromptResult method.
* (返回true,表示客户端处理提示行为,调用适当的JsPromptResult方法)
*
* The prompt bridge provided for the InAppBrowser is capable of executing any
* oustanding callback belonging to the InAppBrowser plugin. Care has been
* taken that other callbacks cannot be triggered, and that no other code
* execution is possible.
* (为InAppBrowser提供的提示桥能够执行任何属于InAppBrowser插件的回调。注意,
其他回调不能被触发,并且不可能有其他的代码执行。)
*
* To trigger the bridge, the prompt default value should be of the form:
*
* gap-iab://
*
* where is the string id of the callback to trigger (something
* like "InAppBrowser0123456789")
*
* If present, the prompt message is expected to be a JSON-encoded value to
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
*
* @param view
* @param url
* @param message
* @param defaultValue
* @param result
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
if (defaultValue != null && defaultValue.startsWith("gap")) {
if(defaultValue.startsWith("gap-iab://")) {
//处理inappbrowser插件的回调
....
}
else
{
// 处理 cordova API回调
CordovaWebViewEngine engine = webView.getEngine();
if (engine != null) {
CordovaBridge cordovaBridge = null;
try {
//反射得到CordovaBridge 的对象实例
Field bridge = engine.getClass().getDeclaredField("bridge");
bridge.setAccessible(true);
cordovaBridge = (CordovaBridge) bridge.get(engine);
bridge.setAccessible(false);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (cordovaBridge != null) {
// Unlike the @JavascriptInterface bridge, this method is always called on the UI thread.
String handledRet = cordovaBridge.promptOnJsPrompt(url, message, defaultValue);
if (handledRet != null) {
result.confirm(handledRet);
} else {
final JsPromptResult final_result = result;
CordovaDialogsHelper dialogsHelper = new CordovaDialogsHelper(webView.getContext());
dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() {
@Override
public void gotResult(boolean success, String value) {
if (success) {
final_result.confirm(value);
} else {
final_result.cancel();
}
}
});
}
}else {
// Anything else with a gap: prefix should get this message
LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue);
result.cancel();
}
}
return true;
}
}
return false;
}
现在,你可以试试在新的webview中调用其他插件的API了,是不是很爽(~ >-< ~)
写在最后:
经测试有一个小bug,若通过inappbrowser跳转新的webview后可以正常调用cordova API,但是点击返回如果跳转前的网页没用刷新(即:不重新加载),那么此时的页面将不可以调用cordova API.
这是因为cordova初始时在原生会随机生成一个整数传给网页,来作为原生与H5之间交互的secret.此值由UI线程编写,由JS线程读取。
这里CordovaBridge.class:
/** Called by cordova.js to initialize the bridge. */
int generateBridgeSecret() {
// 如果已经产生过 secret,则直接返回原值,不再重新生成。
// 目的是让一个 CordovaBridge 实例能够服务于多个 webview 实例(比如用 InAppBrowser 新打开的网页),避免串扰。
if (expectedBridgeSecret >= 0)
return expectedBridgeSecret;
SecureRandom randGen = new SecureRandom();
expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE);
return expectedBridgeSecret;
}
【Cordova|Cordova android平台开发四(支持在新webview中打开网页,并支持cordova其他插件)】技术重在分享,不敢闭门造车.文章如有不当,欢迎指正~ >-< ~
(写作不易,加个关注呗~)
推荐阅读
- android第三方框架(五)ButterKnife
- 基于微信小程序带后端ssm接口小区物业管理平台设计
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- android防止连续点击的简单实现(kotlin)