likes
comments
collection
share

Flutter与原生系统Android之间的通信序言 Flutter是一个UI框架,但是当您需要访问超出Flutter范

作者站长头像
站长
· 阅读数 19

序言

Flutter是一个UI框架,但是当您需要访问超出Flutter范围的功能时会发生什么,就需要从系统底层获取信息,也就是需要和特定的系统语言的交互。这就是方法通道发挥作用的地方。在这篇博文中,我们将探讨如何使用方法通道将原生Android代码无缝集成到Flutter应用中。Flutter使用灵活的系统,允许您使用可直接使用这些API的语言来调用特定于平台的API:

  • Android上的Kotlin或Java
  • iOS上的Swift或Objective-C
  • Windows上的C++
  • macOS上的Objective-C
  • Linux上的C 本文只讲解和Android系统底层的交互,原理是一样的。

浅析方法通道的定义

方法通道为Flutter Dart代码和原生代码(本例中为Android Java/Kotlin)之间的通信提供了桥梁。这让您可以从Flutter应用调用原生层中的方法,让您同时享受两全其美的优势。方法通道的常见用例有如下所示:

  1. 调用原生API
  2. 在Flutter和原生代码之间共享数据
  3. 在Flutter和原生代码之间发送事件
  4. 与第三方库集成

平台通道图解

消息通过平台通道在客户端(UI)和主机(平台)之间传递,如下图所示: Flutter与原生系统Android之间的通信序言 Flutter是一个UI框架,但是当您需要访问超出Flutter范 消息和响应异步传递,以确保用户界面保持响应。

数据类型如何编解码

标准平台通道使用标准消息编解码器,支持对简单的JSON 类值(例如布尔值、数字、字符串、字节缓冲区以及这些值的Lists和Maps)进行高效的二进制序列化(有关详细信息,请参阅StandardMessageCodec)。当您发送和接收值时,这些值与消息之间的序列化和反序列化会自动发生。Flutter和Android之间的数据类型的映射,如下图所示:

Flutter与原生系统Android之间的通信序言 Flutter是一个UI框架,但是当您需要访问超出Flutter范

方法通道逐步指南

  1. 在Flutter Dart代码中,首先创建一个方法通道并定义要在原生端调用的方法。示例代码如下:
  static const platform = MethodChannel('samples.flutter.dev/battery');
  // Get battery level.
  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final result = await platform.invokeMethod<int>('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

方法通道的名称和调用的方法名称要具有唯一性,不能重复。

2.处理Android上的方法通道。在原生Android端,您需要设置与Flutter中定义名称相同的方法通道。这将允许您有效地处理方法调用。如果方法调用的名称相同(和getBatteryLevel进行比较),则调用Android的本地方法。结果可以返回成功,调用result.success()方法;结果可以返回异常,调用result.error()方法;结果可以没有实现,调用result.notImplemented()方法。以下是 Kotlin中的完整示例代码片段:

package com.example.batterylevel

import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
class MainActivity: FlutterActivity() {
  private val CHANNEL = "samples.flutter.dev/battery"

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      // This method is invoked on the main thread.
      call, result ->
      if (call.method == "getBatteryLevel") {
        val batteryLevel = getBatteryLevel()

        if (batteryLevel != -1) {
          result.success(batteryLevel)
        } else {
          result.error("UNAVAILABLE", "Battery level not available.", null)
        }
      } else {
        result.notImplemented()
      }
    }
  }

  private fun getBatteryLevel(): Int {
    val batteryLevel: Int
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    } else {
      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
    }

    return batteryLevel
  }
}

3.最后,您可以通过调用定义方法通道的相应函数从Flutter UI触发本地方法。这将通过方法通道启动通信并调用相应的本地方法。Flutter完整端的示例代码如下:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const platform = MethodChannel('samples.flutter.dev/battery');
  // Get battery level.
  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final result = await platform.invokeMethod<int>('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            ElevatedButton(
              onPressed: _getBatteryLevel,
              child: const Text('Get Battery Level'),
            ),
            Text(_batteryLevel),
          ],
        ),
      ),
    );
  }
}

效果图如下:

Flutter与原生系统Android之间的通信序言 Flutter是一个UI框架,但是当您需要访问超出Flutter范

BasicMessageChannel

用于传递字符串和半结构化的信息,这个用的比较少。用于Flutter和Native之间的双向交流通信。

EventChannel

EventChannel:用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端,或者注册定位信息,经纬度变化之后,直接向Flutter端直接推送数据。EventChannel是一个stream流,会一直返回数据。

总结

方法通道(MethodChannel)是一种强大的工具,可将原生功能无缝集成到您的Flutter应用中。此方法允许您利用Flutter和原生Android代码的功能,提供统一的用户体验。基本信息通道(BasicMessageChannel)是一个可以和原生进行交换消息的方式。EventChannel是一个和原生进行流式数据交换的方式。这三种方式,各自都有自己的应用场景和优势。祝您编码愉快!

参考资料

docs.flutter.dev/platform-in… api.flutter.dev/flutter/ser…

转载自:https://juejin.cn/post/7402060970425892873
评论
请登录