当前位置 : 主页 > 手机开发 > 其它 >

优化Hybrid app的原生支持

来源:互联网 收集:自由互联 发布时间:2021-06-12
最近老板组织了一下技术分享,我分享了两点东西,最后老板的点评还是挺有意思的,多点图,尽量深入浅出,吃透了才能深入浅出,说得云里雾里的,多半是自己还没理解到位。我整

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

什么是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/
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		//lastHttpUrl
		if(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;
	}
	
	@Override
	public 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("", "");}
	}

	@Override
	public 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();
	} 

	@Override
	public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
		super.onReceivedError(view, errorCode, description, failingUrl);
		onError(ConstVal.WebError.net);
	}

	@Override
	public 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://www.voidcn.com/article/p-giglocoz-da.html

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


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


原生到web:

这个是最简单的:

//android
public void runJs(String action){
	if(iWebView != null){
		iWebView.loadUrl("javascript:"+action);;			
	}
}

//IOS
func 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{
	@JavascriptInterface
	public void clearHistory(){
		//let it empty
	}
}

//javascript
window.nativeJava.clearHistory()
IOS也有提供类似的方法: https://developer.apple.com/reference/webkit/wkscriptmessagehandler

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

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

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


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

上一篇:hybrid app开发工具
下一篇:勘误
网友评论