详解android与HTML混合开发总结

现在很多的 APP中会嵌套HTML5的页面,比如经常变化的等等,有一部分页面需要原生Java与HTML5中的js进行交互操作,下面介绍一下android中HTML5的使用:

1、关于HTML5种cookie

网页中可能会用到 用户信息等很多参数,可以提前把这些信息放到cookie中,可以采用以下方法: 

 -1) {
 CookieSyncManager.createInstance(context);
 CookieManager cookieManager = CookieManager.getInstance();
 cookieManager.setAcceptCookie(true);
 try {
 List data = getCookiesstring();
 if (!ListUtils.isEmpty(data)) {
 for (String value : data) {
 cookieManager.setCookie(url, value);
 }
 }
 cookieManager.setCookie(url, "client_id=" + Constant.CLIENT_ID + ";path=/;domain=.XXXX.com");
 cookieManager.setCookie(url, "appVersion=" + Constant .VERSION + ";path=/;domain=.XXXX.com"); 
             CookieSyncManager.getInstance().sync(); 
         } catch (Exception e) { 
             LogUtils.e("Exception:" + e.getMessage()); 
         } 
       } 
     } 
   }
 public List getCookiesstring() {
ArrayList data = new ArrayList();
this.clearExpired();
Collection values = this.mCookies.values();
Iterator var3 = values.iterator();
 
while(var3.hasNext()) {
SwiftCookie c = (SwiftCookie)var3.next();
data.add(c.toCookieString());
 }

 return data;
 }

 在 mWebView.loadUrl(Url)之前添加cookie,网页就可以通过cookie取到相应的参数值了。

2、关于js的安全问题

js在4.2以前有漏洞

通过JavaScript,可以访问当前设备的SD卡上面的任何东西,甚至是联系人信息,短信等。好,我们一起来看看是怎么出现这样的错误的。

1,WebView添加了JavaScript对象,并且当前应用具有读写SDCard的权限,也就是:android.permission.WRITE_EXTERNAL_STORAGE

2,JS中可以遍历window对象,找到存在“getClass”方法的对象的对象,然后再通过反射的机制,得到Runtime对象,然后调用静态方法来执行一些命令,比如访问文件的命令.

3,再从执行命令后返回的输入流中得到字符串,就可以得到文件名的信息了。然后想干什么就干什么,好危险。核心JS代码如下:

rush:js;toolbar:false">function execute(cmdArgs)
 {
 for (var obj in window) {
 if ("getClass" in window[obj]) {
 alert(obj);
 returnwindow[obj].getClass().forName("java.lang.Runtime")
.getmethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
 }
 }
}

解决方案:

1、Android 4.2以上的系统

在Android 4.2以上的,google作了修正,通过在Java的远程方法上面声明一个@JavascriptInterface,如下面代码

rush:js;toolbar:false">class JsObject {
@JavascriptInterface 
public String toString() { return "injectedobject"; }
 }
 webView.addJavascriptInterface(new JsObject(), "injectedobject");
 webView.loadData("", "text/html", null);
 webView.loadUrl("javascript:alert(injectedobject.toString())");

 2、Android 4.2以下的系统

这个问题比较难解决,但也不是不能解决

首先,我们肯定不能再调用addJavascriptInterface方法了。关于这个问题,最核心的就是要知道JS事件这一个动作,JS与Java进行交互我们知道,有以下几种,比prompt, alert等,

这样的动作都会对应到WebChromeClient类中相应的方法,对于prompt,它对应的方法是onjsPrompt方法,这个方法的声明如下:

rush:js;toolbar:false">public boolean onjsPrompt(WebView view, String url, String message, 
String defaultValue, JsPromptResult result)

通过这个方法,JS能把信息(文本)传递到Java,而Java也能把信息(文本)传递到JS中,通知这个思路我们能不能找到解决方案呢?

经过一番尝试与分析,找到一种比较可行的方案,请看下面几个小点:

【1】让JS调用一个Javascript方法,这个方法中是调用prompt方法,通过prompt把JS中的信息传递过来,这些信息应该是我们组合成的一段有意义的文本,可能包含:特定标识,方法名称,参数等。

在onjsPrompt方法中,我们去解析传递过来的文本,得到方法名,参数等,再通过反射机制,调用指定的方法,从而调用到Java对象的方法

【2】关于返回值,可以通过prompt返回回去,这样就可以把Java中方法的处理结果返回到Js中。

【3】我们需要动态生成一段声明Javascript方法的JS脚本,通过loadUrl来加载它,从而注册到html页面中,具体的代码如下:

rush:js;toolbar:false">javascript:(function JsAddJavascriptInterface_(){
if (typeof(window.jsInterface)!='undefined') {
console.log('window.jsInterface_js_interface_name is exist!!');} 
else {
window.jsInterface = {
onButtonClick:function(arg0) { 
return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));
},
 
 onImageClick:function(arg0,arg1,arg2) { 
 prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));
 },
 };
 }
 }
 )()

 说明:

1,上面代码中的jsInterface就是要注册的对象名,它注册了两个方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有返回值,就添加上return。

2,prompt中是我们约定的字符串,它包含特定的标识符MyApp:,后面包含了一串JSON字符串,它包含了方法名,参数,对象名等。

3,当JS调用onButtonClick或onImageClick时,就会回调到Java层中的onjsPrompt方法,我们再解析出方法名,参数,对象名,再反射调用方法

4,window.jsInterface这表示在window上声明了一个Js对象,声明方法的形式是:方法名:function(参数1,参数2)

3、在html5中进行java和js的交互

方法一:

rush:java;toolbar:false">mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "xxx");

然后在当前类中实现以下方法

rush:java;toolbar:false">@JavascriptInterface
public void callbackFromH5(final String j) {
//Todo
}

callbackFromH5的名字必须和网页中的js方法名一样

Java调用js方法

rush:js;toolbar:false">mWebView.loadUrl(String.format("javascript:java2js(0)"));//这里是java端调用webview的JS

js方法名需要和网页端一直

方法二: 

jsbridge方法(https://github.com/lzyzsd/JsBridge)

Android JsBridge 就是用来在 Android app的原生 java 代码与 javascript 代码中架设通信(调用)桥梁的辅助工具

1、将jsBridge.jar引入到我们的工程 

rush:java;toolbar:false">Android Studio:
repositories {
// ...
maven { url "https://jitpack.io" }
}
dependencies {
 compile 'com.github.lzyzsd:jsbridge:1.0.4'
}

2、布局文件

rush:xml;toolbar:false;">

 
 

 3、java代码

rush:java;toolbar:false">//加载服务器网页
webView.loadUrl("https://www.baidu.com");

//必须和js同名函数。
webView.registerHandler("submitFromWeb", new BridgeHandler() {

@Override public void handler(String data, CallBackFunction
function) {

String str = "html返回给java的数据:" + data;

makeText(MainActivity.this, str, LENGTH_SHORT).show();

Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
function.onCallBack(str + ",Java经过处理后:" + str.substring(, ));
}

});
//模拟用户获取本地位置
User user = new User();
Location location = new Location();
location.address = "xxx";
user.location = location;
user.name = "Bruce";

webView.callHandler("functionInjs", new Gson().toJson(user), new CallBackFunction() {@Override public void onCallBack(String data) {
makeText(MainActivity.this, "网页在获取你的信息", LENGTH_SHORT).show();

}
});

webView.send("hello");
webView.callHandler("functionInjs", "data from Java", new CallBackFunction() {

@Override public void onCallBack(String data) {
// Todo Auto-generated method stub
Log.i(TAG, "reponse data from js " + data);
}

});

js调用

rush:java;toolbar:false">var str1 = document.getElementById("text1").value;
var str2 = document.getElementById("text2").value;
//调用本地java方法
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, {
'param': str
}
,
function(responseData) {
document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
}
);
 
//注册事件监听 
 document.addEventListener(
 'WebViewJavascriptBridgeReady' 
 , function() {
 callback(WebViewJavascriptBridge)
 },
 false 
 );
 
//注册回调函数,第一次连接时调用 初始化函数 
connectWebViewJavascriptBridge(function(bridge) {
 bridge.init(function(message, responseCallback) {
 console.log('JS got a message', message);
 var data = {
 'Javascript Responds': 'Wee!' 
 };
 console.log('JS responding with', data);
 responseCallback(data);
 });
 
 bridge.registerHandler("functionInjs", function(data, responseCallback) {
 document.getElementById("show").innerHTML = ("data from Java: = " + data);
 var responseData = "Javascript Says Right back aka!";
 responseCallback(responseData);
 });
 })

4、关于webView的优化

1、设置WebView 缓存模式

rush:js;toolbar:false">private void initWebView() {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);//设置 缓存模式
// 开启 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//开启 database storage API 功能
mWebView.getSettings().setDatabaseEnabled(true); 
 String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME;
 //String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME;
 Log.i(TAG, "cacheDirPath="+cacheDirPath);
 //设置数据库缓存路径
 mWebView.getSettings().setDatabasePath(cacheDirPath);
 //设置Application Caches 缓存目录
 mWebView.getSettings().setAppCachePath(cacheDirPath);
 //开启 Application Caches 功能
 mWebView.getSettings().setAppCacheEnabled(true);

2、清除缓存

rush:js;toolbar:false">/** 
* 清除WebView缓存 
*/
public void clearWebViewCache() {

//清理Webview缓存数据库
try {
deleteDatabase("webview.db");
deleteDatabase("webviewCache.db");
} catch(Exception e) {
e.printstacktrace();
}

//WebView 缓存文件
File appCacheDir = new File(getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME);
Log.e(TAG, "appCacheDir path=" + appCacheDir.getAbsolutePath());

File webviewCacheDir = new File(getCacheDir().getAbsolutePath() + "/webviewCache");
Log.e(TAG, "webviewCacheDir path=" + webviewCacheDir.getAbsolutePath());

//删除webview 缓存目录
if (webviewCacheDir.exists()) {
deleteFile(webviewCacheDir);
}
//删除webview 缓存 缓存目录
if (appCacheDir.exists()) {
deleteFile(appCacheDir);
}
}

    3、在使用WebView加载网页的时候,有一些固定的资源文件如js/css/图片等资源会比较大,如果直接从网络加载会导致页面加载的比较慢,而且会消耗比较多的流量。所以这些文件应该放在assets里面同app打包。

解决这个问题用到API 11(HONEYCOMB)提供的shouldInterceptRequest(WebView view, String url) 函数来加载本地资源。

API 21又将这个方法弃用了,是重载一个新的shouldInterceptRequest,需要的参数中将url替换成了成了request。

比如有一个图片xxxxx.png,这个图片已经放在了assets中,现在加载了一个外部html,就需要直接把assets里面的图片拿出来加载而不需要重新从网络获取。当然可以在html里面将图片链接换成file:///android_asset/xxxxx.png,

但是这样这个html就不能在Android ,ios,WAP中公用了。

= Build.VERSION_CODES.HONEYCOMB) {
response = super.shouldInterceptRequest(view, url);
if (url.contains("xxxxx.png")) {
try {
response = new WebResourceResponse("image/png", "UTF-8", getAssets().open("xxxxx.png"));
} catch(IOException e) {
e.printstacktrace();
}
}
}
//return super.shouldInterceptRequest(view, url);
return response;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
WebResourceResponse response = null;
response = super.shouldInterceptRequest(view, request);
if (url.contains("xxxxx.png")) {
try {
response = new WebResourceResponse("image/png", "UTF-", getAssets().open("xxxxx.png"));
} catch(IOException e) {
e.printstacktrace();
}
}
return response;
}
}

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我正在使用一个返回PNG编码的base64字符串的插件,我无法更改它,我必须使用它,但是我真正需要的是tiff编码值(base-64).有办法吗?我试图创建一个画布,加载pngbase64,然后使用toDataURL(‘image/tiff’),但经过一些研究,我发现不支持tiff作为toDataURL()的输出.有什么建议么?解决
我有这个正在玩的浏览器游戏.但是我也在尝试使其也可以在移动浏览器中使用.但是当同时按下多个按钮时,我似乎无法使其正常工作.我有这个游戏,您可以左右移动并跳跃.(见下图)touchbuttonsingamehttp://www.userhome.org/mobilegame.png我的代码如下所示:functioninittouc
我正在使用d3.js进行散点图绘制,我想绘制x和y轴以使其在点(100,75)相交.如何做到这一点?我在用svg.append("g").attr("class","axis").attr("transform","translate(0,"+(padding+223)+")").call(xAxis2);//CreateY2ax
我在htmlimg标签中有一张图片.我的要求是,当用户单击图像时,它将在拖动鼠标的同时标记一个点并画一条线.然后,当用户完成拖动并单击图像时,它应该显示的线条还以毫米/厘米为单位显示线条的尺寸.即,用户必须在图像上画一条线并显示其画线的距离/长度(以毫米/厘米为单位).如何在Web
我正在开发智能电视应用程序,并且从IP摄像机流媒体直播视频是该功能的很大一部分.相机手册指出,如果将其设置为流MPEG-4或H.264,则不支持HTTP.只有RTSP/RTP.替代方法(通过HTTP)是MotionJPEG,我希望尽可能避免这种情况(电视上的帧速率糟透了).是否有用于RTSP的JavaScript/HT
遵循ControlstartpositionanddurationofplayinHTML5video的原则,我尝试使每个片段播放完后自动将视频从一个片段跳到下一片段.每个片段的持续时间相同,每个片段的开始时间位于一个数组中.我似乎无法弄清楚如何在addEventListener之后循环遍历数组.varvideo=document
我正在尝试将相当简单的html导出到canvas,然后导出到png.为此,我正在使用rasterizeHTML(http://cburgmer.github.ioasterizeHTML.js/).我面临的问题是,我收到一条警告,例如正在加载外部图像/资源,但我没有.这是我尝试过的:HTML:<canvasheight="500"width="500"id="rasterize
Thisjsfiddledemonstratesthefollowingissue.最简单的示例是:<inputid="number"type="number"value="1">console.log(document.getElementById('number').value);这将按预期记录1.但是,这:<inputid="number"type
我正在使用Spring表单,并且想使用Spring表单似乎不支持的HTML5‘required’属性.<form:inputpath="someinput"cssClass="required"/>我似乎做不到<form:inputpath="someinput"cssClass="required"required="required"/>因为Spring
我正在使用angularJS开发应用程序.我的应用程序要求将数据保存在本地.因此,我正在使用HTML5本地存储.HTML5本地存储的问题在于,当用户清除浏览数据时,所有数据都将丢失,我们需要避免这种情况.我意识到,由于数据存储在用户计算机上,因此无法100%保护数据.本质上,是否有一种方法可以
我希望在首次加载时在我的网站上预加载一些小视频文件.这样,当用户单击以进入网站的一部分时,可以自动播放和使用它们.我需要无缝过渡,因此理想情况下应预加载整个视频文件,以免造成任何延迟.香港专业教育学院尝试了一些不同的插件,如html5preloader,以及其他一些JS技术将视频文件
 指引表单在网页应用中十分重要,基本上任何一个网站都必须使用到表单元素,所以表单的美观和易于交互对于网站设计就变得十分重要。HTML5对目前Web表单进行了全面提升,使得我们使用表单更加智能。它在保持了简单易用的特性的同时,还增加了许多内置的控件或者控件属性来满足用户的需
我正在学习JavaScript,但经验不足.但是我正在制作一个HTML表,我想在每个表单元格(<td>)中添加一个onClick事件.<tableid="1"><tr><tdonClick="tes()">1<d><tdonClick="tes()">2<d><r><tr
我正在使用AngularJS,并且在选择数据列表(html5)的元素时要使用指令ng-click.这是我的实际代码示例:<label>Search</label><inputtype="text"class="input-search"list="datalistcit"ng-change="changeQuery(queryCity)"ng-model="queryC
当您将来自其他域的图像放在画布中时,画布会受到污染,并且诸如getDataURL或getImageData之类的某些功能会被禁用.当您尝试使用此类功能时,会发生SECURITY_ERROR错误.为什么有这种保护措施?这种方法使用了哪种攻击?谢谢!编辑:我应该问:如何将来自其他域的图像用于攻击?   我弄不
我写了这个webRTC应用程序,它使用添加了不同滤镜的照片进行拍摄.当我单击“单击”按钮时,来自webRTC视频提要的帧将被加载到右侧的画布上.HTML代码-<videoid="vid"autoplay="true"></video><canvasid="cvs"></canvas><buttonid="btn1"onclick="start(
如果Chrome中使用了网络摄像头,则该页面的标签上会出现一个红点.并且,如果其他页面尝试访问网络摄像头,视频将变黑.我的问题是,是否可以使用JavaScript检查是否正在使用网络摄像头?怎么样?通过使用navigator.getUserMedia,我尝试了以下代码:navigator.getUserMedia=navigator.get
好的,所以我发现使用JqueryMobile和Phonegap开发androidApp在android4中工作正常,但是在旧版本(例如2.2.2)上,可滚动功能中断.因此,我尝试使用iScroll,但是存在冲突的问题,最终我只是决定编写一个Javascript函数来处理它.它曾经可以工作,但是现在坏了.所以这里是:<!DOCTYPEHT
我有一个正在构建的Webapp,并且刚开始使用SQLite.我已经能够创建表单,打开我创建的数据库,创建表和所需的字段以及在字段中输入数据.现在,我尝试使用SELECT语句读回数据,以将其显示在屏幕上以及列的列表中.我只是不知道javascript或HTML5中的SELECTstatemnt的语法‘SELECT*FRO
这些是我正在开发的表单的屏幕截图.我要设计表单中的选择框,以使选项中的文本右对齐,并且在选择选项后,所显示的所选文本也应显示如下图所示.HTML代码:<select><optionvalue="0"selected="selected"style="text-align:right;">EqualsTo</option><optionvalue="1&quo