在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。
须要源码的同窗看这里