Android、IOS原生工程引入Codova混合开发

一、背景       
       由于历史原因,一期项目是用原生开发的,二期及以后想引入H5开发,原生开发和H5开发的选择因素另文介绍,这里暂不赘述。引入H5开发由两种方案供团队参考:一、使用H5重写一期的原生功能,后期整个项目全部使用纯H5开发。二、新模块使用H5开发,一期的模块复用。方案一优点:纯H5开发,有成熟的技术框架、开发人员充足;缺点:需要重写一期功能,时间紧、工作量大,所有的技术问题全部使用H5解决,如果需要使用原生的功能,需开发插件调用;方案二优点:一期已经完成的模块可以复用,新模块开发过程中H5解决起来比较困难时,可使用原生开发,比如地图相关功能。缺点:代码架构需要重构,引入混合开发模式,技术问题需要花时间和精力解决。经过技术评估和讨论,团队决定采用混合开发模式,故决定采用方案二,也就是本文介绍的重点:android和ios原生工程引入H5混合开发。当讨论如何进行混合开发时,我们有个原则就是一期能用的页面全部复用,但是由于引入了H5开发,有部分页面无可避免的需要重写,到底哪些页面需要重写,才能最大化减少工作量,同时又能搭建一个既支持苹果开发又支持安卓开发的易扩展、可复用的良好的工程架构。
二、主页的选择
    安卓举例,我们新开发的功能都尽量想使用H5开发,由于cordova 只有activity类,没有fragment类,若使用原生的页面做为主页,则无法调用H5的fragment,因为cordova压根没有fragment,则主模块的fragment无法做成h5页面。所以主页只能用H5做,不可免避免的需要重写的页面就是指主页和各个主模块的Fragment页面。但是由于二期该部分页面改动较大,即使不重写也需有较大改动的工作量,所以重写是可以接受的。简单点说,就是H5调原生和原生调H5的区别,即哪种页面作为主架构(主页)的选择,另一种页面只能作为分支页面。通过这些考量,我们设计出整个工程的代码架构图,登录及注册模块业务较复杂,而且不影响H5的整体架构,所以可直接复用。
Android、IOS原生工程引入Codova混合开发
       
三、node.js,dva的安装和工程创建
    1、 安装nodejs,下载安装对应自己操作系统的版本,官方地址: https://nodejs.org/en/download/ 
    2、 安装 dva-cli,命令:npm install -g dva-cli  网络不好的情况下,使用淘宝镜像,
           命令: npm config set registry https://registry.npm.taobao.org
    3、 创建和启动app, 命令:$ dva new myapp, $ cd myapp, $ npm run start 
    4、 打包:$ npm run build,将dist目录下的四个文件考入到cordova工程的www文件夹下,四个文件指index.css、index.html、index.js、myIndex.css

四、cordova的安装和工程创建
    1、安装cordova,命令:npm install -g cordova
    2、创建工程,注意包名和app名字和之前原生的工程尽量保持一致,减少工程合并时的复杂度,命令:cordova  create  test  com.cordova.test test   
       (创建cordova工程  <文件夹名>  <包名> <app名>)
    3、 cordova生成android和ios工程:cordova platform add android/ios, 如果查看有哪些平台可以添加,可以使用命令 cordova platform 查看通过上面的操作,就完成了我们合并工程的准备工作,接下来我们就可以进行最重要的环节,合并cordova工程和原生工程。

五、cordova生成的android工程与原生android工程合并
    经过团队的综合考虑和讨论,合并应该以cordova的工程为主,将原生工程的代码和配置文件拷入。主要拷入内容分为:
    1、src目录下的java文件
    2、res目录下的资源文件,含layout、drawable等
    3、合并manifests.xml
    4、工程视图切换到project,首先合并工程下的build.gradle,将dependencies和allprojects下相关配置拷入,如本项目拷入配置dependencies:classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0' ;allprojects:flatDir { dirs 'libs' };最后合并app目录下的build.gradle,包括dependencies,apply plugin等,仔细检查原生工程,相关的配置都需要合并进来。
        经过上面四个步骤,android的工程应该合并完毕,这时,可以使用cordova打包,命令:cordova run android 或android studio直接打包,运行效果应该是一致的。

六、cordova生成的ios工程与原生ios工程合并
   将iOS原生工程加入cordova工程 ,需要将iOS原生工程文件考入cordova工程下platforms文件夹下生成的iOS工程中,不能将cordova生成的文件加入iOS原生在整体拷回来。后者会产生各种问题。拷入部分主要有:
   1、工程文件,.m、.h、.xib文件
   2、图片资源,可以直接拷贝原来的文件,需要注意的是如果没有放到cordova生成的iOS 工程文件图片片管理器下,记得引用的地方改下路径
   3、cocoapod,如果用了这个,请在cordova生成的iOS工程中,重新添加一下podfile文件 ,然后pod install
   4、plist文件,pch文件,可用cordova生成的 也可以用原来的,用原来的要记得改路径
   5、注意General中的配置,因为工程用的是cordova生成的,原来项目中的如bitcode等的配置会影响到最后的结果,配置如下:1)User Header Search Paths 添加$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include  2)BitCode 改为NO
    经过上面五个步骤,iOS的工程合并完毕,这时,可以使用cordova打包,命令:cordova run ios 或xcode直接打包,运行效果应该是一致的。

     
七、原生页面调用H5页面
    本项目中唯一一个原生页面调用H5页面的功能,就是登录页面调用主页,文章前面也有介绍,因为登录页面业务较复杂(本项目做过密码加密、密码强度不够直接跳到修改密码页面等功能),而且不影响H5的整体架构,所以直接复用。因为登录是原生的,就需要把登录后从服务器拿到的用户信息和sessionId传到H5的主页。本项目登录后获取用户对象和sessionId,我们把这两个信息封装成json格式的参数,通过URLEncoder编码后直接放在H5主页url后面作为参数传递,代码:launchUrl+ "?"+ URLEncoder.encode(param,"UTF-8");主页接到参数后,通过decodeURIComponent(param)解码后,即可得到我们用户对象和sessionId,用户对象是h5页面各个功能模块需要使用的数据,sessionId是请求后台服务时,需要传递到后台作为认证标识的,如果sessionId过期或失效后,后台会返回401或403错误,这时H5直接调用原生的登录页面。登录页面会重新使用token登录或者输入用户名、密码登录。
    介绍完原生页面调用H5页面,下面介绍H5调用原生页面,本项目中调用的地方较多,凡是一期复用的功能,均是使用改方法实现。

八、H5调用原生页面
    H5页面调用原生插件是通过提供cordova自定义插件实现的,含android和ios,下面具体结束cordova自定义插件的使用方法。
1、安装插件开发助手
npm install -g plugman
2、创建插件
plugman create --name [插件名] --plugin_id [插件ID] --plugin_version [插件版本号]
plugman create --name call-activity-plugin --plugin_id call-activity-plugin --plugin_version 1.0.0
3、plugin.xml的配置文件,包含android和ios两个平台。
Android、IOS原生工程引入Codova混合开发
4、js文件的编写
var exec = require('cordova/exec');
module.exports.go = function (arg0, success, error) {
    exec(success, error, 'callActivityPlugin', 'call', arg0);
};
5、插件类的编写,前四个步骤是不区分平台的,插件类的编写android和ios需要分开
1) android平台核心代码
Android、IOS原生工程引入Codova混合开发
2)ios平台核心代码
Android、IOS原生工程引入Codova混合开发

6、插件放到指定路径下,然后使用命令安装,命令:cordova plugin add d:\xxx\xxx\xxx\call-activity-plugin,注意需要带上路径。如果插件路径下没有.json文件,使用 plugman createpackagejson  [插件路径] , 一直点确定,直到yes结束,查看已生成,然后再plugin add

7、h5调用方法,按照约定需要传四个参数,(android类名,android参数,ios类名,ios参数),核心代码示例如下:
cordova.plugins.callActivityPlugin.go(['com.jsptpd.zhabapp.activity.WelcomeActivity', JSON.stringify(jsparam), 'LoginViewController', JSON.stringify(jsparamIOS),], null, null);
      调用原生页面时,由上面插件的写法可以看出来,都是从继承cordova的cordovaActivtiy类页面进行跳转的,所以返回也是原生的返回,返回功能不需要改动原生的页面。后期开发过程中,如果用h5实现比较困难的,也可以直接使用此方法调用在原生实现的功能页面。
      经过上面八个步骤,一个完整的原生工程引入混合开发模式的工程项目合并完成。