Flutter通用基礎庫flutter_luakit_plugin

使用flutter_luakit_plugin做爲基礎庫開發Flutter應用

文章開頭咱們先開門見山給出使用flutter_luakit_plugin做爲基礎庫開發和普通flutter的區別。因爲flutter定位是便攜UI包,flutter提供的基礎庫功能是不足以知足複雜數據的app應用的,通常flutter開發模式以下圖所示,當flutter知足不了咱們的需求的時候,使用methodchannel和eventchannel調用native接口。java

image

而使用flutter_luakit_plugin做爲基礎庫的開發模式以下圖所示,用lua來寫邏輯層代碼,用flutter寫UI代碼。luakit 提供了豐富的功能支持,能夠支持大部分app的邏輯層開發,包括數據庫orm,線程管理,http請求,異步socket,定時器,通知,json等等。用戶只須要寫dart代碼和lua代碼,不須要寫oc、swift或java、kotlin代碼,從而大幅提高代碼的一致性(全部運行代碼都是跨平臺的)android

image

flutter_luakit_plugin由來

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

image

以前我曾經開源一個純邏輯層的跨平臺解決方案luakit(附上luakit的起源),裏面提供一個業務開發所須要的基本能力,包括網絡請求,長鏈接,短鏈接,orm數據庫,線程,通知機制等等,並且這些能力都是穩定的、跨平臺並且通過實際業務驗證過的方案。

作完一個版本純flutter以後,我意識到能夠用一種新的開發模式來進行flutter開發,這樣能夠避免我上面提到的兩個問題,咱們團隊立刻付諸實施,作了另外一個版本的flutter+luakit的嘗試,即用flutter作界面,用lua來寫邏輯,結構圖以下。

image

新的方案開發效率獲得極大的提高,不客氣的說真正實現了跨平臺,一個業務,從頁面到邏輯,全部的代碼一鼓作氣所有由腳本完成(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_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_luakit_plugin開發環境IDE--AndroidStudio

flutter 官方推薦的IDE是androidstudio和visual studio code。咱們在開發中以爲androidstudio更好用,全部咱們同步也開發了luakit的androidstudio 插件,名字就叫luakit。luakit插件提供瞭如下的一些功能。

  • 遠程lua調試
  • 查找函數使用
  • 跳到函數定義
  • 跳到文件
  • 參數名字提示
  • 代碼自動補全
  • 代碼格式化
  • 代碼語法檢查
  • 標準lua api自動補全
  • luakit api自動補全

大部分功能,跟其餘IDE沒太多差異,這裏我就不細講了,我重點講一下遠程lua調試功能,由於這個跟平時調試ios和android設備有點不同,下面咱們詳細介紹androidstudio luakit插件的使用。

androidstudio安裝luakit插件

AndroidStudio->Preference..->Plugins->Browse reprositories...

image

搜索Luakit並安裝Luakit插件而後重啓androidstudio

image

配置lua項目

打開 Project Struture 窗口

image

選擇 Modules、 Mark as Sources

image

添加調試器

選擇 Edit Configurations ...

image

Select plus

image

添加Lua Remote(Mobdebug)

image

遠程lua調試

在開始調試lua以前,咱們要在須要調試的lua文件加上下面一句lua代碼。而後設上斷點,便可調試。lua代碼裏面有兩個參數,第一個是你調試用的電腦的ip地址,第二個是調試端口,默認是8172。

require("mobdebug").start("172.25.129.165", 8172)
複製代碼

image

luakit的調試是經過socket來傳遞調試信息的,全部調試機器務必我電腦保持在同一網段,有時候可能作不到,這裏咱們給出一下辦法解決,咱們平常調試也是這樣解決的。首先讓你的手機開熱點,而後你的電腦連上手機的熱點,如今就能夠保證你的手機和電腦是同一網段了,而後查看電腦的ip地址,填到lua代碼上,就能夠實現調試了。

image

image

flutter_luakit_plugin提供的api介紹

(1) 數據庫orm操做

這是flutter_luakit_plugin裏面提供的一個強大的功能,也是flutter如今最缺的,簡單高效的數據庫操做,flutter_luakit_plugin提供的數據庫orm功能有如下特徵

  • 面向對象
  • 自動建立和更新表結構
  • 自帶內部對象緩存
  • 定時自動transaction
  • 線程安全,徹底不用考慮線程問題

具體可參考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通用基礎庫flutter_luakit_plugin

flutter_luakit_plugin使用例子

《手把手教你編譯Flutter engine》

《手把手教你解決 Flutter engine 內存漏》

修復內存泄漏後的flutter engine(可直接使用)

修復內存泄漏後的flutter engine使用例子

持續更新中...

相關文章
相關標籤/搜索