Android和H5、JS進行交互調用
Android開發(fā)過程中,我們或多或少都會用到webview,使用webview來展示一些經常變動的界面更加方便簡單,也更易于維護。另一方面hybrid App開發(fā)也現(xiàn)在用的也越來越多了,其中native和h5之間的交互更是必不可少的。Android中是如何和H5交互的?或者說Android中是如何和JS交互的?
一、webView加載頁面
我們都知道在Android中是通過webView來加載html頁面的,根據(jù)HTML文件所在的位置不同寫法也不同:
- 例如:加載assets文件夾下的test.html頁面
mWebView.loadUrl("file:///android_asset/test.html")
- 例如:加載網頁
mWebView.loadUrl("http://www.baidu.com")
如果只是這樣調用mWebView.loadUrl()加載的話,那么當你點擊頁面中的鏈接時,頁面將會在你手機默認的瀏覽器上打開。那如果想要頁面在App內中打開的話,那么就得設置setWebViewClient:
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//我們可以在這里攔截特定的rl請求,然后進行自己要的操作
if (url.equals("file:///android_asset/test2.html")) {
Log.e(TAG, "shouldOverrideUrlLoading: " + url);
startActivity(new Intent(MainActivity.this,Main2Activity.class));
return true;
} else {
//這里我們自己重新加載新的url頁面,防止點擊鏈接跳轉到系統(tǒng)瀏覽器
mWebView.loadUrl(url);
return true;
}
}
}
});
重寫Activity的onBackPressed方法,使得返回按鈕不會關閉當前頁面,而是返回webview的上一個歷史頁面:
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
//返回上一個頁
webView.goBack();
return ;
}
super.onBackPressed();
}
二、給webView添加加載新頁面的進度條。
-
開啟和關閉進度條:
webView.setWebViewClient(new WebViewClient() { //重寫頁面打開和結束的監(jiān)聽。打開時彈出菊花,關閉時隱藏菊花 /** * 界面打開的回調 */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } //彈出菊花 progressDialog = new ProgressDialog(JSActivity.this); progressDialog.setTitle("提示"); progressDialog.setMessage("軟軟正在拼命加載……"); progressDialog.show(); } /** * 界面打開完畢的回調 */ @Override public void onPageFinished(WebView view, String url) { //隱藏菊花:不為空,正在顯示。才隱藏關閉 if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } } }); 讓進度條顯示頁面加載進度:
//設置進度條
//WebChromeClient與webViewClient的區(qū)別
//webViewClient處理偏界面的操作:打開新界面,界面打開,界面打開結束
//WebChromeClient處理偏js的操作
webView.setWebChromeClient(new WebChromeClient() {
/**
* 進度改變的回調
* WebView:就是本身
* newProgress:即將要顯示的進度
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (progressDialog != null && progressDialog.isShowing())
progressDialog.setMessage("軟軟正在拼命加載……" + newProgress + "%");
}
三、Android本地通過Java調用HTML頁面中的JavaScript方法
想要調用js方法那么就必須讓webView支持
//首先設置Webview支持JS代碼
webView.getSettings().setJavaScriptEnabled(true);
若調用的js方法沒有返回值,則直接可以調用mWebView.loadUrl("javascript:do()");其中do是js中的方法;若有返回值時我們可以調用mWebView.evaluateJavascript()方法:
@TargetApi(Build.VERSION_CODES.KITKAT)
public void onSum(View view){
webView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Toast.makeText(getApplicationContext(),
"相加結果:"+value, Toast.LENGTH_SHORT).show();
}
});
}
public void onDoing(View view){
String msg = "測試";
webView.loadUrl("javascript:showInfoFromJava('"+msg+"')");
}
對應的JS方法:
function sum(a,b){
return a+b;
}
function showInfoFromJava(){
document.getElementById("p").innerHTML="Java成功調的JS方法";
}
四、js調用Android本地Java方法
在Android4.2以上可以直接使用@JavascriptInterface注解來聲明,下面是在一個本地Java方法
public void addJavascriptInterface(Object object, String name);
- 1.object參數(shù):在object對象里面添加我們想要在JS里面調用的Android方法,下面的代碼中我們調用了showToast方法
- 2.name參數(shù):這里的name就是我們可以在JS里面調用的對象名稱,對應下面代碼中的JSTest
對應的JS代碼:
function jsJava(){
//調用java的方法,頂級對象,java方法
//可以直接訪問JSTest,這是因為JSTest掛載到js的window對象下了
JSTest.showToast("我是被JS執(zhí)行的Android代碼");
}
對應的Java代碼:
//java與js回調,自定義方法
//1.java調用js
//2.js調用java
//首先java暴露接口,供js調用
/**
* obj:暴露的要調用的對象
* interfaceName:對象的映射名稱 ,object的對象名,在js中可以直接調用
* 在html的js中:JSTest.showToast(msg)
* 可以直接訪問JSTest,這是因為JSTest掛載到js的window對象下了
*/
webView.addJavascriptInterface(new Object() {
//定義要調用的方法
//msg由js調用的時候傳遞
@JavascriptInterface
public void showToast(String msg) {
Toast.makeText(getApplicationContext(),
msg, Toast.LENGTH_SHORT).show();
}
}, "JSTest");
五、重繪alert、confirm和prompt的彈出效果,并把用戶具體的操作結果回調給JS
- alert彈窗:

- 重繪confirm彈窗:

- 重繪prompt彈窗:

- 具體代碼:
/**
* Webview加載html中有alert()執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
* JsResult:java將結果回傳到js中
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到這個,說明Java成功重寫了Js的Alert方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理確定按鈕,且通過jsresult傳遞,告訴js點擊的是確定按鈕
result.confirm();
}
});
builder.show();
//自己處理
result.cancel();
return true;
}
/**
* Webview加載html中有confirm執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
* JsResult:java將結果回傳到js中
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到這個,說明Java成功重寫了Js的Confirm方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理確定按鈕,且通過jsresult傳遞,告訴js點擊的是確定按鈕
result.confirm();
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理取消按鈕,且通過jsresult傳遞,告訴js點擊的是取消按鈕
result.cancel();
}
});
builder.show();
//自己處理
result.cancel();
return true;
}
/**
* Webview加載html中有prompt()執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
*defaultValue就是prompt的第二個參數(shù)值,輸入框的默認值
* JsPromptResult:java將結果重新回傳到js中
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
final JsPromptResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到這個,說明Java成功重寫了Js的Prompt方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
//添加一個EditText
final EditText editText = new EditText(JSActivity.this);
editText.setText(defaultValue);//這個就是prompt 輸入框的默認值
//添加到對話框
builder.setView(editText);
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//獲取edittext的新輸入的值
String newValue = editText.getText().toString().trim();
//處理確定按鈕了,且過jsresult傳遞,告訴js點擊的是確定按鈕(參數(shù)就是輸入框新輸入的值,我們需要回傳到js中)
result.confirm(newValue);
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理取消按鈕,且過jsresult傳遞,告訴js點擊的是取消按鈕
result.cancel();
}
});
builder.show();
//自己處理
result.cancel();
return true;
}
});
五、效果圖以及代碼布局文件:

界面上方是兩個按鈕,下方是一個webview控件,開啟頁面自動加載url,這里為了方便學習,
我已經寫了一個html文件放置在了Asset文件中,通過 file:///android_asset/test.html 來進行加載。
webview成功加載頁面后,會出現(xiàn)四個新的按鈕,點擊不同的按鈕,會產生不同的效果。
- asset文件夾下面的test.html文件
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
function jsAlert(){
var r = alert("我是Alert的提示框");
document.getElementById("p").innerHTML="Java成功調的JS的alert方法";
}
function jsConFirm(){
var r = confirm("我是ConFirm的彈出框");
if (r == true)
{
document.getElementById("p").innerHTML="用戶點擊了確認按鈕";
}else{
document.getElementById("p").innerHTML="用戶點擊了取消按鈕";
}
}
function jsPrompt(){
//第一個參數(shù)是提示
//第二個參數(shù)是默認值
var r = prompt("請輸入姓名:","小明同學");
if (r != null)
{
document.getElementById("p").innerHTML="用戶輸入的姓名為:"+r;
}else{
document.getElementById("p").innerHTML="用戶點擊了取消按鈕";
}
}
function jsJava(){
//調用java的方法,頂級對象,java方法
//可以直接訪問JSTest,這是因為JSTest掛載到js的window對象下了
JSTest.showToast("我是被JS執(zhí)行的Android代碼");
}
function sum(a,b){
return a+b;
}
function showInfoFromJava(){
document.getElementById("p").innerHTML="JS方法成功被Java調用";
}
</script>
</head>
<body>
<h3 id="p">界面成功初始化</h3>
<h5 onclick="jsAlert()">調用jsAlert方法</h5>
<input type="button" value="開啟Alert提示框" onclick="jsAlert()"/>
<h5 onclick="jsAlert()">調用jsConFirm方法</h5>
<input type="button" value="開啟ConFirm彈窗" onclick="jsConFirm()"/>
<h5 onclick="jsAlert()">調用jsPrompt方法</h5>
<input type="button" value="開啟Prompt彈窗" onclick="jsPrompt()"/>
<h5 onclick="jsAlert()">調用jsJava方法</h5>
<input type="button" value="調用Java方法" onclick="jsJava()"/>
</body>
</html>
- 具體的java代碼
public class JSActivity extends AppCompatActivity {
//assets下的文件的test.html所在的絕對路徑
private static final String DEFAULT_URL = "file:///android_asset/test.html";
private WebView webView;
private ProgressDialog progressDialog;//加載界面的菊花
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_js);
initView();
initWebView();
}
/**
* 初始化控件
*/
private void initView() {
webView = (WebView) findViewById(R.id.webView);
webView.loadUrl(DEFAULT_URL);
}
/**
* 初始化webview
*/
private void initWebView() {
//首先設置Webview支持JS代碼
webView.getSettings().setJavaScriptEnabled(true);
//Webview自己處理超鏈接(Webview的監(jiān)聽器非常多,封裝一個特殊的監(jiān)聽類來處理)
webView.setWebViewClient(new WebViewClient() {
/**
* 當打開超鏈接的時候,回調的方法
* WebView:自己本身webView
* url:即將打開的url
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//自己處理新的url
webView.loadUrl(url);
//true就是自己處理
return true;
}
//重寫頁面打開和結束的監(jiān)聽。打開時彈出菊花
/**
* 界面打開的回調
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
//彈出菊花
progressDialog = new ProgressDialog(JSActivity.this);
progressDialog.setTitle("提示");
progressDialog.setMessage("軟軟正在拼命加載……");
progressDialog.show();
}
/**
* 重寫頁面打開和結束的監(jiān)聽。打開時彈出菊花,關閉時隱藏菊花
* 界面打開完畢的回調
*/
@Override
public void onPageFinished(WebView view, String url) {
//隱藏菊花:不為空,正在顯示。才隱藏
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
});
//設置進度條
//WebChromeClient與webViewClient的區(qū)別
//webViewClient處理偏界面的操作:打開新界面,界面打開,界面打開結束
//WebChromeClient處理偏js的操作
webView.setWebChromeClient(new WebChromeClient() {
/**
* 進度改變的回調
* WebView:就是本身
* newProgress:即將要顯示的進度
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (progressDialog != null && progressDialog.isShowing())
progressDialog.setMessage("軟軟正在拼命加載……" + newProgress + "%");
}
/**
* 重寫alert、confirm和prompt的彈出效果,并把用戶操作的結果回調給JS
*/
/**
* Webview加載html中有alert()執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
* JsResult:java將結果回傳到js中
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到這個,說明Java成功重寫了Js的Alert方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理確定按鈕了,且通過jsresult傳遞,告訴js點擊的是確定按鈕
result.confirm();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
//防止用戶點擊對話框外圍,再次點擊按鈕頁面無響應
result.cancel();
}
});
builder.show();
//自己處理
return true;
}
/**
* Webview加載html中有confirm執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
* JsResult:java將結果回傳到js中
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:" +
"看到這個,說明Java成功重寫了Js的Confirm方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理確定按鈕了,且通過jsresult傳遞,告訴js點擊的是確定按鈕
result.confirm();
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理取消按鈕,且通過jsresult傳遞,告訴js點擊的是取消按鈕
result.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
//防止用戶點擊對話框外圍,再次點擊按鈕頁面無反應
result.cancel();
}
});
builder.show();
//自己處理
return true;
}
/**
* Webview加載html中有prompt()執(zhí)行的時候,會回調這個方法
* url:當前Webview顯示的url
* message:alert的參數(shù)值
*defaultValue就是prompt的第二個參數(shù)值,輸入框的默認值
* JsPromptResult:java將結果重新回傳到js中
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
final JsPromptResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到這個,說明Java成功重寫了Js的Prompt方法");
builder.setMessage(message);//這個message就是alert傳遞過來的值
//添加一個EditText
final EditText editText = new EditText(JSActivity.this);
editText.setText(defaultValue);//這個就是prompt 輸入框的默認值
//添加到對話框
builder.setView(editText);
builder.setPositiveButton("確定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//獲取edittext的新輸入的值
String newValue = editText.getText().toString().trim();
//處理確定按鈕了,且過jsresult傳遞,告訴js點擊的是確定按鈕(參數(shù)就是輸入框新輸入的值,我們需要回傳到js中)
result.confirm(newValue);
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//處理取消按鈕,且過jsresult傳遞,告訴js點擊的是取消按鈕
result.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
//防止用戶點擊對話框外圍,再次點擊按鈕頁面無反應
result.cancel();
}
});
builder.show();
//自己處理
return true;
}
});
//java與js回調,自定義方法
//1.java調用js
//2.js調用java
//首先java暴露接口,供js調用
/**
* obj:暴露的要調用的對象
* interfaceName:對象的映射名稱 ,object的對象名,在js中可以直接調用
* 在html的js中:JSTest.showToast(msg)
* 可以直接訪問JSTest,這是因為JSTest掛載到js的window對象下了
*/
webView.addJavascriptInterface(new Object() {
//定義要調用的方法
//msg由js調用的時候傳遞
@JavascriptInterface
public void showToast(String msg) {
Toast.makeText(getApplicationContext(),
msg, Toast.LENGTH_SHORT).show();
}
}, "JSTest");
}
@TargetApi(Build.VERSION_CODES.KITKAT)
public void onSum(View view){
webView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Toast.makeText(getApplicationContext(),
"相加結果:"+value, Toast.LENGTH_SHORT).show();
}
});
}
public void onDoing(View view){
String msg = "測試";
webView.loadUrl("javascript:showInfoFromJava('"+msg+"')");
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
//返回上一個頁
webView.goBack();
return ;
}
super.onBackPressed();
}
}
- 布局文件activity_js.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".JSoupHtmlActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onSum"
android:text="Java調用JS的Sum方法"
tools:ignore="OnClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onDoing"
android:text="Java調用JS的 showInfoFromJava 方法"
tools:ignore="OnClick" />
<WebView
android:id="@+id/webView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
代碼過程描述的廢話我就不多說了,注釋寫的算是比較仔細了,另外再強調兩點需要注意的地方:
1、不要忘記通過setJavaScriptEnabled(true)設置webview支持JS代碼
2、在使用addJavascriptInterface方法添加掛載對象時,要注意在Android4.2之后需要給對象方法加上@JavascriptInterface注解。
3、重繪alert、confirm和prompt的彈出效果之后,在對話框結束之后一定要調用result.confirm()或者result.cancel()兩個方法中的一個
,否則會出現(xiàn)后續(xù)再次點擊html頁面按鈕,頁面無響應的情況
項目地址:傳送門