MethodChannel
使用VS2019与fluter通过MethodChannel进行双向通信
flutter是一个比较新的框架,由谷歌于2018年推出,前期主要应用于移动端,目前也支持PC端开发,但是网上的资料多数都是移动端相关的,笔者也是刚接触flutter一周,在项目中需要使用flutter做一个windows的项目,需要使用flutter与windows进行交互,这里采用的是flutter插件的方式,部分代码是由插件模板自动创建,这里就直接贴出代码,并没有给出注释,如果有小伙伴感兴趣可以去看看官方的文档,这里不再赘述。言归正传,当笔者浏览了国内外网站后,发现关于windows端的资料甚少,不得已只能自己探索,由于是自己研究,所以难免有不对的地方,欢迎指正, 也希望能给初学者带来一点帮助!
VS端
这里笔者使用的是VS2019其他版本大同小异,首先是接收来自flutter端的函数调用代码:
std::unique_ptr<
flutter::MethodChannel<flutter::EncodableValue>,
std::default_delete<flutter::MethodChannel<flutter::EncodableValue>>>
channel = nullptr;
// static
void IptunctrlPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar)
{
channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "iptunctrl",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<IptunctrlPlugin>();
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto &call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
void IptunctrlPlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (method_call.method_name().compare("getPlatformVersion") == 0) {
//模板自动生成的获取系统版本的方法
std::ostringstream version_stream;
version_stream << "Windows ";
if (IsWindows10OrGreater()) {
version_stream << "10+";
}
else if (IsWindows8OrGreater()) {
version_stream << "8";
}
else if (IsWindows7OrGreater()) {
version_stream << "7";
}
result->Success(version_stream.str());
}
else if (method_call.method_name().compare("Test") == 0) {
//自定义Test方法 无参数,返回字符串
Test(); //调用自定义C++函数
result->Success(flutter::EncodableValue("version_stream.str()"));
}
else if (method_call.method_name().compare("GetNum") == 0) {
//自定义GetNum方法 无参数,返回整形
int n = GetNum(); //调用自定义C++函数
result->Success(flutter::EncodableValue(n));
}
else if (method_call.method_name().compare("GetAdd") == 0) {
//自定义GetAdd方法 整形参数,返回整形
const flutter::EncodableList* arguments = std::get_if<flutter::EncodableList>(method_call.arguments());
if (!arguments)
{
result->Error("arguments error");
return;
}
//获取参数1 int
int arg1 = std::get<int>(arguments->at(0));
//获取参数2 double
double arg2 = std::get<double>(arguments->at(1));
//获取参数3 char*
char* p = const_cast<char*>(std::get<std::string>(arguments->at(2)).c_str());
int n = GetAdd(arg1,arg2,p);//调用自定义C++函数带入参数 并获取返回值
result->Success(n); //传入返回值
}
else if (!method_call.method_name().compare("RetList")) {
//自定义RetList方法 无参,返回列表数据
flutter::EncodableList r;
RetList(r);//调用自定义C++函数 并填充list
result->Success(r);//直接传回list
}
else if (!method_call.method_name().compare("RetMap")) {
//自定义RetMap方法 无参,返回map
flutter::EncodableMap r;
RetMap(r);//调用自定义C++函数 并填充MAP
result->Success(r);//直接返回map
}
else {
result->NotImplemented(); //返回未定义的方法
}
}
接下来我们来看下PC端是如何调用flutter的,调用其实也不复杂,通过InvokeMethod就可以实现,我们先来看下这个方法的声明:
void InvokeMethod(const std::string& method, //传入需要调用的方法名
std::unique_ptr<T> arguments, //传入参数,这个参数可以是单个参数也可以是list或map
std::unique_ptr<MethodResult<T>> result = nullptr)//最后这个参数是获取调用返回值的,笔者也是卡在这个位置
这里笔者重载了几个调用方法:
void IptunctrlPlugin::Call(std::string _function,flutter::EncodableValue* result)
{//无参调用
if (!result)
{//无返回值调用
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(nullptr));
return;
}
//这里主要是同步等待返回结果
auto res = std::make_unique<ResultValue<flutter::EncodableValue>>(result);
HANDLE hEvent = res->GetHandle();//获取当前对象的event句柄
channel->InvokeMethod(_function, nullptr, std::move(res));
ResultValue<>::Wait(hEvent); //等待调用结束
}
void IptunctrlPlugin::Call(std::string _function, flutter::EncodableValue* argument, flutter::EncodableValue* result)
{//单个参数调用
if (!result)
{//无返回值调用
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*argument));
return;
}
//这里主要是同步等待返回结果
auto res = std::make_unique<ResultValue<flutter::EncodableValue>>(result);
HANDLE hEvent = res->GetHandle();//获取当前对象的event句柄
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*argument), std::move(res));
ResultValue<>::Wait(hEvent);//等待调用结束
}
void IptunctrlPlugin::Call(std::string _function, flutter::EncodableList* arguments, flutter::EncodableValue* result)
{//参数为list的调用
if (!result)
{//无返回值调用
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*arguments));
return;
}
//这里主要是同步等待返回结果
auto res = std::make_unique<ResultValue<flutter::EncodableValue>>(result);
HANDLE hEvent = res->GetHandle();//获取当前对象的event句柄
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*arguments), std::move(res));
ResultValue<>::Wait(hEvent);//等待调用结束
}
void IptunctrlPlugin::Call(std::string _function, flutter::EncodableMap* arguments, flutter::EncodableValue* result)
{//参数为map的调用
if (!result)
{//无返回值调用
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*arguments));
return;
}
//这里主要是同步等待返回结果
auto res = std::make_unique<ResultValue<flutter::EncodableValue>>(result);
HANDLE hEvent = res->GetHandle();//获取当前对象的event句柄
channel->InvokeMethod(_function, std::make_unique<flutter::EncodableValue>(*arguments), std::move(res));
ResultValue<>::Wait(hEvent);//等待调用结束
}
重点来了,下面就是如何获取返回值,以及同步调用方法,这里为了方便,笔者使用的是list返回,这个部分资料极少,也可能是我搜索的方式不对,总之…
template <typename T = flutter::EncodableValue>
class ResultValue :public flutter::MethodResult<T> {
HANDLE m_hEvent;//每个调用都使用一个event来控制同步
flutter::EncodableValue* m_value; //返回值指针
public:
ResultValue(T* value) :m_value(value) {
m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //构造对象的同时创建事件句柄
}
~ResultValue() {} //不在析构的时候释放事件句柄
HANDLE GetHandle() { return m_hEvent; } //获取当前对象对应的事件句柄,同步时使用
static void Wait(HANDLE hEvent) { //静态函数,等待返回值
//flutter采用的是异步,如果这里直接等待的话会导致线程卡死,后续也不会在接收到返回值,所以这里需要手动调用循环来保证返回值以及UI的正常运作
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::MSG msg; //此处要考虑是在工作线程做没有消息循环的情况需要手动调用PeekMessage 开启线程消息循环
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 10))
{
//如果获取到返回值则跳出循环并且关闭事件句柄,一次调用接近尾声
if (hEvent)
CloseHandle(hEvent);
break;
}
}
}
protected:
// 重写父类方法 调用成功 返回值解析方法
void SuccessInternal(const T* result) override {
const flutter::EncodableList* res = std::get_if<flutter::EncodableList>(result);
flutter::EncodableList* value = reinterpret_cast <flutter::EncodableList*>(m_value);
value->push_back(flutter::EncodableValue((int)0));
value->push_back(std::move(*res));
SetEvent(m_hEvent); //通知主业务同步调用结束
PostThreadMessage(m_curThreadId, WM_NULL, NULL, NULL); //一旦有返回 通过空消息来激活线程消息 UI线程中也没有影响
}
// 重写父类方法 调用失败,返回错误信息
void ErrorInternal(const std::string& error_code,
const std::string& error_message,
const T* error_details) override {
flutter::EncodableList* value = reinterpret_cast <flutter::EncodableList*>(m_value);
value->push_back(flutter::EncodableValue((int)1));
value->push_back(flutter::EncodableValue(error_code.c_str()));
value->push_back(flutter::EncodableValue(error_message.c_str()));
value->push_back(flutter::EncodableValue(error_details));
SetEvent(m_hEvent);//通知主业务同步调用结束
PostThreadMessage(m_curThreadId, WM_NULL, NULL, NULL); //一旦有返回 通过空消息来激活线程消息 UI线程中也没有影响
}
// 重写父类方法 调用函数名未定义
void NotImplementedInternal() override {
flutter::EncodableList* value = reinterpret_cast <flutter::EncodableList*>(m_value);
value->push_back(flutter::EncodableValue((int)2));
SetEvent(m_hEvent);//通知主业务同步调用结束
PostThreadMessage(m_curThreadId, WM_NULL, NULL, NULL); //一旦有返回 通过空消息来激活线程消息 UI线程中也没有影响
}
};
从上面的代码中可以看出,我们定义了一个MethodResult的派生类,实际上也是通过这个派生类来实现的消息接收,由于flutter采用的是异步,所以如果要等待每次调用的返回值需要手动实现消息循环,否则消息会永远阻塞在WaitForSingleObject,导致重写的三个返回值函数无法被回调!
flutter端
flutter端的资料就比较多了 这里就直接贴上代码:
Future<void> NotImplemented() {
throw MissingPluginException(null); //抛出MissingPluginException异常,flutter将返回未定义的方法
}
@override
Future<dynamic> nativeMessageListener() async {
methodChannel.setMethodCallHandler((call) {
//处理原生 发送过来的消息
switch (call.method) {
case "TestFunction":
{
//单个参数则直接获取
int arguments = call.arguments;
print(arguments);
//如果参数是map类型则:
/*int code = call.arguments["id"];
String message = call.arguments["message"];
//可以直接刷新界面
setState(() {
recive += " code $code message $message and method $method ";
print(recive);
});*/
//如果参数是list类型则:
/*int code = call.arguments[0];
String message = call.arguments[1];*/
String aaa = "333";
return Future.value([1, '123', 5.0]);
}
case "RetValue":
{
//返回单个值:
return Future.value("aaa");
//return Future.value(123);
//返回list:
//return Future.value([1, '123', 5.0]);
//返回map
//return Future.value({"id":1, "message":'123', "double":5.0});
}
default:
{
return NotImplemented();//这里也是一个坑 如果要返回未定义的方法 需要抛出一个异常
}
}
});
}
以上就是关于VS2019与Flutter 通过MethodChannel双向通信的全部内容,如有错误请指出
创作不易,转载请注明出处!