Flutter中dart和原生代码的通信之MethodChannel

前言

据我所知,真正掌握Flutter的Android开发工程师的比例并不是很高,如果你不这么认为,可能是因为你的圈子比较高端。你知道Android原生开发工程师把所有知识体系融汇贯通需要的时间是多久吗?当然这里把Flutter排除在外。资质顶尖的至少要3年,而大部分人智商属于中等偏上,大概要5年。如果是一个普通的码农,可能要8~10年甚至更久。当然我只是按之前非AI时代来进行估计的,未来AI时代可能会略有降低。10年啊,也就是一个普通的程序员需要从毕业干到快35岁危机时才能真正腾出精力来仔细研究Flutter这种跨平台的技术。当然你说我一开始的目标就是Flutter,那没问题,这不在讨论的范畴。

很多安卓开发其实压根没有练手Flutter项目的机会,因为,小公司很难招到这种十年以上的大佬。当然也不会有人从全局角度带你玩转Android Flutter混合开发,除非你运气好,正好有人带。

引入正题

如果完全没接触过Android Flutter混合开发的同学需要先看我这篇文章juejin.cn/post/725412... 。混合开发要想真正落地,不仅需要扎实的原生开发功底,还需要有刻苦钻研新技术的勇气。混合开发避不开一个话题,如何进行通信或者说相互调用的互操作性?这就引入了我们今天的主角------Method Channel。它直译过来就是方法通道,你可以理解成一个调用另一端方法的桥梁。我们看一下怎么写。

通信流程

原生调Dart
kt 复制代码
val intent = FlutterActivity
    .withNewEngine()
    .initialRoute(ROUTE_HOME)
    .build(this)
intent.setClass(this, MyFlutterActivity::class.java)
startActivity(intent)

先启动Flutter界面的原生壳Activity,清单文件该注册注册。

kt 复制代码
package com.doracrypto.crypto.ui.activity

import android.content.Intent
import com.doracrypto.crypto.AppConfig
import dora.util.SPUtils
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MyFlutterActivity : FlutterActivity() {

    companion object {
        private const val CHANNEL = "com.doracrypto.crypto/native"
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "isVipUser" -> {
                        result.success(SPUtils.readBoolean(context, AppConfig.IS_VIP))
                    }
                    "startBuyVipActivity" -> {
                        startActivity(Intent(this@MiniAppActivity, BuyVipActivity::class.java))
                        result.success(null)
                    }
                    else -> result.notImplemented()
                }
            }
    }
}

这东西虽不难,但确是必经之路。在原生侧,我们需要提供各种各样的方法给Dart侧调用。

dart 复制代码
import 'package:flutter/services.dart';

class MethodChannelPlugin {

  static const MethodChannel methodChannel = MethodChannel('com.doracrypto.crypto/native');

  // /// 调用原生浏览器组件
  // static Future<void> gotoWebView(String url) async {
  //   try {
  //     await methodChannel.invokeMethod('gotoWebView',{'url': url});
  //   } on PlatformException {
  //     print('Failed go to gotoWebView');
  //   }
  // }

  /// 检测是否是VIP用户
  static Future<bool> isVipUser() async {
    try {
      final result = await methodChannel.invokeMethod('isVipUser');
      return result == true; // 保证返回 bool
    } on PlatformException catch (e) {
      print('Failed isVipUser: $e');
      return false; // 出错时兜底返回 false
    } catch (e) {
      print('Unexpected error in isVipUser: $e');
      return false;
    }
  }

  /// 跳转购买VIP界面
  static Future<void> startBuyVipActivity() async {
    try {
      await methodChannel.invokeMethod('startBuyVipActivity');
    } on PlatformException catch (e) {
      print('Failed startBuyVipActivity: $e');
    } catch (e) {
      print('Unexpected error in startBuyVipActivity: $e');
    }
  }
}

调用时就这样去调。

dart 复制代码
bool isVip = await MethodChannelPlugin.isVipUser();
if (!isVip) {
  MethodChannelPlugin.startBuyVipActivity();
  return;
}
Dart调原生

主要都是Dart调原生,原生调用Dart的场景不是很多。不多但不代表没有,我这里简单提一嘴。

dart 复制代码
import 'package:flutter/services.dart';

class NativeBridge {

  static const MethodChannel _channel = MethodChannel("com.doracrypto.crypto/dart");

  static void init() {
    // 接收原生调用
    _channel.setMethodCallHandler((call) async {
      if (call.method == "fromNative") {
        String msg = call.arguments as String;
        print("收到原生调用:$msg");
        return "Dart收到: $msg";
      }
      return null;
    });
  }
}

在main.dart里面初始化。

dart 复制代码
void main() {
  // 等待初始化完成 
  WidgetsFlutterBinding.ensureInitialized();
  NativeBridge.init();
  runApp(MyApp());
}

原生侧调用Dart。

kt 复制代码
class MainActivity: FlutterActivity() {

    private lateinit var channel: MethodChannel

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        channel = MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            "com.doracrypto.crypto/dart"
        )
        Handler(Looper.getMainLooper()).postDelayed({
            channel.invokeMethod("fromNative", "Hello from Android")
        }, 3000)
    }
}

通道名称只要唯一就行,并不一定要这样的格式。最后编译aar,三行命令,搞定。

bash 复制代码
flutter clean
flutter pub get
flutter build aar

把编译出的aar包依赖进原生项目就大功告成了,最后正常走打原生包的流程。

写在最后

Flutter是真正的好东西,对创业者相当友好,达到高级开发后,大大节省开发和维护时间,因为只维护一份代码。当你把核心算法用Dart实现后,原生的壳实际上是可以下发给别人开发的,这又进一步节省了开发时间,前提是你的项目先要稳定盈利。欢迎体验混合开发出的app www.pgyer.com/cryptotools

相关推荐
路上^_^2 小时前
安卓基础组件031-Retrofit 网络请求框架
android
计算机毕业设计小帅8 小时前
【2026计算机毕业设计】基于Springboot的Android校园周边美食汇系统
android·spring boot·课程设计
每次的天空8 小时前
Android-MVX技术总结
android
Digitally11 小时前
如何备份和恢复安卓设备2025
android
ClassOps12 小时前
腾讯CODING Maven的aar制品添加上传流程
android·java·maven
ClassOps12 小时前
基于腾讯CODING Maven的Android库发布
android·java·maven
路上^_^13 小时前
安卓基础组件024-底部导航栏
android
加菲猫86014 小时前
文件上传简单的绕过总结
android
一直向钱16 小时前
android 自定义Dialog多种方式
android