文章开头咱们先开门见山给出使用flutter_luakit_plugin做为基础库开发和普通flutter的区别。因为flutter定位是便携UI包,flutter提供的基础库功能是不足以知足复杂数据的app应用的,通常flutter开发模式以下图所示,当flutter知足不了咱们的需求的时候,使用methodchannel和eventchannel调用native接口。java
而使用flutter_luakit_plugin做为基础库的开发模式以下图所示,用lua来写逻辑层代码,用flutter写UI代码。luakit 提供了丰富的功能支持,能够支持大部分app的逻辑层开发,包括数据库orm,线程管理,http请求,异步socket,定时器,通知,json等等。用户只须要写dart代码和lua代码,不须要写oc、swift或java、kotlin代码,从而大幅提高代码的一致性(全部运行代码都是跨平台的)。android
Flutter诞生的时候我很兴奋,由于我对跨平台开发UI的见解一直是不看好的,最主要的缘由是没法得到体验一致性,可是Flutter前无古人的解决了这个问题,真正作到一端开发的UI,不管多复杂,在另外一端是能够获得一致的体验的,作不到这点的跨平台UI方案实际上并无达到跨平台节省工做量的效果,Flutter作到了。ios
Flutter1.0.0 发布了,我认为移动端跨平台开发所须要全部元素都已经齐备了,咱们尝试使用Flutter作一些功能,一个版本以后咱们总结了一些问题。git
Flutter是一套UI解决方案,但一个功能除了UI,还须要不少支持,网络请求,长链接,短链接,数据库,线程控制等等,这些方面Flutter生态中提供得比较差,没有ios 或者android那么多成熟的解决方案。Flutter 为了克服这问题,提供了一个解决方案,利用methodchannel和eventchannel调用ios和android的接口,利用原生成熟的方案作底层逻辑支撑。咱们一开始也是这样解决,但后续的麻烦也来了,因为methodchannel和eventchannel实现的方法是不跨平台的,Flutter从ios和android获得的数据的格式,事件调用的时机等,两个平台的实现是不同的,基本不可能彻底统一,能够这样说,一个功能在一个端能跑通,在另外一个端第一次跑必定跑不通,而后就要花大量的时间进行调试,适配,这样作以后跨平台的优点荡然无存,你们就会不断扯皮。相信我,下面的对话会成为大家的平常。github
ios开发:“大家android写的界面ios跑不起来”web
Android 开发:“咱们android能跑啊,iOS接口写得不对吧”objective-c
ios开发:“哪里不对,android写的界面,android帮忙调吧”sql
Android 开发:“我又不是ios开发,我怎么调”数据库
当一个已有的app要接入flutter,必然会产生一种状况,就是flutter体系里面的数据和逻辑,跟外部原生app的逻辑是不通的,简单说明一下,就是flutter写的业务逻辑一般是用dart语言写的,咱们在原生用object-c、swift或者java、kotlin写的代码是不能够脱离flutter的界面调用dart写的逻辑的,这种互通性的缺失,会致使不少数据联动作不到,譬如原生界面要现实一个flutter页面存下来的数据,或者原生界面要为flutter页面作一些预加载,这些都很不方便,主要是下图中,当flutter界面没调用时,从原生调用flutter接口是不容许的。json
以前我曾经开源一个纯逻辑层的跨平台解决方案luakit(附上luakit的起源),里面提供一个业务开发所须要的基本能力,包括网络请求,长链接,短链接,orm数据库,线程,通知机制等等,并且这些能力都是稳定的、跨平台并且通过实际业务验证过的方案。
作完一个版本纯flutter以后,我意识到能够用一种新的开发模式来进行flutter开发,这样能够避免我上面提到的两个问题,咱们团队立刻付诸实施,作了另外一个版本的flutter+luakit的尝试,即用flutter作界面,用lua来写逻辑,结构图以下。
新的方案开发效率获得极大的提高,不客气的说真正实现了跨平台,一个业务,从页面到逻辑,全部的代码一鼓作气所有由脚本完成(dart+lua),彻底不用object-c、swift或者java、kotlin来写逻辑,这样一个业务基本就能够无缝地从一端直接搬到另外一端使用,因此我写了这篇文章来介绍咱们团队的这个尝试,也把咱们的成果flutter_luakit_plugin开源了出来,让这种开发模式帮助到更多flutter开发团队。
下一步咱们一块儿看看如何用flutter配合lua实现所有代码都是跨平台的。咱们提供了一个 demo project,供你们参考。
dart写界面
在demo中全部的ui都写在了main.dart,固然在真实业务中确定复杂不少,可是并不影响咱们的开发模式。
dart调用lua逻辑接口
FlutterLuakitPlugin.callLuaFun("WeatherManager", "getWeather").then((dynamic d) { print("getWeather" + d.toString()); setState(() { weathers = d; }); }); 复制代码
上面这段代码的意思是调用WeatherManager的lua模块,里面提供的getWeather方法,而后把获得的数据以future的形式返回给dart,上面的代码至关于调用下面一段lua代码
require('WeatherManager').getWeather( function (d) end) 复制代码
而后剩下的事情就到lua,在lua里面可使用luakit提供的全部强大功能,一个app所须要的绝大部分的功能应该都提供了,并且咱们还会不断扩展。
你们可能会担忧dart和lua的数据格式转换问题,这个不用担忧,全部细节在flutter_luakit_plugin都已经作好封装,使用者尽管像使用dart接口那样去使用lua接口便可。
在lua中实现全部的非UI逻辑
这个demo(WeatherManager.lua)已经演示了如何使用luakit的相关功能,包括,网络,orm数据库,多线程,数据解析,等等
若是实在有flutter_luakit_plugin没有支持的功能,能够走回flutter提供的methodchannel和eventchannel的方式实现
通过了几个月磨合实践,咱们团队已经把接入flutter_luakit_plugin的成本降到最低,能够说是很是方便接入了。咱们已经把flutter_luakit_plugin发布到flutter官方的插件仓库。首先,要像其余flutter插件同样,在pubspec.yaml里面加上依赖,可参考demo配置
flutter_luakit_plugin: ^1.0.0
复制代码
而后在ios项目的podfile加上ios的依赖,可参考demo配置
source 'https://github.com/williamwen1986/LuakitPod.git' source 'https://github.com/williamwen1986/curl.git' pod 'curl', '~> 1.0.0' pod 'LuakitPod', '~> 1.0.13' 复制代码
而后在android项目app的build.gradle文件加上android的依赖,可参考demo配置
repositories { maven { url "https://jitpack.io" } } dependencies { implementation 'com.github.williamwen1986:LuakitJitpack:1.0.6' } 复制代码
最后,在须要使用的地方加上import就可使用lua脚本了
import 'package:flutter_luakit_plugin/flutter_luakit_plugin.dart'; 复制代码
lua脚本咱们默认的执行根路径在android是 assets/lua,ios默认的执行根路径是Bundle路径。
flutter 官方推荐的IDE是androidstudio和visual studio code。咱们在开发中以为androidstudio更好用,全部咱们同步也开发了luakit的androidstudio 插件,名字就叫luakit。luakit插件提供了如下的一些功能。
大部分功能,跟其余IDE没太多差异,这里我就不细讲了,我重点讲一下远程lua调试功能,由于这个跟平时调试ios和android设备有点不同,下面咱们详细介绍androidstudio luakit插件的使用。
androidstudio安装luakit插件
AndroidStudio->Preference..->Plugins->Browse reprositories...
搜索Luakit并安装Luakit插件而后重启androidstudio
配置lua项目
打开 Project Struture 窗口
选择 Modules、 Mark as Sources
添加调试器
选择 Edit Configurations ...
Select plus
添加Lua Remote(Mobdebug)
远程lua调试
在开始调试lua以前,咱们要在须要调试的lua文件加上下面一句lua代码。而后设上断点,便可调试。lua代码里面有两个参数,第一个是你调试用的电脑的ip地址,第二个是调试端口,默认是8172。
require("mobdebug").start("172.25.129.165", 8172) 复制代码
luakit的调试是经过socket来传递调试信息的,全部调试机器务必我电脑保持在同一网段,有时候可能作不到,这里咱们给出一下办法解决,咱们平常调试也是这样解决的。首先让你的手机开热点,而后你的电脑连上手机的热点,如今就能够保证你的手机和电脑是同一网段了,而后查看电脑的ip地址,填到lua代码上,就能够实现调试了。
(1) 数据库orm操做
这是flutter_luakit_plugin里面提供的一个强大的功能,也是flutter如今最缺的,简单高效的数据库操做,flutter_luakit_plugin提供的数据库orm功能有如下特征
具体可参考demo lua,下面只作简单介绍。
定义数据模型
-- 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") 复制代码
插入数据
local userTable = Table("user") local user = userTable({ username = "user1", password = "abc", time_create = os.time() }) user:save() 复制代码
更新数据
local userTable = Table("user") local user = userTable.get:primaryKey({"user1"}):first() user.password = "efg" user.time_create = os.time() user:save() 复制代码
删除数据
local userTable = Table("user") local user = userTable.get:primaryKey({"user1"}):first() user:delete() 复制代码
批量更新数据
local userTable = Table("user") userTable.get:where({age__gt = 40}):update({age = 45}) 复制代码
批量删除数据
local userTable = Table("user") userTable.get:where({age__gt = 40}):delete() 复制代码
select数据
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 -----------") 复制代码
联表操做
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 ") 复制代码
(2) 通知机制
通知机制提供了一个低耦合的事件互通方法,即在原生或者lua或者dart注册消息,在任何地方抛出的消息均可以接收到。
Flutter 添加监听消息
void notify(dynamic d) {
}
FlutterLuakitPlugin.addLuaObserver(3, notify);
复制代码
Flutter 取消监听
FlutterLuakitPlugin.removeLuaObserver(3, notify);
复制代码
Flutter抛消息
FlutterLuakitPlugin.postNotification(3, data);
复制代码
lua 添加监听消息demo code
local listener lua_notification.createListener(function (l) 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抛消息demo code
lua_notification.postNotification(3, { lua1 = "lua123", lua2 = "lua234" }) 复制代码
ios 添加监听消息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);
}
复制代码
ios抛消息demo code
post_notification(3, @{@"row":@(2)});
复制代码
android 添加监听消息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); 复制代码
android抛消息demo code
HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("row", new Integer(2)); NotificationHelper.postNotification(3, map); 复制代码
(3) http request
flutter自己提供了http请求库dio,不过当项目的逻辑接口想在flutter,原生native均可用的状况下,flutter写的逻辑代码就不太合适了,缘由上文已经提到,原生native是不能够随意调用flutter代码的,因此遇到这种状况,只有luakit合适,lua写的逻辑接口能够在全部地方调用,flutter 、ios、android均可以方便的使用lua代码,下面给出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}) 复制代码
(4) Async socket
异步socket长链接功能也是不少app开发所依赖的,flutter只支持websocket协议,若是app想使用基础的socket协议,那就要使用flutter_luakit_plugin提供的socket功能了,使用也很是简单,demo code,在callback里面拿到数据后可使用上文提到的通知机制把数据传回到flutter层。
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() 复制代码
(5) json 解析
json是最经常使用数据类型,使用可参考demo
local t = cjson.decode(responseStr) responseStr = cjson.encode(t) 复制代码
(6) 定时器timer
定时器也是项目开发中常常用到的一个功能,定时器咱们在orm框架的lua源码里面有用到,demo
local _timer _timer = lua_timer.createTimer(1)//0表明单次,1表明重复 _timer:start(2000,function () end) _timer:stop() 复制代码
(7) 还有全部普通适合lua用的库均可以在flutter_luakit_plugin使用
flutter通用基础库flutter_luakit_plugin
持续更新中...