VS2019与fluter通过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双向通信的全部内容,如有错误请指出

创作不易,转载请注明出处!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter MaterialApp API 是 Flutter 提供的一个核心组件,用于创建一个基于 Material Design 风格的应用程序。它是一个如何使用 Material Design 风格的应用程序的入口点。 MaterialApp API 提供了许多属性和方法,用于配置和自定义应用程序的外观和行为。其中一些重要的属性包括: 1. title:应用程序的标题,它将显示在应用程序的任务栏或应用程序切换器上。 2. theme:应用程序的主题,包括颜色、字体和形状等。可以通过 ThemeData 类实例化一个主题对象,并将其分配给 theme 属性。 3. home:应用程序的主页,通常是一个 StatefulWidget 对象。当应用程序启动时,将显示主页的内容。 4. routes:定义应用程序的不同页面的路由,可以使用命名路由的方式进行导航。 5. initialRoute:定义应用程序初始显示的页面路由。当应用程序启动时,将自动跳转到指定的初始路由。 6. onGenerateRoute:定义一个函数,用于根据路由名称动态生成页面。可以通过该函数实现动态路由的功能。 7. navigatorObservers:定义一个列表,用于添加导航观察者。导航观察者可以用于监听页面的导航事件。 通过使用 MaterialApp API,我们可以轻松地创建一个基于 Material Design 风格的应用程序,并对其进行自定义和配置。它为我们提供了许多强大的功能和灵活的选项,使我们能够构建出符合用户期望并风格统一的 Flutter 应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值