利用C#开发移动跨平台Hybrid App(一):从Native端聊Hybrid的实现

0x00 前言

前一段时间分别读了两篇博客,分别是叶小钗兄的《浅谈Hybrid技术的设计与实现》以及徐磊哥的《从技术经理的角度算一算,如何可以多快好省的做个app》。受到了很多启发,同时也有自己的一些看法。因为目前三大平台(虽然wp的份额相对于iOS以及android来说十分可怜)的开发语言分别是Objective-C(swift)、Java以及C#,先不论平台的其他特性如何,单单是各个平台开发语言就已经不同了。而往往一个App要同时登陆不同的多个平台,那么如何能够快速的开发跨平台的App,尽量使得代码复用便成了一个开发者不得不面对的问题。

正如叶小钗兄在他的那篇博文中所说的:“这个时候使用IOS&Andriod开发一个APP似乎成本有点过高了,而H5的低成本、高效率、跨平台等特性马上被利用起来形成了一种新的开发模式:Hybrid APP。”——利用Web配合Native开发的Hybrid APP出现了。所以在正式介绍C#开发跨平台App之前,我觉得有必要先从不使用C#语言进行开发的另外两个平台(iOS~OC、Andorid~Java)的角度来聊聊Hybrid。需要提醒各位注意的是本文中我所使用的一些web端代码来自叶小钗兄所分享的web端“简单Hybrid框的实现”的代码 。

本文所使用的iOS端Hybrid的代码可以在这里查看:

https://github.com/chenjd/iOS-Hybrid-Sample

0x01 你好,WebView

由于Hybrid开发的Web端本质上是一些网络页面,因此在使用App的相关功能时相当于利用原生平台去访问一些网页。正是因为内容来源于网页而不是依赖于平台,因此也就变相的实现了跨平台。那么对于iOS和Android来说,它们需要在Native端提供一个用来访问页面的容器——WebView。

iOS的UIWebView

在iOS平台我们会使用UIKit.framework的UIWebView来访问页面。利用UIWebView控件,iOS平台既可以获取网络资源也可以加载本地的HTML代码。这里主要会使用的是以下三个方法:

UIWebView控件中加载资源的方法
- (void)loadData:(NSData *)data
        MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)encodingName
         baseURL:(NSURL *)baseURL
- (void)loadHTMLString:(NSString *)string
               baseURL:(NSURL *)baseURL
- (void)loadRequest:(NSURLRequest *)request

下面我们就利用这些方法,在iOS平台上使用UIWebView控件来访问叶小钗兄所提供的web页面。

//ViewController.h
@interface ViewController : UIViewController
{
    UIWebView *webView;
}

//ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    webView = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    //load yexiaochai‘s html
    NSBundle *thisBundle = [NSBundle mainBundle];
    NSString *path = [thisBundle pathForResource:@"webapp/hotel/index" ofType:@"html"];
    NSURL *baseURL = [NSURL fileURLWithPath:path];
    NSURLRequest *urlReq = [NSURLRequest requestWithURL:baseURL];
    [self.view addSubview: webView];
    [webView loadRequest:urlReq];

在iOS模拟器中运行,我们可以看到在Native端通过UIWebView已经加载出了Web端的内容。

当然,此时我们只是迈出了第一步,即在Native端渲染出Web端的内容。但是还没有涉及到Native和Web之间的交互。既然说道了Native和Web交互,那么便不得不提的是另外两个重要的方法:


UIWebView控件中涉及Native和Web交互的方法

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
 navigationType:(UIWebViewNavigationType)navigationType

除此之外,iOS的UIWebView还有一些页面能否前进后退的属性以及控制它们前进后退的方法:


UIWebView控件中关于页面前进后退的属性和方法

canGoBack
canGoForward
- (void)goBack
- (void)goForward

初步介绍完iOS的UIWebView之后,再让我们来看一看Android平台上的Web页面的容器——WebView。

Android的WebView

在Android的部分,我们首先需要知道的是在Andorid平台上我们使用的是android.webkit下的WebView来访问页面。和iOS平台类似,Andorid平台是通过利用WebView来实现获取网络资源或是加载本地的HTML代码的。需要提醒各位注意的一点是,为了使Activity能够正确的连接上网络,在使用WebView加载远程资源时务必确保在AndroidManifest文件中开启INTERNET权限:

<uses-permission android:name="android.permission.INTERNET" />

而WebView加载资源的方法主要包括以下几个:

WebView加载资源的方法
public void loadUrl(String url);
public void loadUrl(String url, Map<String, String> additionalHttpHeaders);
public void loadData(String data, String mimeType, String encoding);
public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl);

下面我们就利用这些方法,在Andorid平台上使用WebView控件来访问叶小钗兄所提供的web页面。

首先我们要在Native端添加一个WebView控件,通过在layout文件中添加<WebView>就可以十分容易的实现。

<?xml version="1.0" encoding="utf-8"?>
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>

当然,除了在layout中添加<WebView>元素之外,我们可以直接通过代码在Activity设置为WebView,

WebView webview = new WebView(this);
setContentView(webview);

不过下文还是假设我们在layout中添加了<WebView>元素。
此时,我们在Native端已经准备好了盛放Web页面的容器。接下来便是加载Html资源了。

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("file:///android_asset/webapp/hotel/index.html");

到这里我们便再次将叶小钗兄的Web页面渲染了出来。
和上文描述iOS类似,此时我们也仅仅是迈出了第一步,即在Native端渲染出Web端的内容。但是还没有涉及到Native和Web之间的交互。不过Android平台和iOS平台当然也存在着区别,例如默认状态下Andorid的WebView是不支持js的,所以如果我们仅仅是将一个web页面作为一个没有交互的静态UI是可以的,但是如果想要使用js进而实现Native和Web的交互,那么还需要多设置一些内容。这里我们需要获取WebView的WebSettings对象,在WebSettings中开启对js的支持。

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

好了,开启了对js的支持,也就是开启了对Native和Web交互的支持。而Android平台主要通过以下方法来实现Native调用web以及web调用Native的:

  • public void evaluateJavaScript(String script, ValueCallback<String> resultCallback);方法可以在Native端调用JS代码,当然这里存在一个Android版本的问题,下文还会详细说明。
  • 另外我们需要使用JavascriptInterface.java,通过这个interface来完成Web调用Native的实现。

除此之外,Android的WebView当然还有和iOS的UIWebView类似的用来控制页面行为的一些方法:

  1. canGoBack();
  2. goBack();
  3. canGoForward();
  4. goForward();

0x02 Native端调用JS

在上一小节中我分别向大家介绍了iOS和Android在Native端是如何做到既能够访问Native本地资源,又能够访问远程资源的。这样在App开发的过程中将部分常常需要更新的模块以Web的形式创建,不仅仅避免了用户升级App整包替换的情况,又能够比较快速的实现多平台更新迭代

而这一切的背后都离不开Native和Web之间的交互。这种交互说起来无非是Native调用Web的方法,或是Web调用Native的方法。那么在本小节,我就和大伙分别聊聊iOS和Android是如何调用Web中的方法的。

iOS Native调用JS

//ViewController.h
@interface ViewController : UIViewController
{
    UIWebView *webView;
    NSMutableString *msg;
    UITextField *msgText;
    UIButton *nativeCallJsBtn;
}

//ViewController.m
- (void)createNativeCallJsSample{
    [webView loadHTMLString:@"<html><head><script language = ‘JavaScript‘>function msg(text){alert(text);}</script></head><body style=\"background-color: #0ff000; color: #FFFFFF; font-family: Helvetica; font-size: 10pt; width: 300px; word-wrap: break-word;\"><button type=‘button‘ onclick=\"msg(‘Js调用‘)\" style=\"margin:30 auto;width:100;height:25;\">web button</button></body></html>" baseURL:nil];
    //创建一个UITextField,用来在native调用js时向js的函数传送参数
    [self createNativeTextField];
    //创建一个按钮,用来演示Native调用js
    [self createNativeCallJsButton];
}
...
//native 调用 js
- (void)btnClickMsg{
    [msg setString:@"msg(‘"];
    [msg appendString:@"native调用js:"];
    [msg appendString:[msgText text]];
    [msg appendString:@"‘)"];
    [webView stringByEvaluatingJavaScriptFromString:msg];
}

我们首先使用UIWebView的loadHTMLString方法,直接加载了一段Html代码。其中的JS部分定义了一个函数function msg(text){alert(text);},即调用alert方法。而在Native端,我们使用UIWebView的stringByEvaluatingJavaScriptFromString方法来实现对JS的调用。

最左边的截图是直接点击Web页面中HTML代码生成的Button之后触发的JS的msg方法。

而中间和右边的截图,则是通过Native的UITextField控件获得用户的输入之后再通过Native的按钮控件调用btnClickMsg方法,进而调用stringByEvaluatingJavaScriptFromString方法之后,从Native端执行了JS的msg方法。

Android Native调用JS

Andorid平台与iOS在调用JS的部分十分类似。不过需要提醒各位注意两点,其一就是我们在上文提过的,Android的WebView默认状态下没有开启和JS交互的功能。这里我们需要获取WebView的WebSettings对象,在WebSettings中开启对js的支持。第二点需要注意的是Android的版本问题。在Kitkat(4.4)之后,我们可以使用evaluateJavaScript方法来调用JS,而之前的版本我们直接调用WebView的loadUrl的方法,将JS代码作为参数传入。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    WebView myWebView = (WebView) findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    //开启js支持
    webSettings.setJavaScriptEnabled(true);
    myWebView.loadData("<html><head><script language = ‘JavaScript‘>function msg(text){alert(text);}</script></head><body><button type=‘button‘ onclick= ‘msg()‘>web button</button></body></html>", "text/html", "utf-8");

    evaluateJavascript(myWebView);
}

....
//Native调用JS
public static void evaluateJavascript(WebView mWebview) {
    //判断版本调用不同的方法
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        mWebview.evaluateJavascript("msg(‘native call js‘)", null);
    } else {
        mWebview.loadUrl("msg(‘native call js‘)");
    }
}

0x03 Web调用Native

Hybrid交互设计

我想行文至此,各位应该已经明白了两大平台(iOS和Android)的Native端是如何调用JS代码的了。那么本小节则主要来聊一聊Web是如何调用Native的。Web和Native之间交互的第一步便是要约定好格式。由于我直接使用了叶小钗兄的Web部分,因此按照他设计的Web请求Native的模型:

requestHybrid({
  //创建一个新的webview对话框窗口
  tagname: ‘hybridapi‘,
  //请求参数,会被Native使用
  param: {},
  //Native处理成功后回调前端的方法
  callback: function (data) {
  }
});

//index.html中的实际例子
requestHybrid({
    tagname: ‘forward‘,
    param: {
        topage: ‘webapp/flight/index‘,
        type: ‘webview‘
    }
});

而Native端会收到一个URL,例如:

hybrid://forward?t=1447949881120&param=%7B%22topage%22%3A%22webapp%2Fflight%2Findex%22%2C%22type%22%3A%22webview%22%7D
十分清楚,这里我们交互使用的schema是hybrid://,在Native部分会监控Webview发出的所有schema://请求,然后分发到“控制器”hybridapi处理程序,Native控制器处理时会需要param提供的参数。下面就让我们来分别看一下iOS和Android是如何使用Web调用Native的吧。(这里我只是简单的实现了和叶小钗兄web交互的iOS部分,Android部分演示的是一个简单的Web调用Native的例子)。

iOS Web调用Native

在iOS平台,我们会用到shoudStartLoadWithRequest这个方法来捕获Web所发出的请求。下面我们就来看一段简单的演示吧。

//按照和web端规定的格式,获取数据
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL * url = [request URL];
    if ([[url scheme] isEqualToString:@"hybrid"]) {
        NSString *actionType = request.URL.host;
        //从url中获取web传来的参数
        NSDictionary *actionDict = [self getDicFromUrl : url];
        //根据web的指示,native端相应的做出回应
        [self doActionType:actionType : actionDict];
        return NO;
    }
    return YES;
}

//从url中获取web传来的参数
- (NSDictionary *) getDicFromUrl : (NSURL *)url{
    NSArray* paramArray = [[url query] componentsSeparatedByString:@"param="];
    NSString* paramStr = paramArray[1];
    NSString *jsonDictString = [paramStr stringByRemovingPercentEncoding];
    NSData *jsonData = [jsonDictString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *e;
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:&e];
    return dict;
}

//根据web的指示,native端相应的做出回应
-(void) doActionType : (NSString*) type : (NSDictionary*) dict{

    if ([type isEqualToString:@"forward"]) {
        [webView goForward];
    }
    //打开一个新的Web
    if([dict[@"type"] isEqualToString: @"webview"]){
        [self web2Web: dict[@"topage"]];
    }
    //打开一个Native页面(我简化为了控件)
    else if ([dict[@"type"] isEqualToString: @"native"]){
        [self web2Native];
    }
}

最左边的图是最开始的Web页面,点击“新开webview...”按钮之后,Native端又打开了一个新的Web页面,即中间的图所示。

最右边的图演示的是点击“跳转native...”按钮之后,Native端创建了一个Native的UI控件。

Android Web调用Native

Android平台上Web调用Native要比iOS稍微复杂一些。因为在Android平台上我们需要借助JavaScriptInterface来实现Web调用Native的功能。

下面就让我们演示一个小例子,不让Web调用JS的alert,相反使用Web调用Native的Dialog来显示提示吧。

首先我们要在Android应用中引入JavaScriptInterface。

...
...
import android.webkit.JavascriptInterface;

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的 addJavascriptInterface()方法将这个类和JS代码绑定,作为Web调用Native的桥梁。

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

最后,在Web端也做相应的实现。点击Web中的button会调用Native的showToast方法。

<input type="button" value="Say hello" onClick="showAndroidToast(‘Hello Android!‘)" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

0x04 让Web的归Web,Native的归Native

到此,在没有使用C#作为开发语言的两大平台上使用Hybrid的方式令Web和Native交互的内容就介绍完了。但是,不知道各位读者是否和我有相同的感觉呢?那就web和native之间的界线仍然十分明显。

事实上我认为Hybrid的最大价值其实并不在于跨平台。相反,在一些常常变动的模块的热更新的问题上使用这种将Web和Native结合的Hybrid方式是十分适合的。

这是因为它并没有从本质上解决所谓的跨平台的问题,而是采用了一个十分讨巧的方式或者说是逃避问题的方式超脱于平台的范畴。而这也导致了Hybrid的缺点,比如Hybrid体验和Native相比是有距离的。因此,Hybrid是一种优秀的热更新技术,同时也是一个不完美的跨平台方案。

至于解决跨平台问题更好的方案,就交给Native端自己去想办法吧。比如三大移动平台都使用同一种语言甚至是同一个IDE进行Native部分的开发,看上去似乎就是一个很美妙的方案。如果再加上利用Hybrid方式带来的热更新加持,结果就更棒了。

所以,就让Web的归Web,Native的归Native吧。

未完待续~~~~~

时间: 2024-10-08 23:32:00

利用C#开发移动跨平台Hybrid App(一):从Native端聊Hybrid的实现的相关文章

Hybrid APP基础篇(一)-&gt;什么是Hybrid App

说明 Hybrid APP是目前广泛流行的一种APP开发模式,本文对其做简单介绍 目录 前言 参考来源 楔子 Hybrid发家史 突然兴盛的H5 H5大行其道 H5渗入APP开发 Hybrid的兴盛 Hybrid概述 Hybrid定义 Hybrid App的类型划分 Hybrid架构 基本原理 内部的实现原理流程 Hybrid的未来 现行多种App开发模式以及分析比较 Hybrid面临的挑战 前言 参考来源 前人栽树,后台乘凉,本文参考了以下来源 HTML5 APP----2014年H5没火,w

Hybrid APP混合开发的一些经验和总结

Hybrid APP混合开发的一些经验和总结 写在前面: 由于业务需要,接触到一个Hybrid APP混合开发的项目.当时是第一次接触混合开发,有一些经验和总结,欢迎各位一起交流学习~ 1.混合开发概述 Hybrid App主要以JS+Native两者相互调用为主,从开发层面实现“一次开发,多处运行”的机制,成为真正适合跨平台的开发.Hybrid App兼具了Native App良好用户体验的优势,也兼具了Web App使用HTML5跨平台开发低成本的优势. 目前已经有众多Hybrid App开

【JavaScript】Hybrid App开发 四大主流移平台分析

转自http://dev.yesky.com/238/34657738.shtml Hybrid App在过去的两年中已经成为移动界的核心话题,但是作为一名Web开发者来说要如何站在移动互联网的浪潮之巅呢?是选择学习原生开发,研究Java.Object-C.C#等语言,还是选择继续使用网页开发,容忍HTML5功能的局限性?就在开发者左右为难的情况下Hybrid App作为一个折中的解决方案诞生了.那么究竟什么才是Hybrid App呢? Hybrid App概念 Hybrid App:Hybri

Hybrid App开发 四大主流移平台分析

Hybrid App在过去的两年中已经成为移动界的核心话题,但是作为一名Web开发者来说要如何站在移动互联网的浪潮之巅呢?是选择学习原生开发,研究Java.Object-C.C#等语言,还是选择继续使用网页开发,容忍HTML5功能的局限性?就在开发者左右为难的情况下Hybrid App作为一个折中的解决方案诞生了.那么究竟什么才是Hybrid App呢? Hybrid App概念 Hybrid App:Hybrid App is a mobile application that is code

Hybrid App—Hybrid App开发模式介绍和各种开发模式对比

什么是Hybrid App 最开的App开发只有原生开发这个概念,但自从H5广泛流行后,一种效率更高的开发模式Hybrid应运而生,它就是"Hybrid模式".Hybrid APP是目前广泛流行的一种APP开发模式 H5渗入APP开发 我们都知道,原生APP开发中有一个webview的组件(Android中是webview,iOS7以下有UIWebview,7以上有WKWebview),这个组件可以加载Html文件. 在Html5没有兴盛之前,加载的Html往往只能用来做一些简单的静态

【JavaScript】关于Hybrid App技术解决方案的选择

[引言]近年来随着移动设备类型的变多,操作系统的变多,用户需求的增加,对于每个项目启动前,大家都会考虑到的成本,团队成员,技术成熟度,时间,项目需求等一堆的因素.因此,开发App的方案已经变得越来越多了.曾经有一段HTML5的小浪潮,无数的人参与或者看到过一个讨论:原生开发还是混合开发,又或者是Web开发?到底最佳实践是怎样的,笔者认为只有实践过的人才会知道.尤其是在这个充满各种变数的移动互联网时代. [摘要]笔者将从Hybrid App的开发现状出发,阐述Hybrid App的优缺点,同时对比

测试工作——如何区别一个 App 是 Native App, Web App 还是 Hybrid app?

nativeapp是一个原生程序,一般运行在机器操作系统上,有很强的交互,一般静态资源都是在本地的.浏览使用方便,体验度高.在实现上要么使用Objecttive-c和cocoaTouch Framework撰写IOS程序,要么选择java+Android Framework撰写android应用程序. hybridapp是一个半原生程序,伪造了一个浏览器的apk/ipa原生程序,把地址写死了,然后里面运行了一个webapp.里面是WebView UI .但是还是运行在机器的操作系统上,交互较弱,

你真的了解Hybrid APP吗

HTML5标准的定稿,使人们对HTML5技术燃起了更大的热情,HTML5 是唯一通吃PC端.移动端( iOS.Android.Windows Phone )的跨平台语言.由此带来很多革命性的改变,比如降低创业成本.降低技术门槛,借助HTML5技术,Hybrid混合模式也逐渐被认可.但是,你真的了解Hybrid APP吗? 移动端市场的红利,让不少厂商涉足Hybrid开发平台.理论上解说很简单,技术实践却很难,Hybrid App的精髓在混合上,就像水和面粉,在大厨手里比例拿捏的准确,美味出炉:而

Native App、Web App 还是Hybrid App

Native App.Web App 还是Hybrid App? 技术 标点符 1年前 (2014-05-09) 3036℃ 0评论 一.什么是Native App? Native App即原生应用,即我们一般所称的客户端,是针对不同手机系统单独开发的本地应用,如需使用需要先下载到手机并安装,下载Native App的最常见方法是访问应用程序商店,如苹果的App Store.安卓市场.Google Play等.在技术实现上一般采用针对操作系统的特定语言进行编写,如:使用Objective-c开发