End

Flutter 和 Android 通讯 Channel 平台通道

本文地址


目录

Flutter 和原生间相互通讯

Flutter 和 Android 之间一般有三种通讯方式:

  • MethodChanel:用于方法调用(method invocation)
  • EventChannel:用于事件流的发送(event streams)
  • BasicMessageChannel:用于互相主动发送消息,也可用于传递字符串或半结构化的消息

数据类型及映射关系

平台通道数据类型及编解码器

Dart Java Kotlin
null null null
bool java.lang.Boolean Boolean
int java.lang.Integer Int
int, if 32 bits not enough java.lang.Long Long
double java.lang.Double Double
String java.lang.String String
Uint8List byte[] ByteArray
Int32List int[] IntArray
Int64List long[] LongArray
Float32List float[] FloatArray
Float64List double[] DoubleArray
List java.util.ArrayList List
Map java.util.HashMap HashMap

通常情况下,Dart 中的 int 都会被转换成 Java 中的 Long,而非 Int

通道和平台线程

通道和平台线程

  • 目标平台向 Flutter 发起 channel 调用的时候,需要在对应平台的主线程执行。同样的,在 Flutter 向目标平台发起 channel 调用的时候,需要在 root Isolate 中执行。
  • 目标平台侧的 handler 既可以在平台的主线程执行,也可以通过 Task Queue 在后台执行。
  • 目标平台侧的 handler 的返回值可以在任意线程异步执行。

简单来说就是:

  • 平台方法必须在 主线程 上互调,UI 相关的操作必须在 主线程 上调用
  • 回调线程不做要求,即可以同步也可以异步,具体可以使用哪种方式要看 api 是怎么设计的
  • 具体来说,Android 中普通 MethodCallHandleronMethodCall 方法回调在 主线程,但可以在任意线程执行及响应
  • 而指定 TaskQueuemakeBackgroundTaskQueue 时, onMethodCall 方法回调在 子线程,同样可以在任意线程执行及响应

被注解 @UiThread 标记的方法需要在 UI 线程(即 main 线程、主线程)中执行。

MethodChannel

public MethodChannel(BinaryMessenger messenger, String name) {
  this(messenger, name, StandardMethodCodec.INSTANCE);
}

public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
  this(messenger, name, codec, null);
}

public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec, BinaryMessenger.TaskQueue taskQueue) {}

Android 端代码

fun isMainThread() = Looper.getMainLooper().thread == Thread.currentThread()
fun Handler.postDelayed(delay: Long, runnable: Runnable) = postDelayed(runnable, delay)
class MethodChannelActivity : FlutterActivity() {
    companion object {
        private const val CHANNEL_NAME = "com.bqt.test/base_channel"
        private const val METHOD_NAME = "getBatteryLevel"
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
        MethodChannel(binaryMessenger, CHANNEL_NAME).setMethodCallHandler { call, result ->
            when (call.method) {
                METHOD_NAME -> onCallGetBatteryLevel(result)
                else -> result.notImplemented()
            }
        }
    }

    private fun onCallGetBatteryLevel(result: MethodChannel.Result) {
        println("onCallGetBatteryLevel, isMainThread:${isMainThread()}") // true
        Thread {
            val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
            val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
            if (batteryLevel != -1) {
                result.success(batteryLevel) // 可在子线程响应请求
                println("result callback, isMainThread:${isMainThread()}") // false
            } else {
                result.error("UNAVAILABLE", "Battery level not available.", null)
            }
        }.start()
    }
}

Flutter 端代码

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: '演示 MethodChannel'),
    );
  }
}

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('com.bqt.test/base_channel');
  String _batteryLevel = '';
  int _counter = 0;

  void _incrementCounter() {
    setState(() => _counter++); // This call to setState causes rerun the build method below
    _getBatteryLevel().then((value) {
      debugPrint(value);
      setState(() => _batteryLevel = value);
    });
  }

  Future<String> _getBatteryLevel() async {
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      return '电量 $result % .';
    } on PlatformException catch (e) {
      return "获取失败: '${e.message}'.";
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('这是标题')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('电量 $_batteryLevel'),
              Text('点击次数 $_counter'),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(onPressed: _incrementCounter),
      ),
    );
  }
}

TaskQueue:回调在子线程

要在 channel 对应的平台侧的后台中执行 handler,需要使用 TaskQueue API。

class TaskQueueActivity : FlutterActivity() {
    companion object {
        private const val CHANNEL_NAME = "com.bqt.test/base_channel"
        private const val METHOD_NAME = "getVersionCode"
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
        val taskQueue: BinaryMessenger.TaskQueue = binaryMessenger.makeBackgroundTaskQueue()
        val methodCodec: MethodCodec = StandardMethodCodec.INSTANCE
        val methodChannel = MethodChannel(binaryMessenger, CHANNEL_NAME, methodCodec, taskQueue)
        methodChannel.setMethodCallHandler { call, result ->
            when (call.method) {
                METHOD_NAME -> onCallGetVersionCode(result)
                else -> result.notImplemented()
            }
        }
    }

    private fun onCallGetVersionCode(result: MethodChannel.Result) {
        println("onCallGetVersionCode, isMainThread:${isMainThread()}") // false
        result.success(BuildConfig.VERSION_CODE)
    }
}

EventChannel

EventChannel 用于事件流的发送(event streams),可进行持续性的单向通信,只能是 Android/iOS 端主动调用,常用于传递原生设备的信息、状态等。

public EventChannel(BinaryMessenger messenger, String name) {
  this(messenger, name, StandardMethodCodec.INSTANCE);
}

public EventChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
  this(messenger, name, codec, null);
}

public EventChannel(BinaryMessenger messenger, String name, MethodCodec codec, BinaryMessenger.TaskQueue taskQueue) {}

Android 端代码

class EventChannelActivity : FlutterActivity() {

    companion object {
        const val METHOD_CHANNEL_PATH = "com.bqt.test/method_channel"
        const val EVENT_CHANNEL_PATH = "com.bqt.test/enent_channel"
        const val mMillisInFuture: Long = 20000 // The number of millis in the future from the call to start() until the countdown is done and onFinish() is called
        const val countDownInterval: Long = 1000 // The interval along the way to receive onTick(long) callbacks
    }

    private var mEventSink: EventChannel.EventSink? = null
    private var mTimer: CountDownTimer = object : CountDownTimer(mMillisInFuture, countDownInterval) {
        override fun onTick(millisUntilFinished: Long) {
            println("onTick,millisUntilFinished: $millisUntilFinished, isMainThread:${isMainThread()}") // true
            mEventSink?.success("剩余时间 $millisUntilFinished")  // 发送消息通知
            //mEventSink?.endOfStream() // 通知结束,此时 EventSink 的 onCancel 会被调用
        }

        override fun onFinish() {
            println("onFinish,isMainThread:${isMainThread()}") // true
            mEventSink?.error("error_code", "error_message", "error_details") // 发送错误通知
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
        val mMethodChannel = MethodChannel(binaryMessenger, METHOD_CHANNEL_PATH)
        mMethodChannel.setMethodCallHandler { call, result ->
            when (call.method) {
                "startTimer" -> mTimer.start().also { result.success("start success") }
                "stopTimer" -> mTimer.cancel().also { result.success("stop success") }
                else -> result.notImplemented()
            }
        }
        val mEventChannel = EventChannel(binaryMessenger, EVENT_CHANNEL_PATH)
        mEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
            override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                println("onListen(建立连接),arguments: $arguments, isMainThread:${isMainThread()}") // true
                mEventSink = events
            }

            override fun onCancel(arguments: Any?) {
                println("onCancel(断开连接),arguments: $arguments, isMainThread:${isMainThread()}") // true
                mEventSink = null
            }
        })
    }
}

Flutter 端代码

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

class _MyHomePageState extends State<MyHomePage> {
  static const _methodChannel = MethodChannel('com.bqt.test/method_channel');
  static const _eventChannel = EventChannel('com.bqt.test/enent_channel');
  StreamSubscription? _streamSubscription;

  String _millisUntilFinished = "默认值";
  String _state = "默认值";
  bool _hasStart = false;

  @override
  void initState() {
    super.initState();
    debugPrint("initState");
    _streamSubscription = _eventChannel.receiveBroadcastStream().listen((data) {
      debugPrint("返回值: $data");
      _millisUntilFinished = data;
      setState(() => _millisUntilFinished);
    }, onError: (error) {
      debugPrint("onError : ${error.runtimeType} ${error.toString()}");
    }, onDone: () {
      debugPrint("onDone");
    }, cancelOnError: false); // 遇到错误时是否自动结束监听
  }

  void _pauseOrResume() {
    StreamSubscription? temp = _streamSubscription;
    if (temp != null) {
      if (temp.isPaused) {
        debugPrint("resume");
        _streamSubscription?.resume(); // Resumes after a pause
      } else {
        debugPrint("pause"); // pause 后,客户端发送的数据会在 resume 时一次性全部补发回来
        _streamSubscription?.pause(); // Requests that the stream pauses events until further notice
        //_streamSubscription?.cancel(); // Cancels this subscription
      }
    }
  }

  void _startOrStop() {
    _callStartOrStop().then((value) {
      debugPrint("返回值: $value");
      _state = value;
      setState(() => _state);
    });
    _hasStart = !_hasStart;
  }

  Future<String> _callStartOrStop() async {
    try {
      if (_hasStart) {
        return await _methodChannel.invokeMethod('stopTimer');
      } else {
        return await _methodChannel.invokeMethod('startTimer');
      }
    } on PlatformException catch (e) {
      return "Exception: ${e.message}";
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(_state),
            Text(_millisUntilFinished),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(onPressed: _startOrStop),
      bottomNavigationBar: FloatingActionButton(onPressed: _pauseOrResume),
    );
  }
}

BasicMessageChannel

BasicMessageChannel 用于在 Flutter 和 native 互相主动发送消息,也可用于传递字符串或半结构化的消息,并支持使用自定义的消息编解码器

Pigeon 就是基于 BasicMessageChannel 实现的。

public BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec) {
  this(messenger, name, codec, null);
}

public BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec, BinaryMessenger.TaskQueue taskQueue) {}

Android 端代码

class BasicMessageChannelActivity : FlutterActivity() {

    private var mBasicMessageChannel: BasicMessageChannel<String>? = null

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
        val messageCodec: MessageCodec<String> = StringCodec.INSTANCE // 指定编解码器
        mBasicMessageChannel = BasicMessageChannel(binaryMessenger, "com.bqt.test/basic_channel", messageCodec)
        mBasicMessageChannel?.resizeChannelBuffer(5)
        mBasicMessageChannel?.setMessageHandler { message, reply ->
            println("onMessage, message: $message,isMainThread:${isMainThread()}") // true
            reply.reply("已收到【$message】") // 只能 reply 一次
        }

        Handler(mainLooper).apply {
            postDelayed(2000) { sendMessage() } // 可以多次调用 send
            postDelayed(5000) { sendMessage() }
        }
    }

    private fun sendMessage() = mBasicMessageChannel?.send("当前时间 ${System.currentTimeMillis()}") { reply ->
        println("reply: $reply, isMainThread:${isMainThread()}") // true
    }
}

Flutter 端代码

class _MyHomePageState extends State<MyHomePage> {
  static const BasicMessageChannel<String> _channel = BasicMessageChannel('com.bqt.test/basic_channel', StringCodec());
  String _message = "message";
  String _response = "response";

  @override
  void initState() {
    super.initState();
    debugPrint("initState");
    _channel.setMessageHandler((String? message) async {
      debugPrint("message: $message"); // 收到的 native 发送的消息
      _message = message ?? "null";
      setState(() => _message);
      return '已收到【$message】'; // 对 native 的响应
    });
  }

  void _request() async {
    final String? response = await _channel.send('来自 Dart 的请求'); // 对 native 发起请求
    debugPrint("response: $response"); // 收到的 native 响应
    _response = response ?? "null";
    setState(() => _response);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(_response),
            Text(_message),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(onPressed: _request),
    );
  }
}

2022-5-22

posted @ 2022-05-22 02:44  白乾涛  阅读(1700)  评论(0编辑  收藏  举报