android cordova原理,Cordova android平台开发四(支持在新webview中打开网页,并支持cordova其他插件)...

前言:

前段时间项目出需求,想要在android应用中打开一个远程并且可以调用cordova插件功能的网页.网上基本没有这个方面的资料,故写此文章记录一下~

首先,这个需求有两个问题要解决:

在已是cordova项目网页的基础上,重新创建一个webview实例来加载远程网页;

这个远程网页具有调用其他插件的能力.

下面我们来分别实现这两个需求......

6db3988b7a1b161eb539ea8b05f1075d.png

干了这一碗.png

一.创建webview,加载远程网页

这个需求很简单,引入cordova-plugin-inappbrowser插件

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按钮。

至此,第一个需求完成.............(欢呼一下,so little case~)

二.在远程网页上调用插件功能

这个需求不容易搞定,因为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 = %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;

}

技术重在分享,不敢闭门造车.文章如有不当,欢迎指正~ >-< ~

(写作不易,加个关注呗~)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值