Skip to content

新增Bridge

xuwhale6 edited this page Jan 19, 2020 · 18 revisions

Bridge分类

根据是否需要创建实例分类(面向对象分类):

  • 实例类型,需要在业务层创建对象,Lua可GC,虚拟机销毁时释放所有未释放的实例
  • 静态类型,和原生静态方法类似,不需要创建对象,和虚拟机共存亡
  • 枚举类型,主要是字符串或数字枚举

根据Lua调用方式分类:

  • 实例调用型: object = BridgeName() object:methodName()
  • 静态调用型: BridgeName:methodName()
  • 枚举调用型: BridgeName.name

Android原生Bridge编写

Android桥接步骤分三步:
1.新建桥接类
2.编写桥接方法
3.在Application中通过MLSEngine注册

Android Bridge特殊说明:

  • 静态调用型,可分为两类,一类为单例,一类为静态方法。
  • 单例:即有状态(虚拟机销毁时通知),调用和静态类型相同(不需要创建对象)

1. 导入MLN压缩包

MLN模板压缩包解压后的MLN文件夹放入Android Studio安装目录/Contents/plugins/android/lib/templates文件夹下中(注:如果安装多个版本的studio,需要将压缩包放入使用版本的目录下),重启Android Studio。在New选项中,将会看到MLN选项,根据需要做相应选择。建议优先选择带(Simple)标记的构建方法。 如图:

2. 创建实例类型Bridge

建议使用模板,能通过提示或注释快速创建Bridge,下面以SingleInstance为例讲解如何新建Bridge类和方法

1)在工程下选择一个目录->new->MLN->SingleInstance,Java Class Name命名为UDTestBridge,Lua Class Name命名为Toast,添加Bridge方法:

@LuaBridge
public void toast(String text) {
    // 调用该方法时会弹出吐司
    Toast.makeText(MLSEngine.getContext(), text, Toast.LENGTH_SHORT);
}

2)注册新建的UDTestBridge类,可以在Application中调用如下代码:

MLSEngine.registerUD(Register.newUDHolderWithLuaClass(UDTestBridge.LUA_CLASS_NAME, UDTestBridge.class, true))

3)在lua中调用桥接的toast方法

local obj = Toast()
window:onClick(function ()
    obj:toast("新建bridge")
end)

4)查看结果,调用toast方法后,点击屏幕会弹出吐司:"新建bridge"(创建的应用需要手动获取存储权限)

⚠️MLN模板中Enum、Userdata、Static Bridge、SingleInstance、LuaView五种类型的注册方式在模板顶部注释里有说明,可在Application中onCreate方法里注册,参考代码如下图所示。

MLSEngine.init(this, BuildConfig.DEBUG)
                .setLVConfig(new LVConfigBuilder(this)
                        .setRootDir(SD_CARD_PATH)    //set lua root directory
                        .setImageDir(SD_CARD_PATH + "image")  // set lua picture root directory
                        .setCacheDir(SD_CARD_PATH + "cache")  // set lua cache directory
                        .setGlobalResourceDir(SD_CARD_PATH + "g_res") // set the resource file directory
                        .build())
                .setImageProvider(new GlideImageProvider()) // lua loading image tool, if it is not implemented, the image cannot be displayed
                .registerSingleInsance(new MLSBuilder.SIHolder(UDTestBridge1.LUA_CLASS_NAME, UDTestBridge1.class))
                .registerSingleInsance(new MLSBuilder.SIHolder(UDTestBridge.LUA_CLASS_NAME, UDTestBridge.class))
                .registerConstants(TestEnum.class) // enum in lua
                .registerUD(Register.newUDHolderWithLuaClass(TestUserDataBridge.LUA_CLASS_NAME, TestUserDataBridge.class, true)) // register the Userdata class in lua
                .registerSC(Register.newSHolderWithLuaClass(TestStaticBridge.LUA_CLASS_NAME, TestStaticBridge.class)) // register the static tool class in Lua, that is StaticBridge
                .registerSingleInsance(new MLSBuilder.SIHolder(TestSingleInstance.LUA_CLASS_NAME, TestSingleInstance.class)) // register singleton in lua
                .registerUD(Register.newUDHolder(TestLuaView.LUA_CLASS_NAME, TestLuaView.class, true, TestLuaView.methods)) // LuaView registration method
                .build(true);

iOS原生Bridge编写

iOS桥接步骤分三步:
1.新建桥接类
2.编写桥接方法
3.注册到Instance中

⚠️ !!!Bridge扩展目前不支持Swift,Bridge需要使用Object-C编写。

一. 桥接View

1. 新建一个MLNTestView类,继承自MLNView,遵循协议MLNEntityExportProtocol,代码如下

#import <UIKit/UIKit.h>
#import "MLNEntityExportProtocol.h"
#import "MLNView.h"

NS_ASSUME_NONNULL_BEGIN

@interface MLNTestView : MLNView <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

2. 引入头文件,进行桥接

#import "MLNTestView.h"
#import "MLNViewExporterMacro.h"
#import "UIView+MLNKit.h"

@interface MLNTestView()
@property (nonatomic, copy) NSString *text;
@end

@implementation MLNTestView

//将虚拟机与注册类绑定
- (instancetype)initWithLuaCore:(MLNLuaCore *)luaCore frame:(CGRect)frame
{
    if (self = [super initWithLuaCore:luaCore frame:frame]) {
    }
    return self;
}

//这是一个测试桥接方法,展示一个Label到TestView上
- (void)lua_showLabel
{
    UILabel *label = [[UILabel alloc] initWithFrame:self.bounds];
    [self addSubview:label];
    label.text = _text;
}

#pragma mark - Export To Lua
/**
 导出View开始
 
 @param CLZ 类名 (例:NSObject)
 */
LUA_EXPORT_VIEW_BEGIN(MLNTestView)
/**
 导出属性方法映射
 
 @param LUA_FUNC Lua中的方法名称
 @param SETTER_NAME 原生View属性的setter方法
 @param GETTER_NAME 原生View属性的getter方法
 @param CLZ 原生类名称
 */
LUA_EXPORT_VIEW_PROPERTY(text, "setText:", "text", MLNTestView)
/**
 导出方法映射
 
 @param LUA_FUNC Lua中的方法名称
 @param SEL_NAME 原生View的对象方法名称
 @param CLZ 原生类名称
 */
LUA_EXPORT_VIEW_METHOD(show, "lua_showLabel", MLNTestView)

/** 
 标记完成View UserData类导出

 @note ⚠️如果需要自定义初始化方法,第一个参数必须是MLNLuaCore。
 @param CLZ 原生类名称
 @param LUA_CLZ Lua中的类名称
 @param HAS_SUPER 是否有父类 (YES/NO),这是在Lua中的继承关系,并非原生的继承关系
 @param SUPERCLZ 父类的原生类名字,可以没有原生的继承关系。
 @param CONSTRUCTOR_NAME 构造器方法,默认为”initWithLuaCore:“。
**/
LUA_EXPORT_VIEW_END(MLNTestView, TestView, YES, "MLNView", "initWithLuaCore:frame:")
@end

3. 注册及使用

简单的使用场景,在创建Lua控制器的时候,可以注册一组桥接类

  1. 新建一个demo.lua文件如下,点击TestView的时候,展示一个Label到视图上
testView = TestView():marginTop(120):width(300):height(300)
testView:bgColor(Color(211,211,211, 1.0))
testView:text("哈哈哈哈哈")

local text  = testView:text()
print("text:", text)

window:addView(testView)
--点击,展示Label
testView:onClick(function()
    testView:show()
end)
  1. 创建MLN控制器,注册桥接类,push后展示demo
//  在默认的包含MLNKitInstance的视图控制器中注册桥接类,可通过加载本地lua进行测试
    MLNKitViewController *viewController = [[MLNKitViewController alloc] initWithEntryFilePath:@"demo.lua"];
    [viewController regClasses:@[[MLNTestView class]]];
    [self.navigationController pushViewController:viewController animated:YES];

//or 在热重载控制器中注册桥接类,采用HotReload方式进行测试

    MLNHotReloadViewController *hotVC = [[MLNHotReloadViewController alloc] initWithRegisterClasses:@[[MLNTestView class]] extraInfo:nil navigationBarTransparent:YES];
    [self.navigationController pushViewController:hotVC animated:YES];

二. 桥接OC对象

1.新建一个MLNTestObject类,继承自NSObject,遵循协议MLNEntityExportProtocol,代码如下:

#import <Foundation/Foundation.h>
#import <MLNCore.h>

NS_ASSUME_NONNULL_BEGIN

@interface MLNTestObject : NSObject <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.桥接实例属性与方法

#import "MLNTestObject.h"
#import "MLNBlock.h"

@interface MLNTestObject()

@property (nonatomic, copy) NSString *message;

@end

@implementation MLNTestObject

#pragma mark - 测试接收数据
- (void)testReceive:(NSString *)msg
{
    _message = msg;
    NSLog(@"收到了:%@",msg);
}

#pragma mark - 测试接返回数据
- (NSInteger)testAdd:(NSInteger)numberA numberB:(NSInteger)numberB
{
    return numberA + numberB;
}

#pragma mark - 测试回调数据
- (void)testAddWithCallback:(NSInteger)numberA numberB:(NSInteger)numberB callback:(MLNBlock *)callback
{
    if (callback) {
        //添加参数
        [callback addIntegerArgument:numberA + numberB];
        //回调给Lua
        [callback callIfCan];
    }
}

#pragma mark - 注册方法到lua中
LUA_EXPORT_BEGIN(MLNTestObject)
LUA_EXPORT_PROPERTY(message, "setMessage:", "message", MLNTestObject)
LUA_EXPORT_METHOD(testReceive, "testReceive:", MLNTestObject)
LUA_EXPORT_METHOD(testAdd, "testAdd:numberB:", MLNTestObject)
LUA_EXPORT_METHOD(testAddWithCallback, "testAddWithCallback:numberB:callback:", MLNTestObject)
LUA_EXPORT_END(MLNTestObject, TestObject, NO, NULL, NULL)

@end

3.注册并测试

  1. 注册到Lua中
 [viewController regClasses:@[[MLNTestObject class]]];
  1. Lua测试代码
test = TestObject()
--发送数据到原生
test:testReceive("哈哈哈哈哈,你好呀")
--读取原生收到并记录的值
print("message:", test:message())
--调用原生加法,返回值方式
print("11 + 22 = ", test:testAdd(11,22))
test:testAddWithCallback(11, 22, function(sum)
--异步获取计算结果
print("callback sum:", sum)
end)

三. 静态类方法

1.新建一个MLNStaticObject类,继承自NSObject,遵循协议MLNStaticExportProtocol,代码如下:

#import <Foundation/Foundation.h>
#import <MLNCore.h>

NS_ASSUME_NONNULL_BEGIN

@interface MLNStaticObject : NSObject <MLNStaticExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.桥接静态类方法

#import "MLNStaticObject.h"

@implementation MLNStaticObject

+ (void)test:(NSString *)msg
{
    NSLog(@"static test: %@", msg);
}

LUA_EXPORT_STATIC_BEGIN(MLNStaticObject)
LUA_EXPORT_STATIC_METHOD(test, "test:", MLNStaticObject)
LUA_EXPORT_STATIC_END(MLNStaticObject, StaticObject, NO, NULL)

@end

3.注册并测试

  1. 注册
    MLNLuaPageViewController *viewController = [[MLNLuaPageViewController alloc] initWithEntryFilePath:entryFile];
    [viewController regClasses:@[[MLNStaticObject class]]];
  1. 测试
StaticObject:test("测试静态类方法")

四. 初始化参数个数不定的对象

桥接的对象有某些情况下,有不同参数个数的场景,此时需要有一个处理多参数的能力,下面介绍一下如何桥接这种类型的对象。

1.创建一个类MLNMultiParamObject,继承自NSObject,遵循协议MLNEntityExportProtocol,代码如下:

#import <Foundation/Foundation.h>
#import "MLNEntityExportProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface MLNMultiParamObject : NSObject <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.根据参数个数进行初始化

#import "MLNMultiParamObject.h"
#import "MLNLuaCore.h"
#import "MLNKitHeader.h"

@interface MLNMultiParamObject()
@property (nonatomic, assign) NSInteger a;
@property (nonatomic, assign) NSInteger b;
@property (nonatomic, assign) NSInteger c;
@end

@implementation MLNMultiParamObject

- (instancetype)initWith:(NSInteger)a b:(NSInteger)b c:(NSInteger)c
{
    if (self = [super init]) {
        _a = a;
        _b = b;
        _c = c;
    }
    return self;
}

static int lua_multi_param_init(lua_State *L) {
    MLNMultiParamObject *object = nil;
    NSUInteger argCount = lua_gettop(L);
    switch (argCount) {
        case 3: {
            NSUInteger a = lua_tonumber(L, 1);
            NSUInteger b = lua_tonumber(L, 2);
            NSUInteger c = lua_tonumber(L, 3);
            object = [[MLNMultiParamObject alloc] initWith:a b:b c:c];
        }
            break;
        case 2: {
            NSUInteger a = lua_tonumber(L, 1);
            NSUInteger b = lua_tonumber(L, 2);
            object = [[MLNMultiParamObject alloc] initWith:a b:b c:-1];
        }
            break;
        case 1: {
            NSUInteger a = lua_tonumber(L, 1);
            object = [[MLNMultiParamObject alloc] initWith:a b:-1 c:-1];
        }
            break;
        default: {
            object = [[MLNMultiParamObject alloc] init];
            object.a = -1;
            object.b = -1;
            object.c = -1;
            break;
        }
    }
    
    if (object) {
        // 标记为Lua创建
        object.mln_isLuaObject = YES;
        [MLN_LUA_CORE(L) pushNativeObject:object error:NULL];
        return 1;
    }
    return 0;
}

- (void)lua_descript
{
    NSLog(@"a:%ld, b:%ld, c:%ld", _a, _b, _c);
}

#pragma mark - Export To Lua
LUA_EXPORT_BEGIN(MLNMultiParamObject)
LUA_EXPORT_METHOD(descript, "lua_descript", MLNTestObject)
LUA_EXPORT_END_WITH_CFUNC(MLNMultiParamObject, MultiParamObject, NO, NULL, lua_multi_param_init)
@end

3.注册并测试

  1. 注册
    MLNLuaPageViewController *viewController = [[MLNLuaPageViewController alloc] initWithEntryFilePath:entryFile];
    [viewController regClasses:@[[MLNMultiParamObject class]]];
  1. 测试
multi1 =  MultiParamObject(1,2,3)
multi2 =  MultiParamObject(1,2)
multi3 =  MultiParamObject(1)
multi4 =  MultiParamObject()

multi1:descript()
multi2:descript()
multi3:descript()
multi4:descript()
Clone this wiki locally