原生应用使用cordova并与h5应用分离

1.需求与使用场景

  打开一个新页面,要求能够加载本地zip格式的h5应用,该应用使用了某些原生能力;能够加载远程应用,该应用也使用了部分原生能力;能够在多个h5应用时同样适用;h5应用能够移植到其它场景,如web、第三方移动应用;h5应用无需复杂适配移动端,如android、iOS等;

2.目的

  让h5应用只专注于开发h5,涉及到原生功能,则交给原生应用去实现,通过cordova js功能进行h5与原生功能的交互。

3.前提

  需要3个应用,如下所示

  • h5应用:前端开发者只专注于前端开发和打包应用,应支持ionic、vue、react等应用
  • cordova应用:原生开发者创建,将cordova插件安装到cordova应用中,并生成对应的platforms即android、iOS
  • 原生应用:原生开发者创建,iOS应用通过pod引入cordova插件,并将config.xml 通过group 方式添加,www则以文件夹folder的形式引入,即config.xml相对路径引入,www绝对路径引入;

对以上3个应用的解读:

-- h5应用,正常的前端应用,若使用js则无需声明,若使用ts则需要声明 declare let cordova: any;全局变量

-- cordova应用,目的是将config.xml文件和platforms/android/www、platforms/ios/www文件夹copy出来备用。将其归为原生开发者创建范畴,是因为cordova.js通过exec来调用原生插件,将插件的管理交给原生应用。

-- 原生应用,管理插件,提供对应的原生能力。若使用cordova.exec方式交互(推荐),则将config.xml和www/cordova.js引入;若使用js service方式交互,则h5应用需要导入npm包,cordova应用添加插件,并将platforms/./www文件夹copy出来,放到原生应用下.

4. 原生部分

h5应用部分省略不表,原生部分以iOS为例
4.1 如何创建cordova项目

  • 在mac上,安装node,再在terminal中npm install -g cordova@lastest;
  • 创建应用cordova create myApp org.apache.cordova.myApp myApp
  • 项目目录下cd myApp
  • 安装插件cordova plugin add cordova-plugin-camera
  • 生成iOS应用 cordova platform add ios
  • cordova应用下的config.xml和platforms/ios/www是我们需要用到的

4.2 由于UIWebView有缺陷以及不再维护,并且官方推荐使用WKWebView,所以以WKWebView为例,进行\color{#FF7F50}{config.xml}的修改以及\color{#FF7F50}{cordova-plugin-wkwebview-engine}的简单使用的讲解
4.2.1 config.xml的修改

  1. 允许远程网页加载 ATS
<content src="index.html" />是本地加载的入口,也可设置远程地址
<allow-navigation href="https://*/*" />
<allow-navigation href="http://*/*" />
<allow-navigation href="data:*" />

以上三行等价于

<allow-navigation href="*" />  允许所有的网址跳转。
  1. 白名单设置
<access origin="*" /> 设置白名单,允许访问所有域

<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="*" /> 等价于以上两行
  1. 偏好设置
    <preference name="StatusBarStyle" value="lightcontent" />
  2. feature功能引入,以便cordova.js识别
<feature name="Camera">
    <param name="ios-package" value="CDVCamera" />
</feature>

添加后,cordova.exec才可以正确找到对应的原生文件,如cordova.exec(success, failed, 'Camera', 'getPicker',[参数])

  1. 插件添加
<plugin name="cordova-plugin-wkwebview-engine" spec="^1.2.0" />
<plugin name="cordova-plugin-camera" spec="^4.1.0" />
<plugin name="cordova-plugin-device" spec="^2.0.3" />
  1. info.plist文件添加私有权限

4.2.2 wkwebviewengine的简单使用
先决条件:

  • iOS原生应用pod 引入cordova以及插件
  • 新建继承自CDVViewController的ViewController,如HtmlViewController
  1. 简单的本地加载
    cordova加载本地自己的h5应用,需要在HtmlViewController初始化init的地方修改startPage,一定要在viewDidLoad之前;
    原因在于CDVViewController源码会在viewDidLoad的地方调用了与网页相关的三个方法:
    - loadSetting- createGapView- appUrl,并设置了默认appUrl;

适用场景:
在应用内下载了zip格式的h5应用后,将其保存并解压,再将工程目录下的www文件夹copy到应用程序内,并将h5应用替换(不是合并)对应文件,如替换www/index.html,即将www/cordova_plugins.js、www/cordova-js-src、www/cordova.js、www/plugins copy到h5应用下。

  1. 加载远程应用
    uiwebview:只需要设置下 self.startPage为远程地址即可;
    需要注意的地方:

self.startPage的赋值,必须在[super viewDidLoad]之前,否则self.startPage 会被默认赋值为index.html。
需要在config.xml中修改一下配置,否则加载远程H5时,会自动打开浏览器加载。
<allow-navigation href="https:///" />
<allow-navigation href="http:///" />
远程H5中也要引用cordova.js文件。
在 info.plist 中添加 App Transport Security Setting的设置。
wkwebviewengine貌似则不能这样,我试了几次均不行,所以采用了以下方法:
pod 引入cordova-plugin-wkwebview-engine
修改HtmlViewController.h

#import <Cordova/CDVViewController.h>
#import <Cordova/CDVCommandDelegateImpl.h>

@interface HtmlViewController : CDVViewController

@end

@interface CustomWKCommandDelegate: CDVCommandDelegateImpl // 核心指令

@end

@interface CustomCmdQueue : CDVCommandQueue

@end

HtmlViewController.m修改添加内容

#import <WebKit/WebKit.h>
@interface HtmlViewController ()<WKNavigationDelegate>

@end

@implementation CustomWKWebViewController

- (id)init {
    self = [super init];
    if (self) {
        // 重写CDVCommandDelegate
        _commandDelegate = [[CustomWKCommandDelegate alloc] initWithViewController:self];
        _commandQueue = [[CustomCmdQueue alloc] initWithViewController:self];

    }
    return self;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // 由于支持滑动返回,导航栏可以隐藏了
    [self.navigationController setNavigationBarHidden:YES animated:NO];
    
    // 使用驱动器加载
    [self.webViewEngine loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20]];
    
}

// 处理跨域等情况建议使用nginx
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    // 不能跨域的进行单独处理
    NSURL *url = navigationAction.request.URL;
    if ([url.scheme isEqualToString:@"域名"]) {
        
        //在取消跨域请求之前,自定义处理,如使用safari浏览器打开网页,若带参数,则将其进行拼接
        NSDictionary *param = nil;
        if (@available(iOS 10.0, *)) {
            [[UIApplication sharedApplication] openURL:url options:param completionHandler:^(BOOL success) {
                
            }];
        } else {
            // Fallback on earlier versions
            [[UIApplication sharedApplication] openURL:url];
        }
        
        decisionHandler(WKNavigationActionPolicyCancel);// web view取消本次
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    
}

@end

@implementation CustomWKCommandDelegate

//- (id)initWithViewController:(CDVViewController *)viewController {
//    self = [super initWithViewController:viewController];
//    if (self) {
//        self.viewController = viewController;
//    }
//    return self;
//}

- (id)getCommandInstance:(NSString *)pluginName {
    return [super getCommandInstance:pluginName];
}

// 重写资源路径的方法,进行拦截
- (NSString *)pathForResource:(NSString *)resourcepath {
//    if (<#condition#>) {
//        <#statements#>
//    }
    NSLog(@"path ---> %@", resourcepath);
    return [super pathForResource:resourcepath];
}

@end

@implementation CustomCmdQueue

- (BOOL)execute:(CDVInvokedUrlCommand *)command {
    return [super execute:command];
}

@end

这样写的原因,在于CDVViewController会自动加载CDVWKWebViewEngine并自动实现WKUIDelegate,在wkwebview的代理函数中,实现自己的业务逻辑.
适用场景:本地h5应用、html字符串、远程应用(如果较少的www文件,可采用wkwebview预加载,提升加载速度)

关于自定义插件部分:

原有插件不能满足需求,需要自定义
新建集成自CDVPLugin的原生类
修改原生类.h和.m,实现相应函数,
修改config.xml,将自定义插件以feature添加进去
js通过cordova.exec的方式调用
建议将插件以pod的方式引入,方便管理,这就涉及到Podfile语法

持续优化:

webview预加载、js\css\image离线缓存、避免无用的js引入;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容