- 1,编写widget
- 2,转化成json文件和js 文件
- 3,json 文件和js 文件加载,转化成widget,功能逻辑在js中
- 4,点击页面方法,方法是如何执行,并且如何刷新页面的
1、编写widget
fair 的接入过程请看文档:https://fair.58.com/zh...
那么这里写了一个简单的页面widget:
代码内容下:
import 'package:fair/fair.dart';
import 'package:flutter/material.dart';
@FairPatch()
class JsonFileExplain extends StatefulWidget {
const JsonFileExplain({Key? key}) : super(key: key);
@override
_JsonFileExplainState createState() => _JsonFileExplainState();
}
class _JsonFileExplainState extends State<JsonFileExplain> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget _buildTitle() {
return Text('title');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _buildTitle(),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${_counter}',
// '123',
style: TextStyle(
fontSize: 40, color: Color(0xffeb4237), wordSpacing: 0),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
这个widget页面,添加了 @FairPatch() 就将被生成json文件和js文件。
2、转化成json文件和js 文件
通过命令:
flutter packages pub run build_runner build --delete-conflicting-outputs
生成的js 文件内容下:
lib_test_fair_json_file_read.fair.js:
GLOBAL['#FairKey#'] = (function(__initProps__) {
const __global__ = this;
return runCallback(function(__mod__) {
with(__mod__.imports) {
function _JsonFileExplainState() {
const inner = _JsonFileExplainState.__inner__;
if (this == __global__) {
return new _JsonFileExplainState({
__args__: arguments
});
} else {
const args = arguments.length > 0 ? arguments[0].__args__ || arguments: [];
inner.apply(this, args);
_JsonFileExplainState.prototype.ctor.apply(this, args);
return this;
}
}
_JsonFileExplainState.__inner__ = function inner() {
this._counter = 0;
};
_JsonFileExplainState.prototype = {
_incrementCounter: function _incrementCounter() {
const __thiz__ = this;
with(__thiz__) {
setState('#FairKey#',
function dummy() {
_counter++;
});
}
},
};
_JsonFileExplainState.prototype.ctor = function() {};;
return _JsonFileExplainState();
}
},
[]);
})(convertObjectLiteralToSetOrMap(JSON.parse('#FairProps#')));
生成的json 文件 如下:
lib_test_fair_test_plugin_widget.fair.json:
{
"className": "Scaffold",
"na": {
"appBar": {
"className": "AppBar",
"na": {
"title": "%(_buildTitle)"
}
},
"body": {
"className": "Center",
"na": {
"child": {
"className": "Column",
"na": {
"mainAxisAlignment": "#(MainAxisAlignment.center)",
"children": [
{
"className": "Text",
"pa": [
"You have pushed the button this many times:"
]
},
{
"className": "Text",
"pa": [
"#(${_counter})"
],
"na": {
"style": {
"className": "TextStyle",
"na": {
"fontSize": 40,
"color": {
"className": "Color",
"pa": [
"0xffeb4237"
]
},
"wordSpacing": 0
}
}
}
}
]
}
}
}
},
"floatingActionButton": {
"className": "FloatingActionButton",
"na": {
"onPressed": "@(_incrementCounter)",
"tooltip": "Increment",
"child": {
"className": "Icon",
"pa": [
"#(Icons.add)"
]
}
}
}
},
"methodMap": {
"_buildTitle": {
"className": "Text",
"pa": [
"title"
]
}
}
}
从这里我们的页面就转化成了一个json 文件和js文件。json文件对应的是页面布局。js文件对应的是逻辑方法。
3、json 文件和js 文件加载,转化成widget,功能逻辑在js中
使用 FairWidget 加载json文件和js文件。
最后页面渲染效果如下:
![](https://img.haomeiwen.com/i45726/dd4649b091ce2f4d.png)
4、点击页面方法,方法是如何执行,并且如何刷新页面的
点击加号,首先会执行dart方法:
return ([props]) =>
_functions?['runtimeInvokeMethod']?.call(funcName, props);
这个方法实现为:
_bindFunctionsMap['runtimeInvokeMethod'] = (funcName, [props]) {
var arguments;
if (props != null) {
arguments = [];
arguments.add(props);
}
return runtime?.invokeMethod(pageName, funcName, arguments);
};
通过runtime运行时调用invokeMethod
runtime是fair定一个Runtimer类型的对象。所有动态逻辑执行的相关的方法都通过runtime对象进行调用。
class Runtime implements IRuntime {
MethodChannel getBasicChannel(){
return _channel!.basicMethodChannel!;
}
FairMessageChannel? _channel;
}
FairMessageChannel类型是fair对native通讯通道对象的封装,定义如下:
class FairMessageChannel {
Pointer<Utf8> Function(Pointer<Utf8>) invokeJSCommonFuncSync = dl
.lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>)>>(
'invokeJSCommonFuncSync')
.asFunction();
BasicMessageChannel<String?>? _commonChannel;
MethodChannel? _methodChannel;
MethodChannel? basicMethodChannel;
}
所以dart逻辑都会调用 BasicMessageChannel 和 Dart-FFI,MethodChannel三种方式 与native 进行通讯。
回到刚才点击加号,往下面走,执行到这个方法:
runtime?.invokeMethod(pageName, funcName, arguments);
runtime的invokeMethod方法实现为:
var reply = _channel!.sendCommonMessage(jsonEncode(from));
这个channel通道就调用到native中去了BasicMessageChannel
native channel通道方法定义如下
// 执行js
[self.flutterBasicMessageChannel setMessageHandler:^(id message, FlutterReply callback) {
FairStrongObject(strongSelf, weakSelf);
FairLog(@"%@", message);
if (strongSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(executeJSFunctionAsync:params:callback:)]) {
NSArray *params = message && [message isKindOfClass:[NSString class]] ? @[message] : @[];
[strongSelf.delegate executeJSFunctionAsync:FairExecuteJSFunction params:params callback:^(id result, NSError *error) {
FairLog(@"%@", result);
JSValue *value = result;
if (value && [value isKindOfClass:[JSValue class]]) {
NSString *str = value.toString;
FairLog(@"%@", str);
if (![str isEqualToString:@"undefined"] && FAIR_IS_NOT_EMPTY_STRING(str)) {
callback(str);
}
}
}];
}
}];
这个 executeJSFunctionAsync 的实现如下:
- (JSValue *)invokeJSFunctionOnJSThread:(NSString *)functionName params:(NSArray *)params callback:(FairCallback)callback
{
JSValue *jsValue = self.context[functionName];
JSValue *value = [jsValue callWithArguments:params];
if (callback) {
callback(value, nil);
}
return value;
}
看到这里,我们的functionName打印如下:
Printing description of functionName:
invokeJSFunc
这是一个 js 方法。
回顾一下, 点击加号, dart 调用 runtimeInvokeMethod dart方法,继而通过BasicMessageChannel 调用到native,然后通过native通道方法,使用JSContext调用到js 方法方法名字为 invokeJSFunc,并且参数为
<__NSSingleObjectArrayI 0x6000038a8590>(
{"pageName":"lib_test_fair_json_file_read#0","type":"method","args":{"funcName":"_incrementCounter","args":null}}
)
而这个 js 方法定义,我们从文件fair_core.js中找到了:
/*
* 用户的基础js,一般情况不需要改动
*/
let GLOBAL = {};
function invokeJSFunc(parameter) {
if (parameter === null) {
return null;
}
let map = JSON.parse(parameter);
if ('method' === map['type']) {
return _invokeMethod(map);
} else if ('variable' === map['type']) {
return _invokeVariable(map);
}
return null;
}
function _invokeVariable(par) {
console.log('_invokeVariable' + JSON.stringify(par));
let pName = par['pageName'];
let varMap = par['args'];
let curPage = GLOBAL[pName];
let callResult = {
pageName: pName,
result: {}
};
if (!isNull(varMap) && Object.keys(varMap).length > 0) {
Object.keys(varMap).forEach(function (varKey) {
callResult['result'][varKey] = eval('curPage.' + varKey.toString());
});
return JSON.stringify(callResult);
}
//如果没有传参数,默认返回全部的变量以及结果值
Object.keys(curPage).forEach(function (key) {
if (!isFunc(curPage[key])) {
callResult['result'][key] = eval('curPage.' + key.toString());
}
});
return JSON.stringify(callResult);
}
function _invokeMethod(par) {
let pageName = par['pageName'];
let funcName = par['args']['funcName'];
let args = par['args']['args'];
if ('getAllJSBindData' === funcName) {
return getAllJSBindData(par);
}
if ('releaseJS' === funcName) {
return _release(par);
}
let mClass = GLOBAL[pageName];
let func = mClass[funcName];
let methodResult;
if (isNull(func)) {
methodResult = '';
} else {
methodResult = func.apply(mClass, args);
}
let result = {
pageName: pageName,
result: {
result: methodResult
}
};
return JSON.stringify(result);
}
function _getAll(par) {
let pageName = par['pageName'];
let mc = GLOBAL[pageName];
let bind = {}
if (isNull(mc)) {
return JSON.stringify(bind);
}
let bindFunc = [];
let bindVariables = {};
let keys;
if (!isNull(keys = Object.keys(mc))) {
for (let i = 0; i < keys.length; i++) {
let k = keys[i];
if (!mc.hasOwnProperty(k)) {
continue;
}
if (isFunc(mc[k])) {
bindFunc.push(k);
} else {
bindVariables[k] = mc[k];
}
}
}
bind['func'] = bindFunc;
bind['variable'] = bindVariables;
return bind;
}
//demo 获取所有的变量和绑定的方法
function getAllJSBindData(par) {
let pageName = par['pageName'];
let bind = _getAll(par);
let result = {
pageName: pageName,
result: {
result: bind
}
};
return JSON.stringify(result);
}
function _release(par) {
let pageName = par['pageName'];
GLOBAL[pageName] = null;
return null;
}
function isFunc(name) {
return typeof name === "function";
}
function isNull(prop) {
return prop === null || 'undefined' === prop
|| 'undefined' === typeof prop
|| undefined === typeof prop
|| 'null' === prop;
}
function setState(pageName, obj) {
console.log('JS:setState()_before' + pageName + '-' + obj);
let p = {};
p['funcName'] = 'setState';
p['pageName'] = pageName;
// console.log('JS:setState(states)'+JSON.stringify(Object.getOwnPropertySymbols(obj)));
obj();
p['args'] = null;
let map = JSON.stringify(p);
console.log('JS:setState()' + map);
invokeFlutterCommonChannel(map);
}
function mapOrSetToObject(arg) {
if (Object.prototype.toString.call(arg) === '[object Map]') {
let obj1 = {}
for (let [k, v] of arg) {
obj1[k] = mapOrSetToObject(v);
}
return obj1;
}
if (Object.prototype.toString.call(arg) === '[object Array]') {
let obj2 = [];
for (let k of arg) {
obj2.push(mapOrSetToObject(k));
}
return obj2;
}
if (Object.prototype.toString.call(arg) === '[object Object]') {
let keys = Object.getOwnPropertyNames(arg);
let obj3 = {};
for (let key of keys) {
let value = arg[key];
obj3[key] = mapOrSetToObject(value);
}
return obj3;
}
return arg;
}
const invokeFlutterCommonChannel = (invokeData, callback) => {
console.log("invokeData" + invokeData)
jsInvokeFlutterChannel(invokeData, (resultStr) => {
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
};
这段js 方法的流程如下:
js方法invokeJSFunc,调用_invokeMethod , 然后从参数 funcName 为 _incrementCounter 的js 方法, 这个js 方法就是一开始我们页面被flutter build_runner生成转化的js 文件中。
我们继续回顾一下 js 文件中的 _incrementCounter 方法:
_incrementCounter: function _incrementCounter() {
const __thiz__ = this;
with(__thiz__) {
setState('#FairKey#',
function dummy() {
_counter++;
});
}
},
点击加号,执行了这段js 逻辑,继续调用setState方法
这个setState方法同样也是在 fair_core.js 中定义了的:
function setState(pageName, obj) {
console.log('JS:setState()_before' + pageName + '-' + obj);
let p = {};
p['funcName'] = 'setState';
p['pageName'] = pageName;
// console.log('JS:setState(states)'+JSON.stringify(Object.getOwnPropertySymbols(obj)));
obj();
p['args'] = null;
let map = JSON.stringify(p);
console.log('JS:setState()' + map);
invokeFlutterCommonChannel(map);
}
这里我们有需要调用js 的方法 invokeFlutterCommonChannel 。那么这个js方法在哪里定义了呢:
继续看,还是在jsCore.js文件中:
const invokeFlutterCommonChannel = (invokeData, callback) => {
console.log("invokeData" + invokeData)
jsInvokeFlutterChannel(invokeData, (resultStr) => {
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
};
从而最终,js方法都会走向 jsInvokeFlutterChannel 这个js方法。
那么这个方法定义是在native中,看代码:
NSString * const FairExecuteDartFunctionAsync = @"jsInvokeFlutterChannel";
// JS 异步调用 Dart
_context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
}
};
再看一下这个FairExecuteDartFunctionAsync:callback:的实现:
- (void)FairExecuteDartFunctionAsync:(NSString *)data callback:(JSValue *)callback
{
[[FairDartBridge sharedInstance] sendMessageToDart:data callback:^(id result, NSError *error) {
[[FairJSBridge sharedInstance] invokeJSFunction:callback param:result];
}];
}
这里做了2件事:
第一件事是调用sendMessageToDart:callback:
第二件事是调用invokeJSFunction:param:
第一件事是
- (void)sendMessageToDart:(NSString *)message callback:(FairCallback)callback {
[self.flutterBasicMessageChannel sendMessage:message reply:^(id reply) {
if (callback && FAIR_IS_NOT_EMPTY_STRING(reply)) {
callback(reply, nil);
}
}];
}
这是调用 flutterBasicMessageChannel 通道,调用dart 代码:
dart flutterBasicMessageChannel 通道方法定义如下:
_commonChannel!.setMessageHandler((String? message) async {
print('来自native端的消息:$message');
//js 异步调用dart中的相关方法
var data = json.decode(message??'');
var funcName = data['funcName']?.toString();
if (funcName == 'invokePlugin') {
var p = await FairPluginDispatcher.dispatch(message);
return p;
}
_callback?.call(message);
return 'reply from dart';
});
_callback方法的实现为:
//接收native发送过来的消息,实际上是js发送的消息,通过native端透传过来
_runtime.getChannel().setMessageHandler((String? message) {
var data = json.decode(message ?? '');
var funcName = data['funcName']?.toString();
var pageName = data['pageName'];
var args = data['args']??{};
if (funcName == null || funcName.isEmpty) {
return '';
}
//当用户调用setState的时候相当于刷新了数据,通知刷新页更新
if (funcName == 'setState') {
_dispatchMessage(pageName, jsonEncode(args));
return '';
}
});
_commonChannel!.setMessageHandler((String? message) async {
print('来自native端的消息:$message');
//js 异步调用dart中的相关方法
var data = json.decode(message??'');
var funcName = data['funcName']?.toString();
if (funcName == 'invokePlugin') {
var p = await FairPluginDispatcher.dispatch(message);
return p;
}
_callback?.call(message);
return 'reply from dart';
});
_dispatchMessage方法实现为:
String _dispatchMessage(String pageName, String message) {
pageHistories[pageName]?.call(message);
return 'Reply from Dart';
}
pageHistories定义如下:
void call(String t) {
var params={};
try {
params= jsonDecode(t);
} catch (e) {
print(e);
}
delegate.notifyValue(params);
}
//获取js端的数据,刷新指定数据
void notifyValue(Map values) {
setState(() {});
}
void setState(VoidCallback fn) {
if (_state == null || !_state!.mounted) return;
_state?.setState(fn);
_state?._reload();
}
void _reload() {
if (mounted) {
var name = state2key;
var path = widget.path ?? _fairApp!.pathOfBundle(widget.name ?? '');
bundleType =
widget.path != null && widget.path?.startsWith('http') == true
? 'Http'
: 'Asset';
parse(context, page: name, url: path, data: widget.data ?? {})
.then((value) {
if (mounted && value != null) {
setState(() => _child = value);
}
});
}
}
这样就重新加载了一次页面,完成了页面更新。
回到上面第二件事情callback的调用invokeJSFunction:param:
invokeJSFunction:param:方法为:
- (JSValue *)invokeJSFunctionOnJSThread:(JSValue *)function params:(NSArray *)params
{
JSValue *value = [function callWithArguments:params];
return value;
}
这个callback参数为:
_context[@"jsInvokeFlutterChannel"] = ^(id receiver, JSValue *callback) {
FairStrongObject(strongSelf, weakSelf)
NSString *data = [strongSelf convertStringWithData:receiver];
if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
[strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
}
};
callback参数是一个JSValue对象,是一个js对象,js的函数:
jsInvokeFlutterChannel(invokeData, (resultStr) => {
console.log('resultStr' + resultStr);
if (callback) {
callback(resultStr);
}
});
那么这里就是结果回调js。
通过流程分析,回顾一下整个流程为:
点击加号,dart代码执行 runtimeInvokeMethod dart方法,继而通过BasicMessageChannel 调用到native,然后通过native通道方法,使用JSContext调用到js 方法方法名字为 invokeJSFunc,并且参数为具体的执行函数名字_incrementCounter,调用js方法后,继续调用 jsInvokeFlutterChannel,回到native,native 通过flutterBasicMessageChannel通道调用dart,执行dart,dart通过BasicMessageChannel定义了setMessageHandler方法,从而调用 _dispatchMessage 方法 通过不同funcName对应setstate 刷新界面。
网友评论