最近发布了一个跨平台的app开发框架Luakit 。那怎么会想到作这样一个东西呢?这要先说一下我参与过的一些项目,和在项目中接触到的一些技术点和对项目开发体检了,由于Luakit是集合了几个重要技术才能作到用Lua脚原本实现跨平台app开发的。java
我主要参与的项目是QQMail的IOS版。在2017年下半年,因为机缘巧合,我参与开发了企业微信的一个分支版本,appstore上叫政务微信。QQMail的历史比较悠久了,在QQMail项目里咱们使用了两项技术是比较特殊的,其余项目团队接触得比较少,一个是Lua脚本化技术,一个是orm技术。而在政务微信开发过程当中是企业微信团队的跨平台开发技术给我留下很深印象,下面我首先简单介绍这几项技术。android
)来作的,只能在IOS上跑,不具有跨平台的能力。QQMail里面有几个版本中,整个记事本模块从底层逻辑层到界面渲染所有都用Lua来实现,脚本化过程当中咱们也克服了不少技术难点,例如如何在Lua脚本实现竞争式多线程,如何高效方便地在Lua环境实现数据存储之类的这些业界难题,固然了,脚本化以后咱们也第一次吃到脚本化的甜头,最大的优势就是对代码的掌控能力大大提高,一个是能够随时上线,另外就是能够给不一样的用户下发不一样的代码。这个对于发现问题有很大的好处,当有用户投诉的时候,给用户下发特殊的debug代码,基本没有发现不了的问题。ios
深刻接触这几个框架后,我发现Lua跟chromium真是绝配,chromium提供跨平台的消息循环机制能够完美解决lua实现竞争式多线程的问题,在lua环境实现竞争式多线程(注意,不是单单线程安全)是使用lua开发的一个广泛性的难题,cocos2d-x的lua-binding也没解决这个问题,因此基于cocos2d-x lua版开发的游戏也很难作到全脚本化,由于Lua只能单线程。有了Luakit后,这类问题都有解决方案了。而lua的内存管理机制也能够很好的解决chromium用c++开发,内存管理和不适合函数式编程的最大的弊端,二者解合能够产生很好的效果。有了lua的多线程模型后,参考GYDataCenter的实现原理,咱们能够实现一套lua版的orm框架,GYDataCenter只能在ios使用,如今lua版的orm框架能够具备跨平台的特性。c++
Luakit提供的不少强大的功能,这些功能都是能够跨平台运行的,Luakit主要包括如下功能接口git
下面简单介绍多线程接口,orm接口,http请求,异步socket接口和全局通知接口。github
多线程模型objective-c
如何在Lua实现竞争式多线程我会再发一篇文章专门讲讲,由于这个问题是Lua领域的广泛存在的问题,有必定的技术意义。这里我先简单带过一下实现思路,一个lua解析器自己是不具有多线程能力,甚至不是线程安全的,可是在服务器开发上已经有人尝试起多条线程而后给每条线程配置独立的Lua解析器,而后多条线程经过必定的数据通道传输数据,经过这样的方式实现真正的多线程,可是这个思路一直没有延伸到客户端开发,主要缘由是由于客户端一般把真正的线程隐藏起来,不管IOS或者android,都不能轻易地接触真正的线程,可是因为chromium提供了开源的线程模型,经过修改chromium的底层源码,生成消息循环时的给每一个消息循环配置独立的lua解析器,这样最大的问题就获得了解决,下面看一下Luakit 提供的多线程接口。sql
建立线程 ,demo code数据库
-- Parma1 is the thread type ,there are five types of thread you can create. -- BusinessThreadUI -- BusinessThreadDB -- BusinessThreadLOGIC -- BusinessThreadFILE -- BusinessThreadIO -- Param2 is the thread name -- Result is new threadId which is the token you should hold to do further action local newThreadId = lua.thread.createThread(BusinessThreadLOGIC,"newThread")
异步调用方法,相似IOS gcd中的 dispatch_async , demo code编程
-- Parma1 is the threadId for which you want to perform method -- Parma2 is the modelName -- Parma3 is the methodName -- The result is just like you run the below code on a specified thread async -- require(modelName).methodName("params", 1.1, {1,2,3}, function (p) -- end) lua.thread.postToThread(threadId,modelName,methodName,"params", 1.1, {1,2,3}, function (p) -- do something here end)
同步调用方法,相似IOS gcd中的 dispatch_sync , demo code
-- Parma1 is the threadId for which you want to perform method -- Parma2 is the modelName -- Parma3 is the methodName -- The result is just like you run the below code on a specified thread sync -- local result = require(modelName).methodName("params", 1.1, {1,2,3}, function (p) -- end) local result = lua.thread.postToThreadSync(threadId,modelName,methodName,"params", 1.1, {1,2,3}, function (p) -- do something here end)
orm接口
orm 模型的实现方法是参考IOS orm 开源库GYDataCenter的实现方法,GYDataCenter很依赖IOS gcd 的机制,Luakit中能够用新的lua多线程接口取代,能够作到一样的效果,下面罗列一下 orm demo code
Luakit 提供的orm框架有以下特征
定义数据模型, demo code
-- Add the define table to dbData.lua -- Luakit provide 7 colum types -- IntegerField to sqlite integer -- RealField to sqlite real -- BlobField to sqlite blob -- CharField to sqlite varchar -- TextField to sqlite text -- BooleandField to sqlite bool -- DateTimeField to sqlite integer user = { __dbname__ = "test.db", __tablename__ = "user", username = {"CharField",{max_length = 100, unique = true, primary_key = true}}, password = {"CharField",{max_length = 50, unique = true}}, age = {"IntegerField",{null = true}}, job = {"CharField",{max_length = 50, null = true}}, des = {"TextField",{null = true}}, time_create = {"DateTimeField",{null = true}} }, -- when you use, you can do just like below local Table = require('orm.class.table') local userTable = Table("user")
插入数据, demo code
local userTable = Table("user") local user = userTable({ username = "user1", password = "abc", time_create = os.time() }) user:save()
更新数据 demo code
local userTable = Table("user") local user = userTable.get:primaryKey({"user1"}):first() user.password = "efg" user.time_create = os.time() user:save()
select 数据,demo code
local userTable = Table("user") local users = userTable.get:all() print("select all -----------") local user = userTable.get:first() print("select first -----------") users = userTable.get:limit(3):offset(2):all() print("select limit offset -----------") users = userTable.get:order_by({desc('age'), asc('username')}):all() print("select order_by -----------") users = userTable.get:where({ age__lt = 30, age__lte = 30, age__gt = 10, age__gte = 10, username__in = {"first", "second", "creator"}, password__notin = {"testpasswd", "new", "hello"}, username__null = false }):all() print("select where -----------") users = userTable.get:where({"scrt_tw",30},"password = ? AND age < ?"):all() print("select where customs -----------") users = userTable.get:primaryKey({"first","randomusername"}):all() print("select primaryKey -----------")
联表查询,demo code
local userTable = Table("user") local newsTable = Table("news") local user_group = newsTable.get:join(userTable):all() print("join foreign_key") user_group = newsTable.get:join(userTable,"news.create_user_id = user.username AND user.age < ?", {20}):all() print("join where ") user_group = newsTable.get:join(userTable,nil,nil,nil,{create_user_id = "username", title = "username"}):all() print("join matchColumns ")
http请求
Luakit提供了http请求接口,包括了请求队列调度控制, 实现代码, demo code
-- url , the request url -- isPost, boolean value represent post or get -- uploadContent, string value represent the post data -- uploadPath, string value represent the file path to post -- downloadPath, string value to tell where to save the response -- headers, tables to tell the http header -- socketWatcherTimeout, int value represent the socketTimeout -- onResponse, function value represent the response callback -- onProgress, function value represent the onProgress callback lua.http.request({ url = "http://tj.nineton.cn/Heart/index/all?city=CHSH000000", onResponse = function (response) end})
异步socket接口
Luakit 提供了非阻塞的socket调用接口, demo code
local socket = lua.asyncSocket.create("127.0.0.1",4001) socket.connectCallback = function (rv) if rv >= 0 then print("Connected") socket:read() end end socket.readCallback = function (str) print(str) timer = lua.timer.createTimer(0) timer:start(2000,function () socket:write(str) end) socket:read() end socket.writeCallback = function (rv) print("write" .. rv) end socket:connect()
通知接口
app开发中常常会遇到须要一对多的通知场景,例如ios有系统提供Notification Center 来提供,为了跨平台的实现通知,Luakit也提供通知接口
Lua register and post notification, demo code
lua.notification.createListener(function (l) local listener = l listener:AddObserver(3, function (data) print("lua Observer") if data then for k,v in pairs(data) do print("lua Observer"..k..v) end end end ) end); lua.notification.postNotification(3, { lua1 = "lua123", lua2 = "lua234" })
Android register and post notification, demo code
LuaNotificationListener listener = new LuaNotificationListener(); INotificationObserver observer = new INotificationObserver() { @Override public void onObserve(int type, Object info) { HashMap<String, Integer> map = (HashMap<String, Integer>)info; for (Map.Entry<String, Integer> entry : map.entrySet()) { Log.i("business", "android onObserve"); Log.i("business", entry.getKey()); Log.i("business",""+entry.getValue()); } } }; listener.addObserver(3, observer); HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("row", new Integer(2)); NotificationHelper.postNotification(3, map);
IOS register and post notification, demo code
_notification_observer.reset(new NotificationProxyObserver(self)); _notification_observer->AddObserver(3); - (void)onNotification:(int)type data:(id)data { NSLog(@"object-c onNotification type = %d data = %@", type , data); } post_notification(3, @{@"row":@(2)});
在腾讯我也接触过很多项目(参与开发或者了解代码),每一个项目都会发展出一套属于本身的基础架构,基础架构的选择一般都会根据本身原有的知识体系搭建,这个基本无一例外,习惯用chromium的团队,全部由那个团队创建的项目都会基于chromium作基础架构,若是没有特别熟悉的,就会使用原生提供的接口搭建本身的基础架构,这个无可厚非,可是选择完基础架构后,基本上app的素质就已经定下来,能不能跨平台,数据能不能支持orm,代码能不能热更新,全部这些基本能力都已经定下来了,后续加入团队的人不管多牛都只是在原有基础上添砖加瓦,修修补补,大动筋骨一般都有很大代价的。全部我认为对项目的技术负责人来讲,选择什么基础架构这件事是再重要不过了,项目中后期花无数个晚上来解决不知从何查起的bug,对投诉无能为力,没有足够的工具来快速响应,大量重复代码,不断反复的bug,这些问题有可能看似是一个刚入职的工程师的疏忽或者设计不当,其实大部分的缘由从选择基础架构的时候已经注定了,往后代码的复杂度,app具备的能力,早就已经定下了。
Luakit 是我暂时知道的最高效的基础架构,由于它具备如下特色
最后,但愿你们能够多了解,试用Luakit ,有问题能够发邮件到williamwen1986@gmail.com