文章開頭咱們先開門見山給出使用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
持續更新中...