ndroid在加載網頁H5時,會用到WebView組件,以實現服務端可配置,靈活定義Action。
這個時候就需要Java與JavaScript進行交互,典型的應用場景就是電商類APP,如京東和淘寶。
WebView支持JavaScript這些交互動作,那么問題來了,我們怎么實現Java編寫的安卓程序與JavaScript編寫的網頁進行交互呢?下面給大家介紹下,如何實現Java與JavaScript之間的相互調用。
在Java層調用JavaScript的方式:
Java中調用JavaScript有兩種方式,同步和異步:
同步阻塞UI線程的方式: webView.loadUrl("javascript:funtion()")
異步非阻塞方式: evaluateJavaScript()(在API level 19加入)
請看下面代碼:
java中
// android 調用 JavaScript public void callJavaScript(View view) { if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.KITKAT) { // 異步運行調用JavaScript的方式 mWebView.evaluateJavascript("javascript:callAlert()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.e(TAG, "onReceiveValue" + value);//打印返回的值 } }); } else { if (mWebView !=null) { // 同步阻塞UI線程式調用 mWebView.loadUrl("javascript:callAlert()"); } } }
JavaScript中
function callAlert(){ alert("Android call html Alert function !"); return "這是需要返回的值"; }
在JavaScript調用Android中Java的方式
在JavaScript有三種方式可以調用java代碼:
使用 mWebView.addJavascriptInterface(new JavaScriptInject(this), "JavaScriptInject")
java中
mWebView=findViewById(R.id.webview); mWebView.getSettings().setAllowFileAccessFromFileURLs(true); mWebView.addJavascriptInterface(new JavaScriptInject(this), "JavaScriptInject"); public class JavaScriptInject { private Context mContext; JavaScriptInject(Context context) { this.mContext=context; } // 被JS調用的方法必須加入@JavascriptInterface注解 @JavascriptInterface public void callAndroid(String string) { Toast.makeText(mContext,"javascript call Android", Toast.LENGTH_LONG).show(); Log.e("JavaScriptInject", "getAndroidInfo " + string); } }
JavaScript中
function callAndroid(){ JavaScriptInject.callAndroid("javascript call android"); }
2. 通過重寫WebChromeClient中的回調方法攔截JS不同對話框消息:
onJsAlert(WebView view, String url, String message, JsResult result) 對應攔截 alert()
onJsConfirm(WebView view, String url, String message, JsResult result) 對應攔截 confirm()
onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) 對應攔截 prompt()
java中
mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Log.i(TAG,"onJsAlert"); return super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { Log.i(TAG,"onJsConfirm"); return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Log.i(TAG,"onJsPrompt:"+ message); return super.onJsPrompt(view, url, message, defaultValue, result); } });
JavaScript中
function clickPrompt(){ prompt("傳遞給Android的值"); } function clickConfirm(){ confirm("傳遞給Android的值"); } function clickAlert(){ alert("傳遞給Android的值"); }
webview頁面中
<body> <button type="button" id="button3" onclick="clickPrompt()">點擊調用Android onJsPrompt</button> <button type="button" id="button4" onclick="clickConfirm()">點擊調用Android onJsConfirm</button> <button type="button" id="button5" onclick="clickAlert()">點擊調用Android onJsAlert</button> </body>
3. 通過 WebViewClient 的 shouldOverrideUrlLoading 回調,攔截url。
Java中
mWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 解析該 url 的協議,如果檢測到是預先約定好的協議,就調用相應方法 Log.e(TAG,"call shouldOverrideUrlLoading url : " + url); return true; } });
JavaScript中
function callLoadProtocol(){ // 在JS約定所需要的Url協議 document.location="http://www.360.cn?p=ppp&q=qqq"; }
安全方面策略
WebView的addJavascriptInterface方法在Android 4.2版本以下使用時可能出現的安全情況有:
WebView所在頁面對外暴露,即AndroidManifest.xml設置了android:exported="true",會被其他應用惡意調起
網絡劫持導致代碼注入
2. 開發者需對加載的網頁Url多加限制,禁止加載非合法url。
3. WebView對證書錯誤的頁面會打開空白頁。如果當遇到證書錯誤時調用proceed()函數,會忽略證書錯誤繼續訪問,導致https證書校驗完全失效,存在安全隱患。
Java中
mWebView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed();//使用proceed()忽略證書錯誤,會存在安全隱患 } }
4. WebView加載File協議的Url存在安全隱患,需要禁止File協議調用JavaScript,設置為false。如下即可拿到file:///sdcard/test.txt文件內容,這是很危險的情況。
Java中
mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setAllowFileAccessFromFileURLs(true);
JavaScript中
function getFileContent(){ var file="file:///sdcard/test.txt"; var xmlHttpReq=new XMLHttpRequest(); xmlHttpReq.onreadystatechange=function(){ if (xmlHttpReq.readyState==4) { //4: 請求已完成,且響應已就緒 alert(xmlHttpReq.responseText);//執行上傳到遠程 } } xmlHttpReq.open("GET",file,true); xmlHttpReq.send(); } getFileContent();
5. Android3.0以下,Android系統會默認通過searchBoxJavaBridge的Js接口給WebView添加一個JS映射對象:searchBoxJavaBridge對象。該接口可能被利用,實現遠程任意代碼,實現中可以進行刪除該接口。
Java中
super.removeJavascriptInterface("searchBoxJavaBridge_");
6. 為了解決Android4.2中開啟了輔助模式后,LocalActivityManager控制的Activity與AccessibilityInjector不兼容導致的崩潰問題可以在mWebView.getSettings().setJavaScriptEnabled之前執行下面代碼。
Java中
public void fixedAccessibilityInjectorException() { if (Build.VERSION.SDK_INT==17) { try { Object webViewProvider=WebView.class.getMethod("getWebViewProvider").invoke(this); Method getAccessibilityInjector=webViewProvider.getClass().getDeclaredMethod("getAccessibilityInjector"); getAccessibilityInjector.setAccessible(true); Object accessibilityInjector=getAccessibilityInjector.invoke(webViewProvider); getAccessibilityInjector.setAccessible(false); Field mAccessibilityManagerField=accessibilityInjector.getClass().getDeclaredField("mAccessibilityManager"); mAccessibilityManagerField.setAccessible(true); Object mAccessibilityManager=mAccessibilityManagerField.get(accessibilityInjector); mAccessibilityManagerField.setAccessible(false); Field mIsEnabledField=mAccessibilityManager.getClass().getDeclaredField("mIsEnabled"); mIsEnabledField.setAccessible(true); mIsEnabledField.set(mAccessibilityManager, false); mIsEnabledField.setAccessible(false); } catch (Exception e) { e.printStackTrace(); } } }
7. 為了防止Js拿到Android對象后,通過反射(通過getClass()方法來得到Runtime實例)獲取該對象的其他所有方法,從而導致信息泄露和惡意代碼注入,需要過濾掉繼承Object類的方法,包括getClass()方法。過濾方法包括:
getClass
hashCode
notify
notifyAll
equals
toString
wait
免責聲明:轉載自網絡 不用于商業宣傳 版權歸原作者所有 侵權刪
、javaScript介紹
JavaScript是一種基于對象和事件驅動的、并具有安全性能的腳本語言
(客戶端語言)
JavaScript特點
向HTML頁面中添加交互行為
腳本語言,語法和Java類似
解釋性語言,邊解釋邊執行
JavaScript組成:ECMAScript 、DOM、BOM
基本結構:
<script type="text/javascript">
<!—
JavaScript 語句;
—>
</script >
示例:
……
<title>初學JavaScript</title>
</head>
<body>
<script type="text/javascript">
document.write("初學JavaScript");
document.write("<h1>Hello,JavaScript</h1>");
</script>
</body>
</html>
注:<script>…</script>可以包含在文檔中的任何地方,只要保證這些代碼在被使用前已讀取并加載到內存即可
執行原理:
外部JS文件:
<script src="export.js" type="text/javascript"></script>
直接在HTML標簽中使用:
<input name="btn" type="button" value="彈出消息框"
onclick="javascript:alert('歡迎你');"/>
二、基本常見語法:
1、核心語法:同時聲明和賦值變量
var catName="皮皮";
2、數據類型:
undefined:var width;
變量width沒有初始值,將被賦予值undefined;
null:表示一個空值,與undefined值相等;
number:var iNum=23; //整數
var iNum=23.0; //浮點數
boolean:true 和false;
string:一組被引號(單引號或雙引號)括起來的文本
var string1="This is a string";
3、typeof運算符:
typeof檢測變量的返回值
typeof運算符返回值如下函數
undefined:變量被聲明后,但未被賦值
string:用單引號或雙引號來聲明的字符串
boolean:true或false
number:整數或浮點數
object:javascript中的對象、數組和nul
4、String對象:
5、數組:
數組的常用屬性和方法
類別 名稱 描述
屬性 length 設置或返回數組中元素的數目
方法 join( ) 把數組的所有元素放入一個字符串,通過一個的分隔符進行分隔
sort() 對數組排序
push() 向數組末尾添加一個或更多 元素,并返回新的長度
6、邏輯控制語句:
if(條件)
{
//JavaScript代碼;
}
比于 Native App 和 Web App,Hybrid App 憑借其迭代靈活、控制自如、多端同步的優勢在應用市場上越發顯得優勝,主要得力于,其將變更頻繁的部分產品功能使用 H5 開發并在客戶端中借助 WebView 控件嵌入應用當中。所以,開發中我們總會遇到原生 Java 代碼與網頁中的 Js 代碼之間相互調用從而產生的交互問題。
Java 與 Js 彼此調用的前提是設置 WebView 支持 JavaScript 功能:
第一步,在網頁中使用 Js 定義提供給 Java 訪問的方法,就像普通方法定義一樣,如:
第二步,在 Java 代碼中按照 “javascript:XXX” 的 Url 格式使用 WebView 加載訪問即可:
注意:String 類型的參數需要使用單引號 “’” 包裹,數組類型的參數則不用,如:javascript:javaCallJs([01, 02, 03]),其他復雜類型的參數可以轉換為 Json 字符串的形式傳遞。
第一步,在 Java 對象中定義 Js 訪問的方法,如:
注意事項:提供給 Js 訪問的屬性和方法必須定義為 public 類型,并且添加注解 @JavascriptInterface。在 API 17 及更高版本的系統中,任何暴露給 Js 訪問的 Java 接口都需要添加這個注解,否則會報異常:Uncaught TypeError: Object [object Object] has no method ‘XXX’。系統這種做法也是為了降低應用的安全隱患,因為在之前的版本中,Js 可以通過反射的方式訪問注入 WebView 中的 Java 對象的 public 類型 field 和 method,從而隨意修改宿主程序。
第二步,將提供給 Js 訪問的接口內容所屬的 Java 對象注入 WebView 中:
addJavascriptInterface(Object object, String name) 參數說明:object 表示 Js 訪問的接口內容所在的 Java 對象;name 表示 Js 調用 Java 代碼時的接口名稱,與 Js 中的調用保持一致即可。
第三步,Js 按照指定的接口名訪問 Java 代碼,有如下兩種寫法:
這里簡單提供一個可供測試的 Html 網頁和 Activity 代碼:
test.html:
MainActivity.java:
效果圖:
注意:無論是 Java 調用 Js 還是 Js 調用 Java,只能通過參數傳遞數據,而無法獲取彼此方法的返回值!解決方案就是額外添加一層回調來達到這個目的。比如 Java 調用 Js 的方法,Js 計算結束所得結果不能通過 return 語句返回給 Java 調用者,而是再回調 Java 的另一個方法,通過傳參的形式傳遞給 Java。
1.使用 loadUrl() 方法實現 Java 調用 Js 功能時,必須放置在主線程中,否則會發生崩潰異常。比如修改上面的代碼:
運行時會得到如下 logcat 異常信息:
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'Thread-18022'. All WebView methods must be called on the same thread.
如果真的在子線程中遇到調用 Js 的功能,也要將其轉換到主線程中去:
2.Js 調用 Java 方法時,不是在主線程 (Thread Name:main) 中運行的,而是在一個名為 JavaBridge 的線程中執行的,通過如下代碼可以測試:
所以這里需要注意的是,當 Js 調用 Java 時,如果需要 Java 繼續回調 Js,千萬別在 JavascriptInterface 方法體中直接執行 loadUrl() 方法,而是像前面一樣進行線程切換操作。
3.代碼混淆時,記得保持 JavascriptInterface 內容,在 proguard 文件中添加如下類似規則 (有關類名按需修改):
除了上面這種 Java 與 Js 互調方法的方式,還可以利用 WebView 攔截 Url 的方式實現原生應用與 H5 之間的交互動作。通過 WebViewClient 提供的接口攔截網頁內諸如二級跳轉的 Url 鏈接,便可以進行業務邏輯上的判斷處理、Url 參數傳遞等功能,如:
注意:過去常用的 shouldOverrideUrlLoading(WebView view, String url) 方法已經被廢棄。
通過 Java 與 Js 之間的交互可以做很多事情,比如獲取網頁中的圖片,利用原生控件予以展示,類似響應微信公眾號文章中的圖片點擊事件。參考代碼如下:
作者博客地址:
http://yifeng.studio/2016/12/01/android-webview-java-js-interaction
*請認真填寫需求信息,我們會在24小時內與您取得聯系。