Djinni_IOS初步使用
这里简单介绍一下这个开源项目的作用主要是一个工具,完成对跨平台代码的中间层Bridge的自动生成。看一下项目原文介绍。
Djinni is a tool for generating cross-language type declarations and interface bindings. It’s designed to connect C++ with either Java or Objective-C. Python support is available in an experimental version on the python branch.
- 选择这个工具的原因其实很简单,因为前面介绍Android和IOS跨平台时都需要去构造复杂的Bridge层,对开发效率也有影响,而这个工具帮我们将中间层Bridge层都自动生成,只需对需要对具体C++和java层逻辑进行coding,而不必关心其中间时如何进行交互的。
- 这个工具需要写对应的IDL(.djinni)文件,然后通过shell脚本进行一些配置,完成代码的自动生成,脚本中可以指定一些自动生成文件的参数,方便快捷。
- 此篇作为自己学习djinni的一篇记录,方便今后进行一些回顾,在文章中也有很多不对不足的地方,后面会不断学习修改。
- Types
- 这里介绍下Djinni支持的类型,也是写IDL文件的基础,原文介绍如下
- Djinni generates code based on interface definitions in an IDL file. An IDL file can contain three kinds of declarations: enums, records, and interfaces.
-
Enums become C++ enum classes, Java enums, or ObjC NS_ENUMs.
-
Flags become C++ enum classes with convenient bit-oriented operators, Java
enums with EnumSet, or ObjC NS_OPTIONS. -
Records are pure-data value objects.
-
Interfaces are objects with defined methods to call (in C++, passed by shared_ptr). Djinni produces code allowing an interface implemented in C++ to be transparently used from ObjC or Java, and vice versa.
- 1 ——IDL file
(1)此IDL文件为原文介绍,下面为demo的IDL file
my_enum = enum {
option1;
option2;
option3;
}
my_flags = flags {
flag1;
flag2;
flag3;
no_flags = none;
all_flags = all;
}
my_record = record {
id: i32;
info: string;
store: set<string>;
hash: map<string, i32>;
values: list<another_record>;
# Comments can also be put here
# Constants can be included
const string_const: string = "Constants can be put here";
const min_value: another_record = {
key1 = 0,
key2 = ""
};
}
another_record = record {
key1: i32;
key2: string;
} deriving (eq, ord)
# This interface will be implemented in C++ and can be called from any language.
my_cpp_interface = interface +c {
method_returning_nothing(value: i32);
method_returning_some_type(key: string): another_record;
static get_version(): i32;
# Interfaces can also have constants
const version: i32 = 1;
}
# This interface will be implemented in Java and ObjC and can be called from C++.
my_client_interface = interface +j +o {
log_string(str: string): bool;
}
IDL文件中的类型和C++,OC,Java的类型对照表
- Boolean (bool)
- Primitives (i8, i16, i32, i64, f32, f64).
- Strings (string)
- Binary (binary). This is implemented as std::vector<uint8_t> in C++, byte[] in Java, and NSData in Objective-C.
- Date (date). This is chrono::system_clock::time_point in C++, Date in Java, and NSDate in Objective-C.
- List (list). This is vector in C++, ArrayList in Java, and NSArray in Objective-C. Primitives in a list will be boxed in Java and Objective-C.
- Set (set). This is unordered_set in C++, HashSet in Java, and NSSet in Objective-C. Primitives in a set will be boxed in Java and Objective-C.
- Map (map<typeA, typeB>). This is unordered_map<K, V> in C++, HashMap in Java, and NSDictionary in Objective-C. Primitives in a map will be boxed in Java and Objective-C.
- Enumerations / Flags
- Optionals (optional). This is std::experimental::optional in C++11, object / boxed primitive reference in Java (which can be null), and object / NSNumber strong reference in Objective-C (which can be nil).
- Other record types. This is generated with a by-value semantic, i.e. the copy method will deep-copy the contents.
(2) demo.djinni (此文件就是demo中的IDL文件)
#1. hello_world 是C++对象,+C代表此方法需要在C++层进行实现,然后会自动生成OC和Java层的代码,这样C++实现的API就可以直接被OC和Java层进行调用,是不是很方便呢?
#2. static 代表C++的静态成员方法,create_with_listener为方法名,()里面是方法的参数,冒号前为参数名,后面为参数类型。()后面冒号为返回值。
#3. get_hello_world同上
hello_world= interface +c {
static create_with_listener(listener : text_listener):hello_world;
get_hello_world(key : string): string;
}
# 此方法为+o +j代表需要在OC和Java层进行实现,C++层通过此对象的完成创建
text_listener = interface +o +j {
updata_view(key : string) :string;
}
- 2——Generate Code
- 准备好了IDL文件,就可以写shell脚本进行代码的生成了。
- 其中sh文件的中一些参数都是见名知意,就不过多介绍了,当然也不仅仅包含这些。
(1) run_djinni.sh文件
#! /usr/bin/env bash
base_dir=$(cd "`dirname "0"`" && pwd)
cpp_out="$base_dir/generated-src/cpp"
jni_out="$base_dir/generated-src/jni"
objc_out="$base_dir/generated-src/objc"
java_out="$base_dir/generated-src/java/com/mycompany/helloworld"
java_package="com.mycompany.helloworld"
namespace="demospace"
objc_prefix="HW"
djinni_file="demo.djinni"
deps/djinni/src/run \
--java-out $java_out \
--java-package $java_package \
--ident-java-field mFooBar \
\
--cpp-out $cpp_out \
--cpp-namespace $namespace \
\
--jni-out $jni_out \
--ident-jni-class NativeFooBar \
--ident-jni-file NativeFooBar \
\
--objc-out $objc_out \
--objc-type-prefix $objc_prefix \
\
--objcpp-out $objc_out \
\
--idl $djinni_file
- 3——执行脚本
- sh run_djinni.sh
- 你就可以看到genration-src文件了,建议在执行脚本前先新建一个文件夹,将其放入文件夹中。
- 对于其他目录,建议在新建文件夹中再见一个src文件夹,用于源代码的coding。
- 下图是我在新建的djinni-project目录下tree -L 2
- 4——完成src中源码
src/cpp/hello_world_impl.hpp
#ifndef DJINNI_PROJECT_HELLO_WORLD_IMPL_H
#define DJINNI_PROJECT_HELLO_WORLD_IMPL_H
#include "hello_world.hpp"
#include "text_listener.hpp"
namespace demospace {
class HelloWorldImpl : public HelloWorld {
public:
HelloWorldImpl(const std::shared_ptr <TextListener> listener);
std::string get_hello_world(const std::string & key);
private:
std::shared_ptr <TextListener> _listener;
};
}// end namespace
#endif //DJINNI_PROJECT_HELLO_WORLD_IMPL_H
src/cpp/hello_world_impl.cpp
#include "hello_world_impl.hpp"
namespace demospace {
HelloWorldImpl::HelloWorldImpl(const std::shared_ptr <TextListener> listener){
_listener = listener;
}
std::shared_ptr<HelloWorld> HelloWorld::create_with_listener(const std::shared_ptr<TextListener> & listener){
return std::make_shared<HelloWorldImpl>(listener);
}
std::string HelloWorldImpl::get_hello_world(const std::string & key){
return "getHelloWorld + "+_listener->updata_view(key);
}
}// end namespace
src/objc/HWTextListenerImpl.h
#import "HWTextListener.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface HWTextListenerImpl : NSObject<HWTextListener>
@end
src/objc/HWTextListenerImpl.m
#include "HWTextListenerImpl.h"
@implementation HWTextListenerImpl
- (nonnull NSString *)updataView:(nonnull NSString *)key{
return [NSString stringWithFormat:@"updataView + %@",key];
}
@end
- 5——完成内容导入xcode中
- 首先新建一个项目HelloWorld
- 添加group,分别为djinni,C++,Bridge,OC,其中djinni组为djinni_project/deps/djinni/support-lib/objc,C++为src/cpp,Bridge为generation-src/cpp,OC为src/objc。
- 将需要的导入到四个group中,注意是添加reference,而不是copy,否则源文件修改这边感知不到。
- 下面是具体操作是的截图
- 新建一个HelloWorld项目(省略)
- 新建四个group,注意这里建组的时候,只需勾选第三个即可,不要选第一个。
- 导入需要的文件
之后弹窗记得选择reference,
- 最后的效果图