年后请了几天假,今天第一天上班,这边博客本来应该年前写的,唉。。太懒了
先扯点没用的,总结下17年吧。
外包公司,项目倒是很多很杂,但总觉得论起细节、深度不够,但也学习到了很多的东西,而且庆幸的是和三个android同事一起进步、成长,成了很好的朋友。空闲时间写了一个安卓项目“AsYouLike”和一个小程序“我很随便的”,虽然不是特别难,但是自己一直以来的一个想法,总算实现了,还是有点骄傲的哈哈。自从我买了ipad之后,下班回去做好饭,就会打开虎牙看一起看栏目的士兵突击,一遍一遍,看不够似的,很多不经意的话,再重新听的时候,会有别样的感觉。想起几句,分享一下:我怕失落,怕丢失了始终怕不在乎。有容乃大,无欲则刚,容是别人,欲是自己。什么都要成功,搞不好就要失败。最初看的时候,里面会觉得里面的一句话“不抛弃,不放弃”,说的只是战友情,后来才明白,也是对自己追求理想的不抛弃不放弃。许三多跟成才说,人不能太舒服了,太舒服了会出问题,对于我们尤其适用。袁朗淘汰成才的时候,问成才,你的努力是为了什么,为了一个结果虚耗人生?虚耗人生?那怎样才算不虚耗人生呢?袁朗去部队看到独守营房的许三多时评价他不焦虑,这个时代应该很多人都会很焦虑吧,如此焦虑,如此功利。年底换到新的公司,是一家做自己产品的,今天刚刚知道,另一位同事要离职,我来这家公司其实是因为面试的时候觉得他比较牛才来的,但现在又不会太失落,总要承担一定的压力,总要靠自己啊,这不是什么坏事。
展望一下吧,18年希望自己能花更多的时间去学习,更有耐心,多跑步多锻炼身体,把两个项目优化一下,开发艺术探索看完,android提升的同时学习一下js和c。好了,先扯到这吧。
参考自:
- https://developer.android.google.cn/guide/webapps/webview.html#HandlingNavigation
- https://www.jianshu.com/p/3c94ae673e2a webview详解
- https://www.jianshu.com/p/345f4d8a5cfa 与js交互
- https://segmentfault.com/q/1010000004896607
- https://juejin.im/post/5a94fb046fb9a0635865a2d6
说到Android与js交互,避免不了的就是WebView。
想要打开网页时不使用默认的浏览器打开,而是使用当前的WebView,做如下设置即可:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient());
如果当你点击链接的时候你想做更多的控制,则需要重写shouldOverrideUrlLoading方法。shouldOverrideUrlLoading返回true表示你已经处理此次请求。返回false表示有webview自行处理,一般都是把此url加载出来。post请求并不会回调这个函数。
比如有一个下面的需求:有一个web页面上一个链接,点击后我们不希望webview直接转到链接的页面,而是起一个我们自己写的activity,此时就需要让shouldOverrideUrlLoading返回true,如下:
private WebViewClient webViewClient = new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (match(url)) {
foo1();
return true;
}
return false;
}
};
常用的工具类:
**1.WebSettings类:**对WebView进行配置和管理
较常用的设置:
WebSettings webSettings = webView.getSettings();
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//去掉缩放按钮
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
} else {
try {
Class webview = Class.forName("android.webkit.WebView");
Method method = webview.getMethod("getZoomButtonsController");
ZoomButtonsController zoom_controll = (ZoomButtonsController) method.invoke(this, true);
} catch (Exception e) {
e.printStackTrace();
}
}
//设置缓存模式
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
webSettings.setLoadsImagesAutomatically(true);//支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
**2.WebViewClient:**处理各种通知(开始加载/加载结束/加载失败等) & 请求事件
onPageStarted 开始加载 (显示加载进度条等)
onPageFinished 加载结束 (隐藏加载进度条等)
onReceivedError 加载失败(可在此处加载错误提示页面)
shouldOverrideUrlLoading (对要打开的链接做判断进而有不同的操作)
onReceivedSslError 任何HTTPS请求,遇到SSL错误时都会回调这个方法(在处理ssl错误时,遇到不信任的证书可以进行特殊处理,例如对域名进行判断,针对自己公司的域名“放行”,防止进入丑陋的错误证书页面。也可以与Chrome一样,弹出ssl证书疑问弹窗,给用户选择的余地。)
eg考拉做法:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (UrlUtils.isKaolaHost(getUrl())) {
handler.proceed();
} else {
super.onReceivedSslError(view, handler, error);
}
}
**3.WebChromeClient:**辅助 WebView 处理 Javascript 的对话框,加载进度,网站图标/标题等
onProgressChanged 加载进度
onReceivedTitle 网站标题
onJsAlert 当打开js弹窗时回调 返回true则消费掉此js弹窗,返回false则打开默认的提示框
onJsConfirm 当打开js确认框时回调
onJsPrompt 当打开js输入框时回调
android调用js代码
要调用的js代码如下:
<html>
<head>
<meta charset="utf-8">
<title>SilenceBurst</title>
<script>
function callJs(){
alert("Android调用了js的callJs方法");
}
</script>
</head>
</html>
final WebView webView = findViewById(R.id.web_view);
WebSettings settings = webView.getSettings();
//允许与js交互
settings.setJavaScriptEnabled(true);
//允许js弹窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
webView.loadUrl("file:///android_asset/call_js.html");
//通过设置WebChromeClient来处理js的alert函数
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog alertDialog = new AlertDialog.Builder(CallJsActivity.this)
.setTitle("调用Js")
.setMessage(message)
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.create();
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.show();
return true;
}
});
1.loadUrl
//调用javascript的callJs方法
webView.loadUrl("javascript:callJs()");
2.evaluateJavascript
webView.evaluateJavascript("javascript:callJs()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//value为js返回的结果
Toast.makeText(CallJsActivity.this, value, Toast.LENGTH_LONG).show();
}
});
总结: 使用WebView的evaluateJavascript方法调用js代码比loadUrl效率更高,易于获取返回值,更简洁(该方法执行不会使页面刷新,而loadUrl会),但evaluateJavascript方法Android4.4之后才可以使用
建议:
// Android版本变量
final int version = Build.VERSION.SDK_INT;
// 因为该方法在 Android 4.4 版本才可使用,所以使用时需进行版本判断
if (version < 18) {
mWebView.loadUrl("javascript:callJS()");
} else {
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
js调用android代码
1.addJavascriptInterface
定义一个与JS对象映射关系的Android类:AndroidtoJs
多次调用addJavascriptInterface之后的会把之前的覆盖
//继承自Object
public class AndroidToJs extends Object {
//被js调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
System.out.println(msg);
}
}
调用
//设置与js交互的权限
settings.setJavaScriptEnabled(true);
//将java对象映射到js对象
webView.addJavascriptInterface(new AndroidToJs(), "test");
webView.loadUrl("file:///android_asset/add_javascript_interface.html");
add_javascript_interface
<html>
<head>
<meta charset="utf-8">
<title>SilenceBurst</title>
<script>
function callAndroid(){
test.hello("js调用了Android的hello方法");
}
</script>
</head>
<body>
<button type="button" onclick="callAndroid()">点我调android方法</button>
</body>
</html>
需要注意的是,此方法存在严重的安全漏洞 你不知道的 Android WebView 使用漏洞
2.通过shouldOverrideUrlLoading回调拦截url
//设置与js交互的权限
settings.setJavaScriptEnabled(true);
//允许js弹窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
/*
*根据协议的参数判断是否是需要的url
* 一般通过scheme(协议格式)和authority(协议名)判断
*/
if ("js".equals(uri.getScheme()) && "webview".equals(uri.getAuthority())) {
//到此处根据即可做相关的操作,即js调用android完成,下面代码传返回值给js
Set<String> parameters = uri.getQueryParameterNames();
Iterator<String> iterator = parameters.iterator();
HashMap<String, String> map = new HashMap<>();
while (iterator.hasNext()) {
String next = iterator.next();
map.put(next, uri.getQueryParameter(next));
}
//hello方法做一个吐司提示js调用android成功
String result = hello(map);
//传递返回值给js 单引号要注意啊!!!
webView.loadUrl("javascript:getResult('" + result + "')");
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
//通过设置WebChromeClient来处理js的alert函数
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog alertDialog = new AlertDialog.Builder(mContext)
.setTitle("调用Js")
.setMessage(message)
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.create();
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.show();
return true;
}
});
webView.loadUrl("file:///android_asset/should_override_url_loading.html");
should_override_url_loading
<html>
<head>
<meta charset="utf-8">
<title>SilenceBurst</title>
<script>
function callAndroid(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
function getResult(result){
alert("result is"+result);
}
</script>
</head>
<body>
<button type="button" onclick="callAndroid()">点我调android方法</button>
</body>
</html>
3.通过 WebChromeClient 的onJsAlert、onJsConfirm、onJsPrompt方法回调拦截JS对话框alert、confirm、prompt 消息
(类似shouldOverrideUrlLoading拦截)
//设置与js交互的权限
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Uri uri = Uri.parse(message);
if ("js".equals(uri.getScheme()) && "webview".equals(uri.getAuthority())) {
//此处即为js调用android时,android代码里判断拦截掉js弹出的输入框,并传入值到js提示框
//参数:代表消息框的返回值(输入值)
result.confirm("js 调用android的方法成功了");
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
webView.loadUrl("file:///android_asset/on_js_prompt.html");
on_js_prompt
<html>
<head>
<meta charset="utf-8">
<title>SilenceBurst</title>
<script>
function clickPrompt(){
var result = prompt("js://webview?arg1=111&arg2=222");
alert("webview"+result);
}
</script>
</head>
<body>
<button type="button" onclick="clickPrompt()">点我调android方法</button>
</body>
</html>