优化Hybrid app的原生支持

来源:互联网 发布:免备案域名 编辑:程序博客网 时间:2024/05/16 01:14

最近老板组织了一下技术分享,我分享了两点东西,最后老板的点评还是挺有意思的,多点图,尽量深入浅出,吃透了才能深入浅出,说得云里雾里的,多半是自己还没理解到位。我整理成博客吧。

什么是Hybrid app?

(图片来自https://zhuanlan.zhihu.com/p/21387961 :侵立删)

(摘自百度百科:Hybrid App同时使用网页语言与程序语言开发,通过应用商店区分移动操作系统分发,用户需要安装使用的移动应用)

为什么要用Hybrid app?

牺牲一定的交互体验,交换开发的时间成本(我们之前用的一直是android原生的东西,后来用户需求很强烈,加了IOS(swift)的东西,因为我要写两个平台,很麻烦。所以加入了web的东西来跨平台。归根结底还是移动端开发。)

现在我们是怎么做的?

+ HTML5 (ASP.NET MVC4) 

+ webkit ( android:webview , IOS :UIWebview[IOS7] , WKWebview[IOS8+])


回到题目,如何优化原生支持?

有几个点:

1、导航栏要不要做成web的?

问题图中的navigation是原生还是web的。很不幸这么基础的问题我们也遇到过,也实践过。答案是做成原生的比较好。因为web的loading可能会比较久,如果挂了,就很糟糕了。我们做完一段时间后,市面上的hybrid app才多了起来,基本上也是用这种方式。

2、WEB上面有几个页面,如何加载,如何返回?

用android举例,以前我们的页面的概念是基于Activity,每跳转一个页面,就新开一个Activity,好处当然是能利用android框架的东西,保存页面信息。那在WEB上有没有这个概念?一开始我也是直接用webview的堆栈信息,通过拦截“返回”按钮,调用webview里面的返回,直到webview中history堆栈的为空,则退出。

后来遇到几个问题:

i、(IOS、android)列表进去,返回上一页,页面必须重新加载,用户必须等待页面加载完才能继续查看,而且如果是页面中有很长的列表,那么用户就要重新滑动过去,体验很差。

ii、android的对html5的location.replace(url)支持缺失,对页面需要更新连接等操作造成了很大的困扰。

基于这两个原因,我拦截了新打开的连接,手动识别连接地址是否发生变化,发生变化了就用新的activity/fragment /controller 打开。

具体怎么拦截呢?

android

iWebView.setWebViewClient(new MyWebViewClient());private class MyWebViewClient extends WebViewClient{//why Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB //http://tanghaibo001.blog.163.com/blog/static/9068612020121023113646644/@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {//lastHttpUrlif(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB&& (url.startsWith("http:") || url.startsWith("https:"))){lastHttpUrl = url;}if(!onShouldOverrideUrlLoading(view,url)){if(url.startsWith("http:") || url.startsWith("https:")){view.loadUrl(url);}}return true;}@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon){super.onPageStarted(view, url, favicon);if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB&& (url.startsWith("http:") || url.startsWith("https:"))){if(!url.equals(lastHttpUrl)){if(onShouldOverrideUrlLoading(view,url)){view.stopLoading();if(view.canGoBack()){view.goBack();}return;}}}if(url.startsWith("http:") || url.startsWith("https:")){showLoading();}if(delegate != null){delegate.onTitleButtonChange("", "");}}@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);if(isActivityFinishinig()){ return; }if(view != null){runJs(UrlProtocol.ValidProtocol);if(!view.getSettings().getLoadsImagesAutomatically()) {view.getSettings().setLoadsImagesAutomatically(true);}}onPageFinish(view);stopLoading();} @Overridepublic void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {super.onReceivedError(view, errorCode, description, failingUrl);onError(ConstVal.WebError.net);}@Overridepublic void onLoadResource(WebView view, String url) {super.onLoadResource(view, url);runJs(UrlProtocol.ValidProtocol);}} private boolean onShouldOverrideUrlLoading(WebView view, String url){if(HandleUrlByDelegate(url)){ return true;}if(HandleUrlByProtocol(view,url)){return true;}if(HandleUrlByReplace(url)){return true;}return false;}

如上文代码可见,就是在onShouldOverrideUrlLoading中判断是不是activity中扩展的回掉要用到?是不是我们自定义的那些协议?最后一个就是要不要跳转,要的话就打开新页面跳转。

这里有个小坑,就是onShouldOverrideUrlLoading不一定时时有效,具体原因已经给连接了,怎么处理也在代码中有体现了。

IOS:

上一篇博客有给具体实现:

http://blog.csdn.net/yeshennet/article/details/52413517

是在 webViewDidStartLoad 中做的处理,代码都很类似。


3、web和原生如何进行双向交流?


原生到web:

这个是最简单的:

//androidpublic void runJs(String action){if(iWebView != null){iWebView.loadUrl("javascript:"+action);;}}//IOSfunc runJs(action:String?){if #available(iOS 8.0, *) {if let wkweb = view as? WKWebView {wkweb.runJs(action)}} else {if let uiweb = view as? UIWebView{uiweb.runJs(action)}}}
其中,action就是要执行的javascript代码。


web到原生:

有两种方式:

i、原生提供接口给web回掉

举个例子:

//android:webview.addJavascriptInterface(new JsObject(), "nativeJava");public class JsObject implements nativeJava{@JavascriptInterfacepublic void clearHistory(){//let it empty}}//javascriptwindow.nativeJava.clearHistory()
IOS也有提供类似的方法:https://developer.apple.com/reference/webkit/wkscriptmessagehandler

ii、通过2中的连接拦截(onShouldOverrideUrlLoading / webViewDidStartLoad )进行处理。

我用了这种方式,因为可以自定义协议,达到原生和html5的解耦。

所谓一流的公司定义协议,二流的公司遵守协议,三流的公司不懂协议。牛逼吹大了,就不详细写了。


还有很多细节点,下次有时间再接着写吧。






0 0