現(xiàn)在很多的 APP中會(huì)嵌套HTML5的頁(yè)面,比如經(jīng)常變化的等等,有一部分頁(yè)面需要原生Java與HTML5中的js進(jìn)行交互操作,下面介紹一下android中HTML5的使用:
1、關(guān)于HTML5種cookie
網(wǎng)頁(yè)中可能會(huì)用到 用戶信息等很多參數(shù),可以提前把這些信息放到cookie中,可以采用以下方法:
public static void addCookies(Context context, WebView webView, String url) {
String url=“https://www.xxxx.com/xx/xx/”
String protocol = "";
String authority = "";
try {
URL urlObj = new URL(url);
protocol = urlObj.getProtocol();
authority = urlObj.getAuthority();
} catch (Exception e) {
e.printStackTrace();
}
String ua = webView.getSettings().getUserAgentString();
webView.getSettings().setUserAgentString(Constant.PROJECT_NAME + "/" + ParamHandler.getVersion(context) + "(" + ua + "; HFWSH)");
if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(protocol) && !TextUtils.isEmpty(authority)) {
if (protocol.equals("https") && authority.indexOf("liepin.com") > -1) {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
try {
List<String> 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<String> 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,網(wǎng)頁(yè)就可以通過(guò)cookie取到相應(yīng)的參數(shù)值了。
2、關(guān)于js的安全問(wèn)題
js在4.2以前有漏洞
通過(guò)JavaScript,可以訪問(wèn)當(dāng)前設(shè)備的SD卡上面的任何東西,甚至是聯(lián)系人信息,短信等。好,我們一起來(lái)看看是怎么出現(xiàn)這樣的錯(cuò)誤的。
1,WebView添加了JavaScript對(duì)象,并且當(dāng)前應(yīng)用具有讀寫(xiě)SDCard的權(quán)限,也就是:android.permission.WRITE_EXTERNAL_STORAGE
2,JS中可以遍歷window對(duì)象,找到存在“getClass”方法的對(duì)象的對(duì)象,然后再通過(guò)反射的機(jī)制,得到Runtime對(duì)象,然后調(diào)用靜態(tài)方法來(lái)執(zhí)行一些命令,比如訪問(wèn)文件的命令.
3,再?gòu)膱?zhí)行命令后返回的輸入流中得到字符串,就可以得到文件名的信息了。然后想干什么就干什么,好危險(xiǎn)。核心JS代碼如下:
function execute(cmdArgs)
{
for (var obj in window) {
if ("getClass" in window[obj]) {
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
.getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}
}
}
解決方案:
1,Android 4.2以上的系統(tǒng)
在Android 4.2以上的,google作了修正,通過(guò)在Java的遠(yuǎn)程方法上面聲明一個(gè)@JavascriptInterface,如下面代碼:
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以下的系統(tǒng)
這個(gè)問(wèn)題比較難解決,但也不是不能解決。
首先,我們肯定不能再調(diào)用addJavascriptInterface方法了。關(guān)于這個(gè)問(wèn)題,最核心的就是要知道JS事件這一個(gè)動(dòng)作,JS與Java進(jìn)行交互我們知道,有以下幾種,比prompt, alert等,
這樣的動(dòng)作都會(huì)對(duì)應(yīng)到WebChromeClient類中相應(yīng)的方法,對(duì)于prompt,它對(duì)應(yīng)的方法是onJsPrompt方法,這個(gè)方法的聲明如下:
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
通過(guò)這個(gè)方法,JS能把信息(文本)傳遞到Java,而Java也能把信息(文本)傳遞到JS中,通知這個(gè)思路我們能不能找到解決方案呢?
經(jīng)過(guò)一番嘗試與分析,找到一種比較可行的方案,請(qǐng)看下面幾個(gè)小點(diǎn):
【1】讓JS調(diào)用一個(gè)Javascript方法,這個(gè)方法中是調(diào)用prompt方法,通過(guò)prompt把JS中的信息傳遞過(guò)來(lái),這些信息應(yīng)該是我們組合成的一段有意義的文本,可能包含:特定標(biāo)識(shí),方法名稱,參數(shù)等。
在onJsPrompt方法中,我們?nèi)ソ馕鰝鬟f過(guò)來(lái)的文本,得到方法名,參數(shù)等,再通過(guò)反射機(jī)制,調(diào)用指定的方法,從而調(diào)用到Java對(duì)象的方法。
【2】關(guān)于返回值,可以通過(guò)prompt返回回去,這樣就可以把Java中方法的處理結(jié)果返回到Js中。
【3】我們需要?jiǎng)討B(tài)生成一段聲明Javascript方法的JS腳本,通過(guò)loadUrl來(lái)加載它,從而注冊(cè)到html頁(yè)面中,具體的代碼如下:
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]}));
},
};
}
}
)()
說(shuō)明:
1,上面代碼中的jsInterface就是要注冊(cè)的對(duì)象名,它注冊(cè)了兩個(gè)方法,onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有返回值,就添加上return。
2,prompt中是我們約定的字符串,它包含特定的標(biāo)識(shí)符MyApp:,后面包含了一串JSON字符串,它包含了方法名,參數(shù),對(duì)象名等。
3,當(dāng)JS調(diào)用onButtonClick或onImageClick時(shí),就會(huì)回調(diào)到Java層中的onJsPrompt方法,我們?cè)俳馕龀龇椒?,參?shù),對(duì)象名,再反射調(diào)用方法。
4,window.jsInterface這表示在window上聲明了一個(gè)Js對(duì)象,聲明方法的形式是:方法名:function(參數(shù)1,參數(shù)2)
3、在html5中進(jìn)行java和js的交互
1)、方法一:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "xxx");
然后在當(dāng)前類中實(shí)現(xiàn)以下方法:
@JavascriptInterface
public void callbackFromH5(final String j) {
//TODO
}
callbackFromH5的名字必須和網(wǎng)頁(yè)中的js方法名一樣
Java調(diào)用js方法:
mWebView.loadUrl(String.format("javascript:java2js(0)"));//這里是java端調(diào)用webview的JS
js方法名需要和網(wǎng)頁(yè)端一直
2)方法二:
jsbridge方法(https://github.com/lzyzsd/JsBridge)
Android JsBridge 就是用來(lái)在 Android app的原生 java 代碼與 javascript 代碼中架設(shè)通信(調(diào)用)橋梁的輔助工具
1 將jsBridge.jar引入到我們的工程
Android Studio:
repositories {
// ...
maven { url "https://jitpack.io" }
}
dependencies {
compile 'com.github.lzyzsd:jsbridge:1.0.4'
}
2、布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- button 演示Java調(diào)用web -->
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:text="@string/button_name"
android:layout_height="dp"
/>
<!-- webview 演示web調(diào)用Java -->
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.github.lzyzsd.jsbridge.BridgeWebView>
</LinearLayout>
3、java代碼
//加載服務(wù)器網(wǎng)頁(yè)
webView.loadUrl("https://www.baidu.com");
//必須和js同名函數(shù)。
webView.registerHandler("submitFromWeb", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
String str ="html返回給java的數(shù)據(jù):" + data;
makeText(MainActivity.this, str, LENGTH_SHORT).show();
Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
function.onCallBack( str + ",Java經(jīng)過(guò)處理后:"+ 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, "網(wǎng)頁(yè)在獲取你的信息", 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調(diào)用
var str1 = document.getElementById("text1").value;
var str2 = document.getElementById("text2").value;
//調(diào)用本地java方法
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, {'param': str}
, function(responseData) {
document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
}
);
//注冊(cè)事件監(jiān)聽(tīng)
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function() {
callback(WebViewJavascriptBridge)
},
false
);
//注冊(cè)回調(diào)函數(shù),第一次連接時(shí)調(diào)用 初始化函數(shù)
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、關(guān)于webView的優(yōu)化
1、設(shè)置WebView 緩存模式
private void initWebView() {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); //設(shè)置 緩存模式
// 開(kāi)啟 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//開(kāi)啟 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);
//設(shè)置數(shù)據(jù)庫(kù)緩存路徑
mWebView.getSettings().setDatabasePath(cacheDirPath);
//設(shè)置 Application Caches 緩存目錄
mWebView.getSettings().setAppCachePath(cacheDirPath);
//開(kāi)啟 Application Caches 功能
mWebView.getSettings().setAppCacheEnabled(true);
2、清除緩存
/**
* 清除WebView緩存
*/
public void clearWebViewCache(){
//清理Webview緩存數(shù)據(jù)庫(kù)
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加載網(wǎng)頁(yè)的時(shí)候,有一些固定的資源文件如js/css/圖片等資源會(huì)比較大,如果直接從網(wǎng)絡(luò)加載會(huì)導(dǎo)致頁(yè)面加載的比較慢,而且會(huì)消耗比較多的流量。所以這些文件應(yīng)該放在assets里面同app打包。
解決這個(gè)問(wèn)題用到API 11(HONEYCOMB)提供的shouldInterceptRequest(WebView view, String url) 函數(shù)來(lái)加載本地資源。
API 21又將這個(gè)方法棄用了,是重載一個(gè)新的shouldInterceptRequest,需要的參數(shù)中將url替換成了成了request。
比如有一個(gè)圖片xxxxx.png,這個(gè)圖片已經(jīng)放在了assets中,現(xiàn)在加載了一個(gè)外部html,就需要直接把a(bǔ)ssets里面的圖片拿出來(lái)加載而不需要重新從網(wǎng)絡(luò)獲取。當(dāng)然可以在html里面將圖片鏈接換成file:///android_asset/xxxxx.png,
但是這樣這個(gè)html就不能在Android ,ios,WAP中公用了。
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
WebResourceResponse response = null;
if (Build.VERSION.SDK_INT >= 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;
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。