在android內打開一個網頁的時候,有時咱們會要求與網頁有一些交互。而這些交互是在基於javaScript的基礎上。那麼咱們來學習一下android如何與網頁進行JS交互。完整代碼以下:javascript
import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.FragmentActivity; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.JavascriptInterface; import android.webkit.URLUtil; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** * 軟件內通用打開網頁的容器頁面 * * @author ZRP */ public class CommonWebActivity extends FragmentActivity { private ProgressBar progress_bar; protected CustomFrameLayout customFrameLayout; protected TextView titleText, errorTxt; protected View refresh;// 刷新按鈕 protected WebView webView; protected String url = "";// 網址url protected String param = "";// 交互參數,如json字符串 protected WebChromeClient chromeClient = new WebChromeClient() { public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { progress_bar.setVisibility(View.GONE); // 判斷有無網絡 if (!NetUtils.isAvailable(CommonWebActivity.this)) { customFrameLayout.show(R.id.common_net_error); refresh.setVisibility(View.VISIBLE); refresh.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { webView.loadUrl(url); } }); } else { // 判斷網絡請求網址是否有效 if (!URLUtil.isValidUrl(url)) { customFrameLayout.show(R.id.common_net_error); errorTxt.setText("無效網址"); } else { customFrameLayout.show(R.id.common_web); } } } else { progress_bar.setVisibility(View.VISIBLE); progress_bar.setProgress(newProgress); } } // 獲取到url打開頁面的標題 public void onReceivedTitle(WebView view, String title) { titleText.setText(title); } // js交互提示 public boolean onJsAlert(WebView view, String url, String message, android.webkit.JsResult result) { return super.onJsAlert(view, url, message, result); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); url = getIntent().getStringExtra("url"); param = getIntent().getStringExtra("param"); setContentView(R.layout.common_web_activity); initView(); } @SuppressLint("SetJavaScriptEnabled") private void initView() { findViewById(R.id.back).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } }); titleText = (TextView) findViewById(R.id.title); progress_bar = (ProgressBar) findViewById(R.id.progress_bar); customFrameLayout = (CustomFrameLayout) findViewById(R.id.web_fram); customFrameLayout.setList(new int[]{R.id.common_web, R.id.common_net_error}); refresh = findViewById(R.id.error_btn); errorTxt = (TextView) findViewById(R.id.error_txt); webView = (WebView) findViewById(R.id.common_web); webView.getSettings().setDefaultTextEncodingName("utf-8"); webView.getSettings().setJavaScriptEnabled(true); synCookies();//格式化寫入cookie,需寫在setJavaScriptEnabled以後 webView.setWebChromeClient(chromeClient); webView.setWebViewClient(new WebViewClient() {// 讓webView內的連接在當前頁打開,不調用系統瀏覽器 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); webView.addJavascriptInterface(new JavaScriptInterface(), "zrp"); webView.loadUrl(url); new Handler().postDelayed(new Runnable() {//異步傳本地數據給網頁 @Override public void run() { onNotifyListener(param); } }, 1000); } /** * CookieManager會將這個Cookie存入該應用程序/data/data/databases/目錄下的webviewCookiesChromium.db數據庫的cookies表中 * 須要在當前用戶退出登陸的時候進行清除 */ private void synCookies() { String[] split = App.cookie.split(";"); for (int i = 0; i < split.length; i++) { CookieSyncManager.createInstance(CommonWebActivity.this); CookieManager.getInstance().setCookie(url, split[i]); CookieSyncManager.getInstance().sync(); } } /** * 回調網頁中的腳本接口。 * * @param notify 傳給網頁的通知內容。 */ public void onNotifyListener(String notify) { if (webView != null) { webView.loadUrl("javascript:test('" + notify + "')"); } } /** * android js交互實現: * - window.zrp.command("");//在網頁的方法中添加該代碼獲取android內容 * - webView.loadUrl("javascript:test('param')");//android給網頁傳值,須異步執行 */ public class JavaScriptInterface { @JavascriptInterface public void command(String jsonString) { if (TextUtils.isEmpty(jsonString)) { return; } //根據網頁交互回傳的json串進行操做 Toast.makeText(CommonWebActivity.this, jsonString, Toast.LENGTH_LONG).show(); } } }
切記:須要設置聯網權限與網絡狀態獲取權限!html
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
其中CustomFrameLayout爲界面切換控件,分別在無網絡,網址錯誤等狀況下進行提示:java
import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; /** * 用於狀態切換的佈局,只顯示1個狀態 */ public class CustomFrameLayout extends FrameLayout { private int[] list; public CustomFrameLayout(Context context) { super(context); initView(); } public CustomFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public CustomFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } /** * 設置子面板id數組 * * @param list */ public void setList(int[] list) { this.list = list; show(0); } /** * 顯示某個面板 * * @param id */ public void show(int id) { if (list == null) { for (int i = 0; i < getChildCount(); ++i) { View view = getChildAt(i); if (id == view.getId()) { view.setVisibility(View.VISIBLE); } else { view.setVisibility(View.GONE); } } return; } for (int aList : list) { View item = findViewById(aList); if (item == null) { continue; } if (aList == id) { item.setVisibility(View.VISIBLE); } else { item.setVisibility(View.GONE); } } } /** * 隱藏全部面板 */ public void GoneAll() { if (list == null) { for (int i = 0; i < getChildCount(); ++i) { View view = getChildAt(i); view.setVisibility(View.GONE); } return; } for (int aList : list) { View item = findViewById(aList); if (item == null) { continue; } item.setVisibility(View.GONE); } } /** * 切換。 * * @param index 佈局在fram中的index */ public void showOfIndex(int index) { for (int i = 0; i < getChildCount(); ++i) { View view = getChildAt(i); if (index == i) { view.setVisibility(View.VISIBLE); } else { view.setVisibility(View.GONE); } } } public void initView() { } }
界面佈局爲:android
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="50dp" android:gravity="center" android:orientation="horizontal" android:paddingLeft="10dp"> <ImageView android:layout_width="15dp" android:layout_height="15dp" android:src="@drawable/ic_back" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="返回" android:textSize="15sp" /> </LinearLayout> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:text="加載中" android:textSize="15sp" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_below="@+id/title" android:background="#666666" /> <ProgressBar android:id="@+id/progress_bar" style="@style/update_progress" android:layout_width="match_parent" android:layout_height="3dp" android:layout_marginTop="51dp" android:max="100" /> <com.zrp.webviewdemo.web.CustomFrameLayout android:id="@+id/web_fram" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="51dp"> <WebView android:id="@+id/common_web" android:layout_width="match_parent" android:layout_height="match_parent" /> <include layout="@layout/common_net_error" /> </com.zrp.webviewdemo.web.CustomFrameLayout> </RelativeLayout>
JavaScript是經過調用android中指定的方法名來進行值傳遞的,因此android部分的代碼不能進行混淆,須要添加keep語句:-keep class com.zrp.webviewdemo.web.CommonWebActivity$JavaScriptInterface {*;}
git
好了,這時候咱們來添加一些數據進行測試:github
用上面的方法爲web頁面中的param賦值,在asset文件夾中放入要進行測試的html文件,url賦值爲:url = "file:///android_asset/html.html";
如上html.html文件源碼爲:web
<!DOCTYPE html> <html> <head> <title>測試容器調用</title> </head> <body> <div class="main"> <button onclick="test2()">獲取網頁的值</button> </div> <script type="text/javascript"> function test(json){ alert(json); } function test2(){ alert("網頁傳值給android"); window.zrp.command("{'type':1,'text':'hello boy'}"); } </script> </body> </html>
android傳值給web:可經過在java代碼中異步執行webView.loadUrl("javascript:test("這是須要傳給網頁的數據")");
來調用網頁代碼中的test()方法傳值給web端。web傳值給android:在網頁代碼中能夠看到button的點擊事件添加了
window.zrp.command("{'type':1,'text':'hello boy'}");
來回傳一個json串給java頁面,即js經過以上的方法調用了java的方法。chrome
若是webView展現頁面的時候須要給網頁傳遞cookie,即各類登陸信息,則須要在調用webView.loadUrl(url)以前一句調用以下方法:注意!多個cookie值必須屢次進行setCookie!數據庫
設置cookie方法一:json
/** * CookieManager會將這個Cookie存入該應用程序/data/data/databases/目錄下的webviewCookiesChromium.db數據庫的cookies表中 * 須要在當前用戶退出登陸的時候進行清除 */ private void synCookies() { CookieSyncManager.createInstance(this); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.removeSessionCookie(); String[] split = App.cookie.split(";"); for (int i = 0; i < split.length; i++) { cookieManager.setCookie(url, split[i]); } CookieSyncManager.getInstance().sync(); }
在當前用戶退出登陸的時候清除掉保存的cookie信息。
/** * 清除當前用戶存儲到cookies表中的全部數據 */ private void removeCookie() { CookieSyncManager.createInstance(this); CookieManager.getInstance().removeAllCookie(); CookieSyncManager.getInstance().sync(); }
注意:在調用設置Cookie以後不能再設置以下的這類屬性,不然設置cookie無效。
webView.getSettings().setBuiltInZoomControls(true); webView.getSettings().setJavaScriptEnabled(true);
可是!在如上設置cookie以後,每次設置cookie的時候都有延時,要是點擊頻率較快的時候,會出現cookie沒有被設置進去的問題!
設置方法二:
通過一番查找,找到以下兩個解決辦法:
java.lang.IllegalStateException: CookieSyncManager::createInstance() needs to be called before CookieSyncManager::getInstance()
,按照提示加上以下代碼以後正常。/** * CookieManager會將這個Cookie存入該應用程序/data/data/databases/目錄下的webviewCookiesChromium.db數據庫的cookies表中 * 須要在當前用戶退出登陸的時候進行清除 */ private void synCookies() { String[] split = App.cookie.split(";"); for (int i = 0; i < split.length; i++) { CookieSyncManager.createInstance(CommonWebActivity.this); CookieManager.getInstance().setCookie(url, split[i]); CookieSyncManager.getInstance().sync(); } }
同時,在退出登陸的時候清除cookie。
CookieSyncManager.createInstance(this); CookieManager.getInstance().removeAllCookie(); CookieSyncManager.getInstance().sync();
清除當前用戶存儲到cookies表中的全部數據。
這部分網上也有不少博客與筆記。此處僅做記錄。
添加步驟以下:
AndroidMainfext.xml
中你須要接收跳轉信息的Activity
標籤中添加以下代碼,則該Activity
能夠接收和處理網頁中相似於zrp://zrp_test.net/
的連接:<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="zrp_test.net" android:scheme="zrp" /> </intent-filter>
activity
中添加數據接收的代碼:private void initScheme(Intent intent) { Log.d(TAG, "initScheme: ---DataString--->" + intent.getDataString()); Uri uri = intent.getData(); if (uri != null && "zrp_test.net".equals(uri.getHost())) { Log.d(TAG, "initScheme: ---data--->" + uri.getQueryParameter("data")); } }
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> <title>test</title> </head> <body> <p>從瀏覽器打開應用的測試網址。</p> <script LANGUAGE='javascript'> var str = {"uid":123456,"nickname":"sex lady","sex":"female"}; window.location = "zrp://zrp_test.net?data="+JSON.stringify(str); </script> </body> </html>
該網頁文件我在雲存儲放了一份,你們能夠經過訪問這個網址來進行測試:http://sanchi.56ef923d5f32c.d01.nanoyun.com/blog_test/app_browser_test.html
D/MainActivity: initScheme: ---DataString--->zrp://zrp_test.net?data={"uid":123456,"nickname":"sex lady","sex":"female"} D/MainActivity: initScheme: ---host--->zrp_test.net D/MainActivity: initScheme: ---data--->{"uid":123456,"nickname":"sex lady","sex":"female"}
一切OK。
須要源碼的同窗看這裏