整个移动应用框架是采用Hybrid模式构建,分为H5端和原生端。就目前适应CRM复杂又经常变动的业务,我们以H5为主,原生为辅的形式构建我们的app。H5端采用原生控件webview承接,原生端对应的平台各自研发承接。既然是由两部分构成,就需要一种框架能支持两者之间的交互。仔细研究Android和iOS这两个平台的webview控件的源码和内核,它们都支持一种叫”语言穿梭机”的机制:Android可从Java对象穿梭到JS中构造成JS对象,反之亦然;iOS可从OC对象穿梭到JS中构成JS对象,反之也亦然。只是在Android端是系统类支持的,而iOS端需要从webview内核中遍历出这个穿梭机的上下文对象,在这个穿梭机的上下文对象中注册Java对象,进而穿梭到H5中变成JS对象;
一、H5端接入步骤
1、为兼容PC需引入plugin.js
此js对原生插件API做一个PC端的API的映射,目前我们给一个基础的插件API样例JS,可根据项目的需要增加插件接口,plugin.js内容如下:
$(document).ready(function(){
mboss.noTerminal = {
error: function(){
alert('请在手机终端运行此方法');
return false;
},
returnEmptyStr: function(){
return '';
},
};
/* 框架相关方法兼容 */
if (!window.ExtendScriptPlugin){
window.ExtendScriptPlugin = {};
ExtendScriptPlugin.JN_Photograph = mboss.noTerminal.error;//拍照
/* 本地存储 */
//localStorage操作
mboss.storage = {
get: function(name){
return window.localStorage.getItem(name) || '';
},
set: function(name, value){
window.localStorage.setItem(name, value);
},
remove: function(name){
window.localStorage.removeItem(name);
},
clear:function(){
window.localStorage.clear();
}
};
ExtendScriptPlugin.JN_GetValueWithKey = mboss.storage.get;
ExtendScriptPlugin.JN_SetValueWithKey = mboss.storage.set;
ExtendScriptPlugin.JN_RemoveAllData = mboss.storage.clear;
ExtendScriptPlugin.JN_RemoveDataByKey = mboss.storage.remove;
}
});
(1)第14行的ExtendScriptPlugin就是原生插件对象;
(2)以JN_开头的API就是插件的原生API, 在plugin.js中我们做一个PC端API的映射,例如,JN_Photograph 在PC端就映射成mboss.noTerminal.error。如果应用运行在手机端,就会调用原生API:JN_Photograph。
(3)第35-38是做数据存储的函数的映射
2、在H5页面中调用原生接口
假定原生定义的JN_Photograph如下:
- (void)JN_Photograph:(NSString*)functionName;
1)非iframe页面中使用
请使用window对象引用插件对象:
window.ExtendScriptPlugin.JN_Photograph('callbackFunctionName');
2)在iframe页面中使用
请使用top对象引用插件对象:
top.ExtendScriptPlugin.JN_Photograph('callbackFunctionName');
3、js回调函数的处理
目前我们是采用给原生接口传入回调函数名的方式实现JS回调,如上面的JN_Photograph。后面我们会支持JS匿名函数的回调方式。
二、原生端接入步骤
1、iOS平台接入步骤
1)引入AIBase.framework框架包
2)定义插件接口穿梭协议
此协议继承自系统的穿梭协议JSExport。 在协议中定义插件提供给H5端调用的原生API,如图所示我们定义的插件基类的穿梭协议:
/**
* 扩展插件类
* 1、JSExport是JS与OC之间的穿梭机;
* 2、把需要扩展的API定义到protocol:JSExport里,在插件类里实现接口;
* 3、若是多参数的API,可以采用JSExportAs来声明接口,可以参考ScriptPluginBase;默认的情况,在JS端是会把参数tag首字母大写拼接成JS的function调用方式,例如:test:(NString*)str key:(NString*)k --> testKey(str,k);
* 4、注意,扩展的API,如果要操作UI的,一定要在主线程里调用。很多情况,jscontext是非主线程调用API;
*/
@protocol JN_BasePluginExport <JSExport>
// 基础的能力接口
// 1、数据存取
JSExportAs(JN_SetValueWithKey,- (void)JN_SetValueWithKey:(NSString*)key value:(NSString*)value);
- (NSString*)JN_GetValueWithKey:(NSString*)key;
- (void)JN_RemoveAllData;
- (void)JN_RemoveDataByKey:(NSString*)key;
// 2、数据安全
- (NSString*)JN_Encrypt:(NSString*)context;
- (NSString*)JN_Decrypt:(NSString*)encrypt;
- (NSString*)JN_IDMD5:(NSString*)userName;
- (NSString*)JN_Token:(NSString*)params;
JSExportAs(JN_ShowFadeMsg, -(void)JN_ShowFadeMsg:(NSString *)msg status:(int)status);
// 隐藏键盘
- (void)JN_HideKeyboard;
// 获取手机型号等信息
- (NSString *)JN_GetPhoneModule;
@end
由于OC与JS定义函数的语法不同,针对有多个入参的函数,应该采用JSExportAs做函数语法的映射,如上图的 JN_SetValueWithKey,它在JS中的定义为:
function JN_SetValueWithKey(key,value);
在OC中定义为:
- (void)JN_SetValueWithKey:(NSString*)key value:(NSString*)value;
为了后面的讲述,我们假设插件定义的穿梭协议:
@protocol JN_ExtendPluginExport <JSExport>
@end
3)继承插件的基类:AIWebViewBasePlugin
假设子类的类名为:ExtendScriptPlugin
4)实现穿梭协议
插件类需要实现穿梭协议定义的API:
@interface ExtendScriptPlugin : AIWebViewBasePlugin<JN_ExtendPluginExport>
@end
请在ExtendScriptPlugin中实现JN_ExtendPluginExport中定义的函数。注意:在实现穿梭协议的函数的时候,如果该函数有操作UI,包括H5和原生的UI元素的操作,请在UI主线程中执行这些代码 如下面的代码所示:
- (void)JN_Photograph:(NSString*)functionName {
__weak CRMMainVC *vc = (CRMMainVC*)self.vc;
dispatch_async(dispatch_get_main_queue(), ^{
[vc JN_Photograph:functionName];
});
}
上面dispatch_async就是iOS平台推荐的主线程中执行代码的标准写法,这样[vc JN_Photograph:functionName]就会在UI主线程中执行。
5)配置H5插件
为了配置文件通用性,我们iOS和android采用同一种xml格式的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<plugins>
<plugin name="ExtendScriptPlugin" class="com.ai.crmapp.ExtendScriptPlugin" packageName="com.ai.crmapp"></plugin>
</plugins>
配置中我们可以配置多个plugin。这里仅配置一个,即上面定义的ExtendScriptPlugin。对于iOS平台class和packageName两个字段是无效的。假定我们的配置文件为:h5Plugin.xml。
6)向WebView内核中注册H5插件
在我们框架中提供插件引擎类:AIWebViewPluginEngine。它目前主要两个能力:一、注册上述配置的H5插件;二、调用H5的JS 函数;
(1)注册H5插件
- (void)registerH5Engine:(UIWebView*)webView {
NSData *configData = [[AIPluginTools SharedObj] roadResWithName:@"h5Plugin.xml"];
AIWebViewPluginEngine *engine = [AIWebViewPluginEngine SharedObj];
[engine registerPluginsInVC:self webView:webView configData:configData];
}
上述代码读取插件配置文件h5Plugin.xml,然后通过通过插件引擎在webview上注册插件。
(2)通过引擎调用H5的JS
我们的回调JS函数以及原生需要调用H5的JS函数都可通过引擎调用H5的JS
dispatch_async(dispatch_get_main_queue(), ^{
NSString *js = [NSString stringWithFormat:@"%@('%@')",@"JS_SetScanResult",result];
AIWebViewPluginEngine *engine = [AIWebViewPluginEngine SharedObj];
[engine excuteJavascript:js];
});
上述对象js即我们需要调用H5中的JS。这里我们也采用在主线程中执行调用(因为这个执行的JS会操作H5的图片展示控件)
2、android平台接入步骤
1)引入框架jar包
请在android studio中引入我们的框架包 aibase.jar和wade-data.jar。
2)继承插件的基类:AIWebViewBasePlugin
假设子类的类名为,ExtendScriptPlugin:
/**
* Created by wuyoujian on 2017/5/4.
*/
public class ExtendScriptPlugin extends AIWebViewBasePlugin {
3)定义H5调用的原生接口
在Android平台定义一个H5调用的原生接口只需要做的两点:一、是public的函数;二、需要增加注解:@JavascriptInterface。如下面的一个插件函数:
// 拍照
@JavascriptInterface
@NotProguard
public void JN_Photograph(String functionName) {
final String mFunctionName = functionName;
final String checkPermissinos [] = {Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
PermissionUitls.mContext = getActivity();
if(!PermissionUitls.isGetAllPermissionsByList(checkPermissinos) ) {
new AlertDialog
.Builder(getActivity())
.setTitle("提示信息")
.setMessage("该功能需要您接受应用对一些关键权限(拍照)的申请,如之前拒绝过,可到手机系统的应用管理授权设置界面再次设置。")
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
checkPermission(mFunctionName,PermissionUitls.PERMISSION_CAMERA_CODE,checkPermissinos);
}
}).show();
} else {
jumpToRectCameraActivity(functionName);
}
}
4)配置H5插件
为了配置文件通用性,我们iOS和android采用同一种xml格式的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<plugins>
<plugin name="ExtendScriptPlugin" class="com.ai.crmapp.ExtendScriptPlugin" packageName="com.ai.crmapp"></plugin>
</plugins>
配置中我们可以配置多个plugin。这里仅配置一个,即上面定义的ExtendScriptPlugin。对于iOS平台class和packageName两个字段是无效的。假定我们的配置文件为:h5Plugin.xml。
5)向WebView内核中注册H5插件
在我们框架中提供插件引擎类:AIWebViewPluginEngine。它目前主要两个能力:一、注册上述配置的H5插件;二、调用H5的JS 函数;
(1)注册H5插件
private void setH5PluginEngine() {
AIWebViewPluginEngine_dl engine = AIWebViewPluginEngine_dl.getInstance();
engine.registerPlugins(this, mWebView, mPluginCfgFile);
}
mWebView是webview控件,mPluginCfgFile 就是上述定义的插件配置文件: h5Plugin.xml,这个配置文件请放在assets里,如图所示:
(2)通过引擎调用H5的JS
我们的回调JS函数以及原生需要调用H5的JS函数都可通过引擎调用H5的JS。
runOnUiThread(new Runnable() {
@Override
public void run() {
AIWebViewPluginEngine_dl engine = AIWebViewPluginEngine_dl.getInstance();
String JS = String.format("%s('%s','%s')","JS_LaunchApp_callback","0","");
engine.excuteJavascript(JS, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
LogUtil.d("jsMethod",value);
}
});
}
});
同理,在插件函数里如果会牵涉到操作UI元素(包括原生和H5)都需要在UI主线程中执行,如上面的runOnUiThread
三、框架提供的能力
目前iOS平台提供比较全面支撑crm的能力,android目前提供的部分能力,后面会根据iOS平台以及业务的需求两个平台能力做的一致。
1、iOS平台提供的能力
1)自定义证件拍照控件类AICaptureView
范例:
//243px x 153px
CGFloat width = 200 * 243 / 153;
CGRect markFrame = CGRectMake((self.view.frame.size.width - width)/2.0, 184, width, 200);
AICaptureView *captureView = [[AICaptureView alloc] initWithFrame:self.view.bounds];
self.captureView = captureView;
[_captureView cropRectForInterest:markFrame];
[self.view addSubview:captureView];
上述cropRectForInterest设置证件拍照的区域框。
2)手写控件类SignatureView
控件中提供了画笔的基本的操作:笔的大小、笔的颜色、撤销一笔、重做一笔、清除所有笔画、保存签名图片。
范例:
__weak SignatureViewController *wSelf = self;
self.signView = [[SignatureView alloc] initWithFrame:CGRectMake(0, [DeviceInfo navigationBarHeight], self.view.frame.size.width, [DeviceInfo screenHeight] - [DeviceInfo navigationBarHeight] - 44) status:^(SigntureStatus status) {
//
SignatureViewController *sSelf = wSelf;
if (status == SignatureStatusBegin) {
[sSelf.undoItem setEnabled:YES];
}
}];
[_signView setPenSize:3.0];
[self.view addSubview:_signView];
3)手势密码控件类AIGesturePasswordView
范例:
// 创建控件
_passwordView = [[AIGesturePasswordView alloc] initWithFrame:CGRectMake(0, 120, self.view.frame.size.width, self.view.frame.size.height - 60 - 120)];
[_passwordView setDelegate:self];
[_passwordView setDefaultColor:[UIColor blackColor]];
[_passwordView setPathColor:[UIColor colorWithHex:0x378FC9]];
[_passwordView setSelectColor:[UIColor colorWithHex:0x378FC9]];
[_passwordView setWrongColor:[UIColor redColor]];
[self.view addSubview:_passwordView];
// 实现手势控件回调
#pragma mark - AIGesturePasswordViewDelegate
// 通过手势设置密码
- (void)gesturePasswordView:(AIGesturePasswordView*)sender password:(NSString*)password {
}
// 密码个数小于4位,无效
- (void)gestureInvalidPasswordView:(AIGesturePasswordView*)sender {
}
4)扫码控件类QRViewController
范例:
QRViewController *qrController = [[QRViewController alloc] init];
[picker presentViewController:qrController animated:YES completion:^{
//
[qrController startScanQRCodeWithFinish:^(NSString *result, QRCodeScanStatus status) {
//
[picker dismissViewControllerAnimated:YES completion:^{}];
ImagePickerController *sSelf = wSelf;
if (sSelf.finishBlock) {
sSelf.finishBlock(ImagePickerTypeScanQRCode,ImagePickerStatusSuccess,result);
}
}];
}];
QRViewController是一个ViewController控制器,所以页面的启动可以根据需要用push还是用modal方式,上图实例中是采用的modal方式;startScanQRCodeWithFinish启动扫描,通过block的方式回调传回扫描的结果。
5)二维码图片识别和生成
是通过给UIImage做一个类别扩展,方法定义如下:
// 生成二维码
+ (UIImage *)generateQRCode:(NSString *)code width:(CGFloat)width height:(CGFloat)height;
// 二维码识别
- (NSArray *)recognitionQRCodeFromImage;
6)验证码控件类CaptchaControl
范例:
CaptchaControl *codeCtrl = [[CaptchaControl alloc] initWithFrame:CGRectMake(40, 100, 90, 30) interval:60];
[codeCtrl setBackgroundColor:[UIColor lightTextColor]];
[codeCtrl addTarget:self action:@selector(getCode:) forControlEvents:UIControlEventTouchUpInside];
[codeCtrl setDefaultText:@"验证码"];
[self.view addSubview:codeCtrl];
7)循环轮播控件CycleBannerView
范例:
CycleBannerView *banner = [[CycleBannerView alloc] initWithFrame:view.bounds];
NSArray *images = @[@"http://pic22.nipic.com/20120717/9774499_115645635000_2.jpg",
@"http://pic4.nipic.com/20090919/3372381_123043464790_2.jpg",
@"http://www.9doo.net/__demo/jd0024/upload/b1.jpg",
@"http://pic.58pic.com/58pic/13/18/50/23K58PIC38v_1024.jpg",
@"http://pic2.ooopic.com/10/57/50/93b1OOOPIC4d.jpg"];
[banner reloadData:images];
[banner setPageControlPos:PageControlPositionRight];
[banner autoScroll];
[view addSubview:banner];
可设置pageControl的位置;也可设置是否是自动轮播,默认不自动轮播。
8)自消失的提示框控件FadePromptView
(1)范例-默认位置弹出
[FadePromptView showPromptStatus:@"当前设置暂时没有办法发送邮件" duration:1.0 finishBlock:nil];
(2)范例-指定位置弹出
[FadePromptView showPromptStatus:@"请输入您的笔记~" duration:0.6 positionY:[DeviceInfo screenHeight]- 300 finishBlock:^{
//
}];
上述两个范例都是可以支持提示框消失后的回调。
9)九宫格控件GridMenuView
self.gridView = [[GridMenuView alloc] initWithFrame:CGRectMake(0, [DeviceInfo navigationBarHeight], [DeviceInfo screenWidth], [DeviceInfo screenHeight] - [DeviceInfo navigationBarHeight])];
[_gridView setDelegate:self];
[_gridView setColumnCount:3];
[_gridView setRowHeight:120];
[self.view addSubview:_gridView];
[_gridView appendingMenusData:[self gridData]];
- (NSArray *)gridData {
GridMenuItem *item1 = [[GridMenuItem alloc] init];
item1.icon = @"http://ico.58pic.com/iconset01/Doraemon-icons/gif/79802.gif";
item1.title = @"菜单1";
item1.iconSize = CGSizeMake(60, 60);
item1.titleFont = [UIFont systemFontOfSize:14];
item1.titleColor = [UIColor grayColor];
GridMenuItem *item2 = [[GridMenuItem alloc] init];
item2.icon = @"http://ico.58pic.com/iconset02/fast_icon_users/gif/13406.gif";
item2.title = @"菜单2";
item2.iconSize = CGSizeMake(60, 60);
item2.titleFont = [UIFont systemFontOfSize:14];
item2.titleColor = [UIColor grayColor];
GridMenuItem *item3 = [[GridMenuItem alloc] init];
item3.icon = @"http://ico.58pic.com/iconset01/Doraemon-icons/gif/79792.gif";
item3.title = @"菜单3";
item3.iconSize = CGSizeMake(60, 60);
item3.titleFont = [UIFont systemFontOfSize:14];
item3.titleColor = [UIColor grayColor];
NSArray *menus = @[item1,item2,item3];
return menus;
}
此九宫格布局,如下图所示:
10)icon菜单控件AIActionSheet
范例:
AIActionSheet *sheet = [[AIActionSheet alloc] initInParentView:self.tabBarController.view delegate:self];
for (int i = 0; i < 7; i ++) {
AISheetItem * item = [[AISheetItem alloc] init];
item.icon = @"capture.png";
item.title = [NSString stringWithFormat:@"测试测-%d",i];
[sheet addActionItem:item];
}
[sheet show];
它所展示的效果如下:
11)图片视频选择器类ImagePickerController
这个类集成了图片的选择,系统的拍照、系统的录像、二维码识别等能力。使用简单请参考.h里的接口定义即可
12)邮件和短信发送器类MailSMSController
这个类集成了邮件和短信的发送,使用简单请参考.h里的接口定义即可。
13)HTTP通信及数据model一体化封装
通信引擎类NetworkTask,它封装了常用的http通信API:文件上传、GET、POST、PUT、DELETE等等,它需要结合数据model类配套使用。
数据model基类NetResultBase。
范例:
// 1、接口调用
NSDictionary* param =[[NSDictionary alloc] initWithObjectsAndKeys:
nameString,@"phone",
[pwdString md5EncodeUpper:NO],@"passwd",nil];
[SVProgressHUD showWithStatus:@"正在登录..." maskType:SVProgressHUDMaskTypeBlack];
[[NetworkTask sharedNetworkTask] startPOSTTaskApi:API_Login
forParam:param
delegate:self
resultObj:[[LoginResult alloc] init]
customInfo:@"login"];
// 2、回调实现
#pragma mark - NetworkTaskDelegate
-(void)netResultSuccessBack:(NetResultBase *)result forInfo:(id)customInfo {
[SVProgressHUD dismiss];
if ([customInfo isEqualToString:@"login"]) {
AppDelegate *app = [AppDelegate shareMyApplication];
[app.mainVC switchToHomeVC];
LoginResult *userInfo = (LoginResult *)result;
[[SysDataSaver SharedSaver] saveUserInfo:userInfo];
}
}
-(void)netResultFailBack:(NSString *)errorDesc errorCode:(NSInteger)errorCode forInfo:(id)customInfo {
[SVProgressHUD dismiss];
[FadePromptView showPromptStatus:errorDesc duration:1.0 finishBlock:^{
//
}];
}
// 实现配套的数据model类
//
// LoginResult.h
//
// Created by wuyj on 16/12/14.
// Copyright © 2016年 Asiainfo. All rights reserved.
//
#import "NetResultBase.h"
@interface LoginResult : NetResultBase<NSCoding>
@property (nonatomic, copy)NSString *nick;
@property (nonatomic, copy)NSString *avatar;
@property (nonatomic, copy)NSString *favorCount;
@property (nonatomic, copy)NSString *noteCount;
@property (nonatomic, copy)NSString *user_id;
@property (nonatomic, copy)NSString *phone;
@property (nonatomic, copy)NSString *mood;
@end
注意对于数据model对象,如果数据字段是Array类型,请使用宏定义定义,如下:
@property(nonatomic,strong,getter=arrayLeibie)NSArray *BaiduParserArray(leibie,NSString);
上述宏定义:BaiduParserArray,里面有两个参数第一参数:leibie是Array字段名称;NSString是Array的item的类型;当然也可以使用自定义的类,如下:
@property(nonatomic,strong,getter=arrayBook)NSArray *BaiduParserArray(books,BookItem);
上述Array字段名books,Array的item的类型:BookItem,定义如下:
@interface BookItem : NSObject
@property (nonatomic, copy)NSString *id;
@property (nonatomic, copy)NSString *name;
@property (nonatomic, copy)NSString *pic_big;
@property (nonatomic, copy)NSString *pic_small;
@property (nonatomic, copy)NSString *author;
@property (nonatomic, copy)NSString *range;
@property (nonatomic, copy)NSString *type;
@property (nonatomic, copy)NSString *f_age;
@property (nonatomic, copy)NSString *press;
@property (nonatomic, copy)NSString *isbn;
@property (nonatomic, copy)NSString *recommend;
@property (nonatomic, copy)NSString *link;
@property (nonatomic, copy)NSString *comment;
@property (nonatomic, copy)NSString *status;
@property (nonatomic, copy)NSString *created_date;
@property (nonatomic, copy)NSString *created;
@property (nonatomic, copy)NSString *isOnline;
@property (nonatomic, copy)NSString *isFavor;
@property (nonatomic, copy)NSString *price;
@property (nonatomic, copy)NSString *statement;
@property (nonatomic, copy)NSString *brief;
@property (nonatomic, copy)NSString *introduction;
@property (nonatomic, copy)NSString *pic_intr;
@property (nonatomic, copy)NSString *pic_jj;
@end
13)独立的分享框架
分享能力我们目前是提供一个独立的framework,目前集成了3个常用的分享平台: 微信、QQ、微博;
如果需要使用分享能力,请引入我们框架的AIShare.framework框架包。这个框架中主要实现了3大平台的分享能力,同时集成了支付能力。
主要提供的类如下图所示:
(1)SharedDataModel,分享的数据类;
(2)SharedManager,分享引擎类;
使用范例:
SharedDataModel *sharedData = [[SharedDataModel alloc] init];
sharedData.content = @"测试URL分享";
sharedData.title = @"测试视频分享";
sharedData.url = @"http://www.tudou.com/programs/view/_cVM3aAp270/";
sharedData.dataType = SharedDataTypeText;
[[SharedManager sharedManager] sharedData:sharedData finish:^(NSInteger statusCode,id resp) {
//
}];
14)数据存储、处理集合
(1)keychain存储类:AISave2Keychain
(2)剪贴板存储类:AIPasteboard
(3)基于系统的UserDefault存储类:SysDataSaver
(4)文件存储类:FileCache
(5)文件流操作类:FileStreamOperation
(6)图片处理类别:UIImage+Utility和UIImage+ResizeMagick
(7)颜色处理类别:UIColor+Utility
(8)字符串处理类别:NSString+Utility
(9)数据流处理类别:NSData+Crypto
(10)zip解压缩:ZipArchiveEx
14)framework动态加载框架
这个框架主要是封装framework的动态加载和资源从framework的自己的bundle中加载的能力。这封装主要解决就是iOS平台实现插件机制。
主要掌握下面两个类的使用,请参考.h文件即可:
(1)AIPluginLoader类:framework插件加载器。
//
// AIPluginLoader.h
// CommonProject
//
// Created by wuyoujian on 2017/6/19.
//
//
#import <Foundation/Foundation.h>
#import "../Owns/AINavigationController.h"
#import "../Owns/AICommonDef.h"
#import "AIPluginProtocol.h"
typedef NS_ENUM(NSInteger,PluginLoadStatus) {
PluginLoadStatusNone = 0,
PluginLoadStatusUnDownload = 1, // 没有下载
PluginLoadStatusDownloading = 2, // 下载中
PluginLoadStatusDownloaded = 3, // 下载完毕
PluginLoadStatusException = 4, // 加载异常
};
@interface AIPluginLoader : NSObject
AISINGLETON_DEF(AIPluginLoader,SharedObj);
// 获取插件的状态
- (PluginLoadStatus)pluginloadStatus:(NSString*)pluginURL;
// 下载插件
- (void)downloadPlugin:(NSString*)pluginURL;
// 加载插件
- (void)loadPluginName:(NSString*)frameworkName
nav:(AINavigationController*)navVC
enterVCClassName:(NSString*)className;
// 加载插件
- (UIViewController<AIPluginProtocol> *)loadPluginName:(NSString*)frameworkName enterVCClassName:(NSString*)className;
@end
(2)AIPluginTools:framework资源加载工具类
//
// AIPluginTools.h
// AIBase
//
// Created by wuyoujian on 2017/6/21.
// Copyright © 2017年 Asiainfo. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "../Owns/AICommonDef.h"
@interface AIPluginTools : NSObject
AISINGLETON_DEF(AIPluginTools,SharedObj);
// 从framework根目录下加载图片
- (UIImage *)roadImageWithName:(NSString *)name;
// 从framework根目录下的包名:@param bunleName 中加载图片,例如,res.bundle
- (UIImage *)roadImageWithName:(NSString *)name inResBundle:(NSString*)bundleName;
// 从矢量图加载图片
- (UIImage *)roadVectorImageWithName:(NSString *)name;
// 从framework根目录下加载其他资源
- (NSData *)roadResWithName:(NSString*)name;
// 从framework根目录下的包名:@param bunleName 中加载其他资源,例如,res.bundle
- (NSData *)roadResWithName:(NSString*)name inResBundle:(NSString *)bundleName;
// 返回资源路径
- (NSString *)resPathWithName:(NSString*)name;
// 返回资源路径
- (NSString *)resPathWithName:(NSString*)name inResBundle:(NSString *)bundleName;
@end
iOS平台的能力待续。。。
2、android平台提供的能力
目前android平台的能力提供仅满足crm常用的能力,后面陆续新增能力,在封装android能力的时候,我们是全部采用代码来实现控件,资源文件能实现编码的采用编码。
1)自定义证件拍照控件类RectCameraActivity
使用范例:
// 拍照
@JavascriptInterface
@NotProguard
public void JN_Photograph(String functionName) {
final String mFunctionName = functionName;
final String checkPermissinos [] = {Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
PermissionUitls.mContext = getActivity();
if(!PermissionUitls.isGetAllPermissionsByList(checkPermissinos) ) {
new AlertDialog
.Builder(getActivity())
.setTitle("提示信息")
.setMessage("该功能需要您接受应用对一些关键权限(拍照)的申请,如之前拒绝过,可到手机系统的应用管理授权设置界面再次设置。")
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
checkPermission(mFunctionName,PermissionUitls.PERMISSION_CAMERA_CODE,checkPermissinos);
}
}).show();
} else {
jumpToRectCameraActivity(functionName);
}
}
private void jumpToRectCameraActivity(String functionName) {
Intent intent = new Intent(getActivity(), RectCameraActivity.class);
intent.putExtra("functionName",functionName);
intent.putExtra("fileId","fileId");
getActivity().startActivityForResult(intent, 101);
}
2)手写控件类AISignatureView
它是一个标准的View类,可以类似于系统的View一样的使用方式;他提供保存手写图片、获取手写图片的Bitmap对象、删除保存的图片、设置笔的粗细、设置画布背景、设置画笔颜色等等能力;
3)手势密码控件类AIGesturePasswordLayout
它是一个标准的View类,用法同系统的View,下面我们给一份使用纯代码布局的范例:
mGesturePasswordLayout = new AIGesturePasswordLayout(this);
mGesturePasswordLayout.setGravity(Gravity.CENTER_VERTICAL);
mGesturePasswordLayout.setBackgroundColor(0x00ffffff);
mGesturePasswordLayout.setOnGestureLockViewListener(mListener);
//
tvParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
mRelativeLayout.addView(mGesturePasswordLayout,tvParams);
4)扫码控件类CaptureActivity
这个控件是基于ZXing开源库来封装的。
使用范例:
private void ToScanCode2(int type) {
//跳转扫描页面
Intent intent = new Intent(getActivity(), CaptureActivity.class);
intent.putExtra("type",type);
getActivity().startActivityForResult(intent,SCAN_BAR_CODE_REQUEST_CODE);
}