AndroidDocker一致架构设计(5)

-- <HTML5 + Docker>的插件集装箱


By 高焕堂 (台灣Docker論壇 主席)

2015/03/16  misoo.tw@qq.com

歡迎報名參加 2015 北京開講 (4月10-11兩天)

doker UI容器安装 docker必装插件_doker UI容器安装

 高煥堂 老師

==> 請看說明

ee                                                                                                             ee

高煥堂的相關文章:

  A01、从「集装箱」思考Docker风潮:Docker潮流下的赢家策略

  A02、Docker:从容<器>到集装箱设计之<道>

  B01、Android和Docker的一致架构设计(1):追求今天决策的未来性

  B02、Android和Docker的一致架构设计(2):包装<跨集装箱>的通信协议

  B03、Android和Docker的一致架构设计(3):建立企业主的<核心集装箱> 

  B04、Android和Docker的一致架构设计(4):<分合自如>的设计模式

  

前言

  在我的上篇文章《Android和Docker的一致架构设计(3):建立企业主的<核心集装箱>》文章里,我借镜于Android核心进程(即SystemServer)为例说明了,我们可迅速替各企业主设计出其独特的核心集装箱,来协助创造业务App模块之间,极为迅速的、瞬间的动态组合,来支撑企业流程(Business Process)。如果您很孰悉Android系统服务层的架构,则有足够的能力去理解<核心集装箱(进程)>的设计和角色。反之,从许多读者给我的反馈中,发现大多数App开发者并不容易看懂Android核心进程(即SystemServer)的架构设计。因此,本文特别拿较简单的PhoneGap框架(Framework)来作为例子,或许可让读者们更能够领悟企业主<核心集装箱>的设计了。

  大家都知道,PhoneGap是基于HTML,CSS和JavaScript的跨平台移动App软件框架。例如,它能搭配HTML5来支持Web App的开发,并可发挥本地(Native)设备的功能,包括地理定位、加速器、联系人、声音和振动等。此外,PhoneGap还可以有效管理各种插件(Plugin),提供给HTML5应用来调用。于是,本文从HTML5跨平台的视角来看Docker、Android与PhoneGap的整合架构设计。让架构师扩大视野,涵盖终端和云平台,以一致的架构设计思维,追求决策未来性,开创产品的不一样生命。

  

1HTML5Docker的端云结合 

   由于Android也是基于Linux进程(Process)的沙盒隔离策略的,它与Docker的系统架构里蕴含了高度的一致性:都采取基于进程的集装箱思维。所以,我们能以集装箱设计思维看HTML5与Docker的结合,让HTML5的跨(终端)平台与Docker的跨(云端)平台相互辉映,而且能基于<一致的架构设计理念>(Conceptual Integrity)来建立一致化的企业(微)服务管理环境。例如,典型的HTML5与Docker集装箱的结合如下图:

   

doker UI容器安装 docker必装插件_Docker_02

图-1、典型的HTML5 + Docker集装箱

这是HTML5的Web App理想架构,然而Android平台的碎片化,其实现难度很大,每一种机器的Android 系统实现都有些差异,其WebKit 浏览引擎实现也有差异。因此产生了混合型App(即Hybrid App),其兼具了Web App和Native App的特性。例如像Image Capture, NFC 或Android Open Accessory等是目前JS无法支持的,只能实作于本地(Native)插件。但是App逻辑(logic)和UI都能使用web technologies 来跨平台,实现了一致的使用体验。因此,就需要有一套框架,来有效管理这些众多的本地插件。像业界很著名的PhoneGap就是扮演此项角色的优秀框架。PhoneGap是一个应用框架让开发者使用HTML, JavaScript和CSS来撰写手机上可执行的App。 这意味着,这个App有它自己的图标(icons)且执行起来就如同Native App一般,不会看到浏览器(Browser)的边框。这些App可以摆在应用商店里,给大家自由下载。同时也能呼叫一些本地功能(Native Function)让其表现得更像本地App。开发者会喜欢使用PhoneGap的主要理由是:能有共同的API,可跨平台。这种混合型App的平台架构,如下图:

   

doker UI容器安装 docker必装插件_Android_03

图-2、混合型App的平台架构

  对于Android终端人员来说,欲理解PhoneGap框架API及其幕后的插件管理机制,是很容易的。在Docker集装箱的理念下,Android终端服务企业,就能轻易地将这套熟悉的插件管理机制,移植到Docker云平台上,包括插件共通API制定、插件状态管理、异步回调(Callback)机制等等,以便有效管理分散于众多Docker集装箱里的企业逻辑插件。如下图:

   

doker UI容器安装 docker必装插件_doker UI容器安装_04

图-3、采取一致的管理模式

  于是,加深了终端与云端的一致设计理念(Conceptual Integrity),让分散于终端与云端的企业(逻辑)插件管理和维护,拥有整体的和谐感。 

doker UI容器安装 docker必装插件_doker UI容器安装_05

    图-4、促进设计概念的一致性

  基于HTML5/PhoneGap来实现移动终端的高度跨平台,结合Docker的云端跨平台巨大潜能,开创终端与云端的完美跨平台。为了实现这个美好的梦想,本文陪您踏出第一步:将PhoneGap的本地插件管理机制,移植到Docker云平台上,包括插件共通API制定、插件状态管理、异步回调机制等,以便有效管理分散于众多Docker集装箱里的企业逻辑插件。终端与云端的<架构设计一致理念>是实现上述梦想最佳途径。


2PhoneGap的插件管理              

  愈来愈多人认为,在大数据的异型分布和碎片化的趋势下,以Docker集装箱来包装了微服务(Micro-Service)是一条康庄大道。因为,在运行时间(Run-time)容易将它们动态的组合成为多样化的App来支撑企业多变的业务流程(Business Process)。然而,微服务只是PAAS、SAAS等平台上的一个通称而已,大家常常会问到,这些微服务会以什么形式而存在Docker集装箱里呢? 又如何有效管理这些微服务所代表的企业逻辑(Business Logic)呢? 其答案是:软件框架(Framework)与插件(Plug-in)。例如下图里,大家非常孰悉的HttpServlet就是框架基类(Super-class),而我们则把业务逻辑写成(框架的)插件,也就是HttpServlet的子类(Subclass),就是下图里的myServlet子类。

   

doker UI容器安装 docker必装插件_PhoneGap_06

    图-5、云平台插件之例

  所以,Docker集装箱里的微服务,经常会以(框架的)插件形式而在,而在代码上则会以框架基类的子类来实现。例如,HTML5常常搭配PhoneGap框架来管理一些以Java撰写的插件(Plug-in),让WebView(含幕后的WebKit引擎)在执行HTML5和JS(即JavaScript)时,能够很方便地调用这些插件。

   

doker UI容器安装 docker必装插件_doker UI容器安装_07

图-6、PhoneGap框架的角色(一)

  HTML5主要用来设定UI画面(或页面)的布局(Layout),画面展现出来,让用户来操作画面,触发事件(Event)。WebView(含WebKit)会去执行JS(即JavaScript)的函数代码,来处理这些事件。在执行JS代码时,可以转而调用PhoneGap里的Java插件。Android的View类别体系里,定义了各式各样的子类别,包括TextView、Button、SurfaceView和WebView等。其中的WebView是给浏览器(如WebKit)用来呈现网页画面的。而画面布局(Layout)是由HTML(含HTML5)来规范的。上述架构落实为代码,其结构如下:

   

doker UI容器安装 docker必装插件_Docker_08

    图-7、PhoneGap框架的角色(二)

  在App执行时,用户会使用WebView画面而触发UI事件,JS就负责处理这些事件。必要时,JS会透过WebView(包含幕后的WebKit引擎)调用到IPlugin接口的函数,而实际执行了Java插件。此外,Java插件也能反向调用到JS模块里的函数。此外,PhoneGap也撰写了PluginManager来负责管理Java插件。例如,常用的插件能事先创建起来,当App需要时,就能迅速提供服务;而且能让多App来共享同一个插件。由于Android也是基于Linux进程(Process)的沙盒隔离策略的,它与Docker的系统架构里蕴含了高度的一致性:都采取基于进程的集装箱思维。于是,可以将Android进程视为集装箱,如下图所示。

    

doker UI容器安装 docker必装插件_doker UI容器安装_09

         图-8、Android进程包容PhoneGap的插件

  

刚才提到了,在我的另一篇文章:《Android和Docker的一致架构设计(3)建立企业主的<核心集装箱>》里,我提到过,无论是Linux、Docker或Android都各有它自己的核心部分。而且Docker和Android都以进程为沙盒空间(集装箱),例如,Docker的核心进程是Docker Daemon;而Android的核心进程是SystemServer。以此类推,我们也可知道PhoneGap也需要建立一个核心集装箱来管理愈来愈多的插件了。PhoneGap核心集装箱内部主要是插件管理器(PluginManager),如下图。

   

doker UI容器安装 docker必装插件_Android_10

        图-9、PhoneGap的核心模块

  PhoneGap框架基类(如Plugin等)会复制到各Android集装箱(进程)里,成为myPlugin模块的依赖部分(Dependency)。在集装箱里,框架扮演平台角色,它提供API(接口)来与插件衔接。所以,集装箱的内含软件模块常常可区分为三个不同角色:平台(Platform)、接口(API)和插件(Plug-in)。其中,平台建置的主流技术就是自己行业(如游戏)的应用平台框架设计与开发;而插件则包括:1)用户插件(即随用户而改变的插件);2)业务插件(如随企业而变的业务规则模块);3)平台插件(如配合Android、iOS、Win-8而异的音效插件)。至于接口,则是整合、管理及测试插件的核心机制。随着企业规模的成长和发展,上述的三种插件数目愈来愈多,其内涵的业务逻辑也都各不相同。此时,集装箱的<一致造形,包容变化>的功能就派上用场了。例如,上图的Plugin集装箱内部可能会随着企业逻辑的变化而改变如下:

  

doker UI容器安装 docker必装插件_doker UI容器安装_11

    图-10、Plugin集装箱内部的变化

  在企业应用软件开发时,会循序渐进地,先从企业应用领域解析出上述三种插件,将插件分离出来,并设计精致的接口,以便随时能将插件组合回来;最后,运用框架设计技术,将各接口整合于平台框架上,就成为自己的应用平台框架了。除了集装箱内部的善变之外,随着插件的增多,集装箱的数目也会增加,如下图:

   

doker UI容器安装 docker必装插件_Docker_12

图-11、插件和集装箱数目增加了

 人们往往喜欢追求平台的稳定性和标准性。然而,平台差异化是本质性的,无法删除的。反而必须以高度弹性的<平台插件>去吸收它,例如浏览器的JavaScript插件、PhoneGap的Java插件等。除了平台插件之外,还有企业的<业务插件>(如企业业务规则模块)。有些业务插件可能摆在Android终端的进程(集装箱)里,也可能摆在云平台的Docker集装箱里。如果采取本文所建议的<端云一致设计理念>让分散于终端与云端的企业(逻辑)插件管理和维护,拥有整体的和谐感,就能大幅企业的应变能力,以及市场竞争力。


3HTML5/JSJava插件的结合机制

例如,一般的语言表达Android的WebView的一段JavaScript语言:

// show.html (JavaScript程序代码)

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

<script type="text/javascript">

    function jsShow(str) {

        IToast.showToast(str);

    }

</script> 

当WebView(含幕后的Webkit里的其它类别)解析(执行)到其指令:

        IToast.showToast(str); 

就会去呼叫一段扩充的(Extended)的Java程序码(或称类别): 

// myToast.java (Java程序代码)

public class myToast{

        Context mContext;

    myToast(Context c) {

        mContext = c;

    }

    public void showToast(String str) {

        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();

    }

于是,上面的JavaScript程序代码就透过WebView而呼叫到这个showToast()函数了。那么,这两段程序代码之间,又由谁来建立其连结关系呢? 那就依赖另一段程序代码了,就是: 

// myActivity.java

public class myActivity extends Activity {

    @Override

    public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

WebView appView = new WebView(this);

appView.loadUrl("file:///android_asset/www/index.html");

appView.getSettings().setJavaScriptEnabled(true);\

 

myToast obj = new myToast();

appView.addJavascriptInterface(obj, "IToast");

appView.loadUrl("file:///android_asset/show.html");

 

setContentView(appView);

    } 

其中的指令:   

    myToast obj = new myToast(); 

就诞生一个myToast类别的对象。接着, 

    appView.addJavascriptInterface(obj, "IToast"); 

就将该对象的参考(Reference)传递给WebView,这建立了WebView与myToast类别(对象)之间的链接关系。让WebView在执行JavaScript程序代码时,可以来呼叫myToast类别里的showToast()函数。首先,以图形表达出来:WebView与Android框架的关系:

  

doker UI容器安装 docker必装插件_doker UI容器安装_13

    图-12、 WebView是Android框架的一部分

  然后,以图形来表达JavaScript程序代码(即show.html)和myToast类别,如下图:

  

doker UI容器安装 docker必装插件_PhoneGap_14

    图-13、myToast和show.html都属于应用程序的一部分

 再以图形来表达myActivity类别,如下图:

  

doker UI容器安装 docker必装插件_PhoneGap_15

    图-14、 myActivity继承Activity基类

 由myActivity类别来诞生WebView对象,也诞生myToast对象,如下图:

  

doker UI容器安装 docker必装插件_Docker_16

     图-15、myActivity扮演Factory(工厂)的角色

  接着,由myActivity呼叫WebView的addJavascriptInterface()函数,将myToast对象的参考(Reference)传递给WebView对象,这建立了WebView与myToast类别(对象)之间的链接关系。如下图: 

  

doker UI容器安装 docker必装插件_doker UI容器安装_17

   图-16、WebView就能参考到myToast了

 接着,由myActivity呼叫WebView的loadURL()函数去将show.html载入到WebView里,如下图: 

  

doker UI容器安装 docker必装插件_doker UI容器安装_18

   图-17、WebView载入了show.html

 建立了链接关系之后,JavaScript的jsShow()函数就能透过WebView而呼叫到myToast的showToast()函数了,如下图: 

  

doker UI容器安装 docker必装插件_PhoneGap_19

    图-18、 JavaScript呼叫了Java类别

  从图里可以看出来,JavaScript模块(即jsShow()函数)与Java模块(即myToast类别)之间的相依性(Dependency)非常高,也就俗称的<高耦合性>,所以很不利于JavaScript模块的跨平台,也连带导致HTML5代码的框平台要求。因而有了PhoneGap框架来化解上述的不利情形。搭配PhoneGap的新架构就如下图所示: 

  

doker UI容器安装 docker必装插件_Android_20

    图-19、搭配PhoneGap的新架构

  由于IPlugin接口只提供单一函数:execute();使得PhoneGap也只能提供单一函数:exec()。然而,在HTML5_JS里可能有多个函数,例如play()和stop()等。于是,在HTML5_JS里,必须从play()和stop()函数转而呼叫PhoneGap.exec(),然后透过WebView而调用IPlugin接口的execute()函数;最后转而调用myPlugin的mpPlay()和mpStop()函数。其中的接口转换过程,以程序代码表达如下: 

var myProxy=function(){};   

myProxy.prototype.play = function(success, fail, para1, para2){

    return PhoneGap.exec( success, fail,

                'myPlugin',       //java类别名称,注册于plugins.xml中

                'mpPlay',         //在Plugin里用来配对的action名称

                [para1, para2]     //所传递的参数(parameter),Array结构

    );

};

myProxy.prototype.stop = function(success, fail, para1, para2){

    return PhoneGap.exec( success, fail,

                'myPlugin',       //java类别名称,注册于plugins.xml中

                'mpStop',        //在Plugin里用来配对的action名称

                [para1, para2]     //所传递的参数(parameter),Array结构

    );

};

PhoneGap.addConstructor(function() {

    PhoneGap.addPlugin('myProxy', new myProxy());

});

 此时,HTML5_JS里面就看不到PhoneGap.exec()函数了,其好处是:一方面让HTML5_JS程序代码比较单纯;另一方面,让HTML5_JS与PhoneGap.exec()两者相依性(Dependency)降低,让HTML5_JS不受制于PhoneGap的框架API。因而HTML5实现了完美的跨平台特性。

 

4结语

   人们不会期待一辆完整汽车既能在沙滩上跑,也能街道、高山、雪地里跑。 

     

doker UI容器安装 docker必装插件_PhoneGap_21

图-20、为什么会把轮胎拔?

   所以,设计了轮盘(或轮毂)接口,只要抽换轮胎,则引擎和车体就能通用于沙滩、街道、高山和雪地里跑了,我们就称它为<跨(地面)平台>了。架构师专注于未来环境的变化,创造更好架构来包容未来环境的变化,包括地面变化、引擎变化、买主变化、技术变化和用户变化等。现在,就拿地面变化来说明吧,如下图所示。面对地面的变化,架构师最熟悉的方法就是:把轮胎拔掉。 

     

doker UI容器安装 docker必装插件_Android_22

      图-21、EIT造形

  这项<把轮胎拔掉>的动作,就等于把一个EIT造形摆在车辆与地面之间,如下图所示。 

    

doker UI容器安装 docker必装插件_Android_23

图-22、包容EIT造形的集装箱

  EIT造形含有3个元素,恰恰好将车辆与地面衔接起来,如下图。 

   

doker UI容器安装 docker必装插件_Docker_24

         图-23、常见的集装箱结构

  在Android框架里,其SurfaceView框架与其(多变的)插件的结合关系,也是一样的。如下图: 

   

doker UI容器安装 docker必装插件_Android_25

       图-24、Android应用的常见结构

  无论在终端平台上,或是在云平台上,善用集装箱来包容插件的变化,是提升跨平台的有效手段。而端云一致的插件设计和管理模式,是迈向更完美跨平台的不二法门。

 

~ End ~