Skip to content

Create Bridges

xuwhale6 edited this page Jan 19, 2020 · 2 revisions

Bridge types

Classify bridges according to whether they need to create instances(object-oriented classification):

  • Instance Bridges: Creating instance bridges needs to create objects in the business layer, and will release all unreleased instances when the virtual machine is destroyed. In addition, lua can be GC.
  • Static Bridges: Static bridges which are similar to native static methods do not need to create objects, and coexist with the virtual machine.
  • Enumeration Bridges: Enumeration bridges are mainly strings or numbers.

Classify bridges according to Lua calling mode:

  • Instance calling type: object = BridgeName() object:methodName()
  • Static calling type: BridgeName:methodName()
  • Enumerated calling type: BridgeName.name

1. Written by Android Native Bridge

Android bridging steps are divided into three steps:  
(i) New bridge class  
(ii) Write bridge methods  
(iii) Register in Application via MLSEngine 

Android Bridge special instructions

  • Static call types can be divided into two types, one is a singleton, and the other is a static method.
  • Singleton: stateful (notify when the virtual machine is destroyed), the call is similar to static type (no need to create an object)

1.1 Import MLN archive

Extract the MLN template and put the MLN folder into the Android Studio installation directory / Contents / plugins / android / lib / templates, then to restart Android Studio. In the New option, you will see the MLN option and make the appropriate selections as needed. It is recommended to prefer the construction method with the (Simple) mark. As shown in the figure:

1.2 Create instance bridge

It is recommended to use a template to quickly create a bridge through prompts or comments. The following uses SingleInstance as an example to explain how to create a new Bridge class and method.

(i) Select a directory under the project, and right-click to create a new SingleInstance class. The Java Class Name is named UDTestBridge and the Lua Class Name is named Toast. The method for adding a Bridge is as follows:

@LuaBridge
// Toast will pop up when calling this method
public void toast(String text) {
    Toast.makeText(MLSEngine.getContext(), text, Toast.LENGTH_SHORT);
}

(ii) Register new UDTestBridge class, you can call the following code in the Application.

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

(iii) Call bridging method in lua in toast.

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

(iv) View the results. After calling the toast method, tap the screen and the toast will pop up: "new bridge" (created applications need to manually obtain storage permissions).
⚠️The five types of registration methods of Enum, Userdata, Static Bridge, SingleInstance, and LuaView in the MLN template are explained in the comment at the top of the template and they can be registered in the onCreate method of Application. The reference code is shown in the following figure.

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);

2. Written by iOS Native Bridge

The iOS bridging stes are divided into three steps:
1. New bridge class   
2. Write bridge methods  
3. Register to Instance  

⚠️ !!!The Bridge extension does not currently support Swift. Bridge needs to be written in Object-C.

2.1 Bridge View

(i) Create a new MLNTestView class, inheriting from MLNView, following the protocol MLNEntityExportProtocol, and the code is as follows.

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

NS_ASSUME_NONNULL_BEGIN

@interface MLNTestView : MLNView <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

(ii) Introduce header files for bridging

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

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

@implementation MLNTestView

// bind the virtual machine to the registration class
- (instancetype)initWithLuaCore:(MLNLuaCore *)luaCore frame:(CGRect)frame
{
    if (self = [super initWithLuaCore:luaCore frame:frame]) {
    }
    return self;
}

//this is a test bridging method, showing a Label to TestView
- (void)lua_showLabel
{
    UILabel *label = [[UILabel alloc] initWithFrame:self.bounds];
    [self addSubview:label];
    label.text = _text;
}

#pragma mark - Export To Lua
/**
 Export View 
 
 @param CLZ 类名 (例:NSObject)
 */
LUA_EXPORT_VIEW_BEGIN(MLNTestView)
/**
 export attribute method mapping
 
 @param LUA_FUNC Methods' names in Lua
 @param SETTER_NAME setter methods for native View properties
 @param GETTER_NAME getter methods for native View properties
 @param CLZ native class name
 */
LUA_EXPORT_VIEW_PROPERTY(text, "setText:", "text", MLNTestView)
/**
 export method mapping
 
 @param LUA_FUNC methods' names in Lua
 @param SEL_NAME object method name of native View
 @param CLZ native class name
 */
LUA_EXPORT_VIEW_METHOD(show, "lua_showLabel", MLNTestView)

/** 
 mark the View UserData class 

 @note ⚠️If a custom initialization method is required, the first parameter must be MLNLuaCore.
 @param CLZ native class name
 @param LUA_CLZ class names in Lua
 @param HAS_SUPER Is there a parent class (YES / NO), this is the inheritance relationship in Lua, not the native inheritance relationship
 @param SUPERCLZ The parent class's native class name can have no native inheritance relationship.
 @param CONSTRUCTOR_NAME Constructor method, default is "initWithLuaCore:".
**/
LUA_EXPORT_VIEW_END(MLNTestView, TestView, YES, "MLNView", "initWithLuaCore:frame:")
@end

(iii) Registration and use
Simple usage scenario, when creating a Lua controller, you can register a set of bridge classes.

  • Create a new demo.lua file as follows. When you click TestView, a Label is displayed on the view.
testView = TestView():marginTop(120):width(300):height(300)
testView:bgColor(Color(211,211,211, 1.0))
testView:text("Hahahahaha")

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

window:addView(testView)
--click to display Label
testView:onClick(function()
    testView:show()
end)
  • Create the MLN controller, register the bridge class, and display the demo after push.
//  register the bridge class in the default view controller containing MLNKitInstance, which can be tested by loading the local lua
    MLNKitViewController *viewController = [[MLNKitViewController alloc] initWithEntryFilePath:@"demo.lua"];
    [viewController regClasses:@[[MLNTestView class]]];
    [self.navigationController pushViewController:viewController animated:YES];

//or register the bridge class in the hot reload controller and test it with HotReload

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

2.2 Bridge OC Object

1.Create a new MLNTestObject class, inheriting from NSObject, and following the protocol MLNEntityExportProtocol, the code is as follows:

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

NS_ASSUME_NONNULL_BEGIN

@interface MLNTestObject : NSObject <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.Bridge instance properties and methods

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

@interface MLNTestObject()

@property (nonatomic, copy) NSString *message;

@end

@implementation MLNTestObject

#pragma mark - Test received data
- (void)testReceive:(NSString *)msg
{
    _message = msg;
    NSLog(@"received:%@",msg);
}

#pragma mark - Test connection returns data
- (NSInteger)testAdd:(NSInteger)numberA numberB:(NSInteger)numberB
{
    return numberA + numberB;
}

#pragma mark - Test callback data
- (void)testAddWithCallback:(NSInteger)numberA numberB:(NSInteger)numberB callback:(MLNBlock *)callback
{
    if (callback) {
        //Adding parameters
        [callback addIntegerArgument:numberA + numberB];
        //Callback to Lua
        [callback callIfCan];
    }
}

#pragma mark - Register method to 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.Register and test

  1. Register to Lua
 [viewController regClasses:@[[MLNTestObject class]]];
  1. Lua test code
test = TestObject()
--Send data to native
test:testReceive("hello")
--Read natively received and recorded values
print("message:", test:message())
--Call native addition, return value
print("11 + 22 = ", test:testAdd(11,22))
test:testAddWithCallback(11, 22, function(sum)
--Get calculation results asynchronously
print("callback sum:", sum)
end)

2.3 Static class method

1.Create a new MLNStaticObject class, inherit from NSObject, and follow the protocol MLNStaticExportProtocol, the code is as follows:

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

NS_ASSUME_NONNULL_BEGIN

@interface MLNStaticObject : NSObject <MLNStaticExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.Bridge static class methods

#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.Register and test

  1. registered
    MLNLuaPageViewController *viewController = [[MLNLuaPageViewController alloc] initWithEntryFilePath:entryFile];
    [viewController regClasses:@[[MLNStaticObject class]]];
  1. test
StaticObject:test("Testing static class methods")

2.4 Objects with variable initialization parameters

There are some cases of bridged objects with different number of parameters. At this time, you need to have the ability to handle multiple parameters. The following describes how to bridge this type of object.

1.Create a class MLNMultiParamObject, inherited from NSObject, and follow the protocol MLNEntityExportProtocol, the code is as follows:

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

NS_ASSUME_NONNULL_BEGIN

@interface MLNMultiParamObject : NSObject <MLNEntityExportProtocol>

@end

NS_ASSUME_NONNULL_END

2.Initialize according to the number of parameters

#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) {
        // Mark created for 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.Register and test

  1. registered
    MLNLuaPageViewController *viewController = [[MLNLuaPageViewController alloc] initWithEntryFilePath:entryFile];
    [viewController regClasses:@[[MLNMultiParamObject class]]];
  1. test
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