以前也用过webview,不过基本都是展示一些静态的页面,就直接对着API怼就行,现在由于某一块功能太过于繁杂,用本地的方法去开发的话就会太过于麻烦,不是做不到而是没必要,所以也就是时候好好尝试下混合开发。
这过程中会涉及到和前端JS的交互,如果没接触过前端的至少应该也要先去了解下前端,其实我对前端也只是略懂略懂。
一.开发前的准备
既然要同时开发Android原生代码和前端代码,那肯定要做些准备,android端就用android studio这是肯定的,我前端打算写vue项目(我也不会angular),我是用webstorm去开发,然后肯定也搭好了Vue的环境,前端这一块用什么写无所谓,思想和操作我觉得其实是大概相同的。
1.手机浏览本地网页
因为是开发的过程,我们不可能每次写完前端的代码都要部署一次,所以第一件要做的事就是在手机端能正常运行到前端的代码。
其实这个一般不用说的,鉴于我是第一次嘛,就顺便记录一下,那怎么做呢,那这用vue举例。
(1)首先要先让手机和电脑都同时连在一个局域网内
(2)配置vue内部的环境(这个我只懂配,不懂原理,其它框架肯定也一样)。把host和端口都配置下。
(3)查看本地电脑的当前ip
我是windows的,在命令行输ipconfig查看IPv4地址,这个没啥好解释的
然后测试一下能不能在本地用ip来打开你写的网页
(4)手机输入网址
电脑是能正常的打开了,然后试试在手机上能不能打开这个网址
看到这样就说明手机能正常展示电脑上的我们写的网页,这样就完成了第一步。
二.对接Android与前端
能正常在手机展示前端网页后,可以对接下Android与前端,其实就是使用webview,webview就是一个内置的浏览器
WebView webview = (WebView) findViewById(R.id.webview); webview.getSettings().setJavaScriptEnabled(true); webview.getSettings().setDomStorageEnabled(true); webview.loadUrl("http://192.168.1.119:8090");
loadUrl就是展示网页这个我想应该不用多解释,把展示的地址设置成我们前端网页的地址看看效果
嗯,能正常的展示出页面,这样我们就能在不部署前端代码的情况下进行混合开发。
这里说一下,这里我用Vue是响应式的就很方便,我直接改前端的代码,不用刷新WebView里面的页面内容也会直接进行改变,这样只要我们不改Android原生的代码的话就不用重新run APP,但是这基本是不可能的,既然是混合开发,一般对接的功能两边都要改。
三.WebView
能正常显示后我们现在就应该把中心放到WebView上面,WebView现在就相当于两个端的一个桥梁。
1.探究两端代码的执行顺序
其实这里我想问一个问题:
WebView属于多进程吗?
我找了很多资料都没有看到有人提到过这个问题,如果有大神知道请告诉我,如果是在别的浏览器打开肯定是不同进程,但是浏览在本地呢算不算不同进程。
首先这个开发我不知道是怎样的一个流程,是我调用loadUrl方法的时候就开始加载前端页面了吗?
带着这个疑问我打算先去探究一下本地代码和前端代码的执行顺序,我在vue页面的生命周期中打印100个数字的循环,在本地loadUrl方法之后也打印100个数字的循环
前端:
本地
框中的方法是能打印前端的日记到androidstudio的logcat上。这样做,是不是说明如果两个数字交叉打印的话就证明至少它们的执行不在一个线程上,然后打印发现结果是先打印本地的0到99,再打印前端的0到99(数字太多我就不贴了),在onResume打印也是这个结果。
那是不是说明,等webview显示出来之后才去加载前端的页面?
很可惜的是在AS里面看不了webview的源码,对此只能到网上去找资料。
我找到别人说webview是多由两个线程组成,他初始化顺序是
出自https://tech.meituan.com/WebViewPerf.html
那说明我们之前答应的数字有顺序是因为webview的初始化时间太长了而不是在同个进程。
四.webview的操作
大概了解两边的执行顺序之后就好弄的,因为如果你不知道执行顺序的话,有可能你在本地执行一个js的方法,你不知道是在什么时候执行,而大概知道整个过程之后就很好把控了。
看看webview有哪些操作,webview有两个比较重要的类
WebChromeClient和WebViewClient
关于这两个类,如果设置的话很多文章都写的是匿名内部类,我建议最好是继承去实现,这样扩展性会比较好。
第一个类主要跟一些网页的弹框之类的有关,第二个类主要跟一些流程有关。
1.Android调用js的方法
API19以上可以用这个方法
webview.evaluateJavascript("javascript:changename()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 Log.v("Native",value); } });
19以下可以用
webview.loadUrl("javascript:changename()");
关键你要知道在什么地方调用js的方法,调用时你要保证页面已经加载完成了,不然你调用也没效果,那么要如何确定页面已经加载完成,这就需要上面说的WebViewClient
如果你想在加载页面后马上调用js的某个方法,你可以这样写
webview.setWebViewClient(new WebViewClient(){ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); // 开始加载页面时 } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // 加载结束 webview.evaluateJavascript("javascript:changename()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 Log.v("Native",value); } }); } });
还有一点,如果使用框架的话,是没法直接调用框架里面写的方法的,我用vue就是这样,网上查了,说要把前端的方法给window,那我这里也贴下我前端的代码
我这里把方法给window,这样android端就能正常的调用vue里面的方法。
2.js调用Android方法
其实这说法按网上的文章的写法,并不是让js直接调用Android的方法,而是在Android定义一个对象,让这个对象映射到js里面的一个对象,再让这个对象去调用方法。
(1)android端
先定义一个要映射给js的对象,要让js调用的方法要加上 @JavascriptInterface
public class WebTest { private Context context; public WebTest(Context context){ this.context = context; } @JavascriptInterface public void show(String s) { Toast.makeText(context,s,Toast.LENGTH_SHORT).show(); } }
然后设置给前端,将WebTest对象映射到前端的mandroid对象
webview.addJavascriptInterface(new WebTest(this),"mandroid");
(2)前端
我先写个点击事件
在这个事件中调用android的方法
show(){ mandroid.show("js调用了android中的方法"); }
这样就能在js中调用android的代码。
五.总结
先讲这么多吧,主要是说说怎么在本地共同开发android和前端,WebChromeClient的onConsoleMessage可以打印前端的日记,这样的话调试的时候也基本没啥问题。
第二个就是简单的讲了Android端和Js的调用,其实只要能通信的话就很方便了,就像我之前写的进程间的通信,也是拿到代理对象后通信,这里差不多的。
我并没用详细去写太多关于webview的内容,因为如果有人想看API这些总结的话别人写得比我好,我就没必要再写了:
https://mp.weixin.qq.com/s/Rn7s31nxxY3vWnFRyjyjiA这里有很多webview的相关方法。
然后就是如果你会了这些其实还是没法做好混合开发,因为体验的效果其实并不是很好,等熟悉webview的使用后可以看看美团的这篇文章:
https://tech.meituan.com/WebViewPerf.html
还有一些其它的特殊功能没详细讲,比如说定位,传图片这些,因为我也没怎么开发过这些功能,不过我敢保证肯定会有坑,剩下的等碰到再写吧。
作者:键盘上的麒麟臂
链接:https://www.jianshu.com/p/3d9a93c9fea2