安卓入门之WebView
过去的一年大大小小写了不少移动端项目(基本都是微信公众号),再加上之前跟Android的同学咨询过Hybird
开发,因此对于WebView
或多或少有一些了解。恰好最近的项目,需要跟移动端的同事协作处理一个分享页面,所以一并整理出来。
参考:
基础
webview主要就是用来在应用中加载和显示网页。一看webview的名字里面居然挂了个view
后缀,没错,这也是一个组件,在布局里面直接调用即可
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/webview_1">
</WebView>
跟网页中插入一个iframe的感觉挺像的,为了体验一般会将整个组件完全填充在屏幕上(顶部栏不算),除此之外,并不需要在额外设置其他样式(因为整个页面的样式都交给CSS控制了)。
骨架是有了,但我们貌似还没有指定加载网页的URL呢?没错,接下来的工作就是在加载网页了,但是在此之前,我们还可以对webview进行一系列设置
// 获取webview对象
webview = (WebView) findViewById(R.id.webview_1);
WebSettings webSettings = webview.getSettings();
// 禁止缓存
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
// 允许javascript
webSettings.setJavaScriptEnabled(true);
// 处理webview的各种事件和通知
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
view.loadUrl(url);
// 这里主要告知提供使用webview而不是浏览器打开网页
return true;
}
});
// 允许弹窗等操作
webview.setWebChromeClient(new WebChromeClient());
// 加载网页
webview.loadUrl("http://www.shymean.com/webview.html");
观察上面这段代码,除了webview对象之外,还有三个类,他们分别是:
WebSettings
,主要用来设置webview对象,包括缓存,JavaScript,缩放和布局等设置接口,这里遇见的第一个坑就是网页刷新页面不发生改变,被缓存坑了无数次的我马上想到了禁止缓存(是不是很机智[/斜眼])WebViewClient
,处理网络请求时的各种事件,这里需要注意的是为了防止应用使用系统浏览器打开网页,需要覆盖shouldOverrideUrlLoading
方法setWebChromeClient
,在调试的时候遇见的一个坑,浏览器的弹出框在webview中不生效!!后来发现原来是没有设置WebChromeClient
的缘故~
实际上这三个类提供了非常多的接口供我们使用(现在我感觉微信公众号就是一个非常庞大的webview),这里先不展开了,有需要再去查文档。
最后,打开模拟器看看效果,卡擦,什么鬼?原来还需要在manifest中配置网络权限,这茬千万比忘记了
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Hybird
关于Hybird这里有一篇不错的文章:浅谈Hybrid技术的设计与实现。我觉得webview一个非常吸引人的地方就是整个界面甚至业务逻辑都可以交给web前端处理,但是又为JS提供调用原生的系统方法的途径。
讲道理,最近两三周一直在搞安卓,个人认为搭页面用CSS比写Android快N倍(还不算上用SCSS,livereload这些了),但是论体验肯定是APP要好得多。哈这里抛开布局不谈,还是来了解JS与原生之间是如何实现交互的。
JS调用原生方法
最近的工作需求是完成一个分享页面,需要通过原生应用将微信QQ等应用分享的权限下放到webview中,那么问题来了,原生的方法如何才能够被webview页面上的JS调用呢?
首先定义方法:
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c){
mContext = c;
}
@JavascriptInterface
// 这里为了省事就只是调用个原生的提示框了
public void showToast(String toast){
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
然后向webview中注入定义的方法,实际上就是向webview的宿主对象添加了一个全局变量
// ...省略相关配置
webview.addJavascriptInterface(new WebAppInterface(this), "Android"); // 这个名字是自定义的,当然得跟前端商量好
webview.loadUrl("http://www.shymean.com/webview.html");
接着,我们就可以在这个webview.html
页面中通过全局变量Android
来访问对应的方法showToast
<script type="text/javascript">
console.log(Android);
Android.showToast("hello Android");
</script>
宾果~现在在模拟器中打开,就可以看见网页上触发了原生的提示框效果,这对于整个应用的设计统一是很有帮助的。
上面有个需要注意的地方:在Android API 17后的JS接口需要使用@JavascriptInterface
注解,而webview中的JS代码只能调用声明时使用该注解的Java方法。
原生调用JS方法
loadUrl
前面通过loadUrl
为webview加载页面,实际上也可以通过它来调用JS函数。稍微修改一下布局,添加一个原生的按钮,然后注册点击事件处理方法
public void callJS(View view){
webview.post(new Runnable() {
@Override
public void run() {
webview.loadUrl("javascript:alert('Hello from Android');");
}
});
}
这里使用的是webview.post
传入了一个任务,然后通过loadUrl
执行了一个伪协议,在《DOM编程艺术》中了解到,通过伪链接javascript:;
的方法也可以执行JS代码,这就相当于为原生Android代码提供了调用JS代码的途径。
这里只是简单调用了alert方法,实际上可以调用任何已被注册的JS函数,相当于达到了我们通过原生调用JS代码的目的。
这里有一个小疑问:看见有的博客上介绍到这里必须通过post来执行loadUrl,然而实际上经过测试我发现直接在callJS函数中使用loadUrl也是可以执行的。查看post方法的源码发现:
The runnable will be run on the user interface thread.
实际上任务也是跑在UI线程的,可能是前面那些博客的Android版本问题,毕竟现在都2017年了,这个问题就不深究了。
另外还有介绍到使用loadUrl
调用伪协议会刷新页面,实际上我也没有发现这个问题,可能是测试的不够全面,先挖个坑吧。
最后的一个问题是如何获取js函数的返回值呢?查这个问题的时候了解到了evaluateJavascript
这个接口。
evaluateJavascript 在Android 4.4之后,我们还可以使用evaluateJavascript
来调用js方法,最主要的,该方法可以很方便的获取函数返回值
public void callJS(View view){
webview.evaluateJavascript("javascript:responseFromJS();", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.i(TAG, value);
}
});
}
对应的Javascript代码
function responseFromJS(){
alert("recive");
return "value from js";
}
这个返回值ValueCallback
也大有讲究,毕竟js跟java是两门完全不一样的语言,在js函数中返回的闭包函数这里会被处理成null哦~不过一般原生和js之间的通信应该都是传基础数据而已吧,如果需要传递函数,一个折衷的办法是两端规定方法的索引值(或者其他的能表明某个方法的字段),在JavaScript返回这个索引值,然后在安卓这边通过判断这个值调用对应的原生方法。
JsBridge
原生和JS的代码交互产生了一个JsBridge
的概念,归根结底就是原生和JS的代码交互问题:
- 原生调用JavaScript代码
- JavaScript调用原生代码,实际上,通常更多地应该是JS借助原生接口实现HTML页面的更多功能
- 参数和返回值处理
这个暂时就没有深入了,首先应该掌握前面俩小结介绍的东西。不同的业务下JsBridge
应该也不尽相同,而在Hybird开发中,应该要写大量的JsBridge吧。
这么回想起来,微信公众号中的JSSDK
也算作是一个JsBridge了吗。PS:为啥要叫做"JS"Birdge呢?
调试
开发WebView页面一个非常蛋疼的问题就是:这特么该怎么调试啊?之前做微信公众号项目的时候,好歹还有微信开发者工具(虽然被我吐槽了无数次),现在怎么办?
起初只能有最原始的办法:alert
大法。不过这个调试起来真的太坑爹了,别的不说,alert的遗留问题:查看对象啥的都十分麻烦。
目前的的调试方法是模拟器结合Chrome进行调试,这个还是比较人道的:
- 打开模拟器,进入一个webview页面(或者打开浏览器随便进个页面)
- 打开chrome浏览器(我现在使用的版本是58.0.3029.110),地址栏输入
chrome://inspect/#devices
- 正常情况下可以在
Remote Target
这里发现我们打开的页面,然后点击下面的inspect
按钮,会弹出一个Developer Tools
窗口,这个过程可能需要加载一段时间 - 现在就可以像调试PC端页面一样的来调试webview页面了,查看数据啥的也简单多了。但是需要注意的是有的操作(比如调用原生Toast)这些操作在开发者工具上面是看不见效果的
- 哈哈,上面说的都是模拟器下的调试,使用真机的话,需要使用USB连接电脑和手机,然后再手机勾选开发者选项啥的,后面就跟这些操作差不多了(PS:真机调试兼容是多么苦逼的事情啊~~)
小结
了解了webview和基本的Android之后,用前端的东西就感觉可以直接开发出一个“伪”APP了。不过这里只介绍了最基本的使用方法,具体的坑还得在实践中一步一步去填。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。