场景

在使用 Flutter 进行功能模块或者整体项目的开发时,如果需要(阶段性)频繁地和某个第三方 Native-SDK 进行交互,而该 Native-SDK 没有实现 Flutter 插件版本的情况下,如果直接把这部分交互 API 加入到原有的 channel 类里面,会使得该 channel 类变得臃肿,造成代码维护及迭代上的难度;而且,对于一些平常比较少用到的功能,也不需要一上来就初始化 channel-api,占用空间,而是在使用时才初始化,使用后也可以手动清除;

在此提出一种“Flutter 与第三方 Native-SDK 的交互代理方案”,通过一种“代理包装”的方式,尝试较好地解决以上提到的问题;

效果

该方案使用的是 Flutter+Android 进行 demo,全屏 loading、底部灰底 toast 为 Android 端实现,方块 loading、中间黑底 toast 为 Flutter 端实现

范例

需要使用时才开启 channel,使用结束后关闭 channel 监听

class _TestPageState extends State {

@override

void initState() {

super.initState();

}

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: const Text('Channel')),

body: Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,

children: [

GestureDetector(

onTap: () => channelTest(),

child: const Text('开启通道,进行交互'),

),

],

),

),

);

}

Future channelTest() async {

TestChannelProxy? channel = await TestChannelProxy.openChannel();

if (channel != null) {

Function()? cancel;

channel.doSomethingAndListener(DoSomethingCallback(

onTestFirst: (result) {

cancel = MsgUtil.loading(msg: result);

},

onTestSecond: () {

cancel?.call();

},

onTestThird: (result) async {

MsgUtil.toast(result.toString());

return !result;

},

));

}

}

}

代码(Flutter 端代理类)

维护与 Native 端的交互,使用者不需要知道具体交互逻辑,但是在该代理类中,需要保证每次有回调的交互都能正确回调(不管是状态为成功的回调,还是状态为失败的回调)

import 'package:flutter/services.dart';

import 'platform_method_channel.dart';

const String _testChannelName = 'zzq.channel.TestChannelProxy';

class TestChannelProxy {

late MethodChannel _channel;

DoSomethingCallback? _callback;

TestChannelProxy._() {

_channel = const MethodChannel(_testChannelName);

_channel.setMethodCallHandler(_handleMethod);

}

/// [return] 为空表示开启失败,不为空表示开启成功

static Future openChannel() async {

bool success = await PlatformMethodChannel.getInstance()

.openPlatformChannel(_testChannelName);

return success ? TestChannelProxy._() : null;

}

/*Future closeChannel() async {

await _channel.invokeMethod('closeChannel');

_channel.setMethodCallHandler(null);

}*/

// ------------------------------ flutter 调用 native ------------------------------

void doSomethingAndListener(DoSomethingCallback callback) {

_callback = callback;

_channel.invokeMethod('doSomethingAndListener');

}

// ------------------------------ native 调用 flutter ------------------------------

Future _handleMethod(MethodCall call) async {

var args = call.arguments;

switch (call.method) {

case 'getString':

return _getString();

case 'getBoolean':

return _getBoolean();

case 'onTestFirst':

_onTestFirst(args);

break;

case 'onTestSecond':

_onTestSecond();

break;

case 'onTestThird':

return _onTestThird(args);

default:

break;

}

}

Future _getString() async {

await Future.delayed(const Duration(seconds: 2));

return 'String';

}

Future _getBoolean() async {

await Future.delayed(const Duration(seconds: 2));

return true;

}

void _onTestFirst(String result) {

_callback?.onTestFirst?.call(result);

}

void _onTestSecond() {

_callback?.onTestSecond?.call();

}

Future _onTestThird(bool result) async {

return (await _callback?.onTestThird?.call(result)) ?? false;

}

}

class DoSomethingCallback {

final Function(String result)? onTestFirst;

final Function()? onTestSecond;

final Future Function(bool result)? onTestThird;

DoSomethingCallback({

this.onTestFirst,

this.onTestSecond,

this.onTestThird,

});

}

需要在默认/全局的 channel 中定义通用的开启 channel 的 api,提供代理交互 channel 的开启能力

/// 开启 flutter 与 native 端的交互通道,用于使用第三方 native-sdk 的能力

/// 可以是 BasicMessageChannel、MethodChannel、EventChannel

/// [return] 通道是否开启成功

Future openPlatformChannel(String channelName) async {

var result = await _channel.invokeMethod('openPlatformChannel', channelName);

return result == true;

}

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {

val args = call.arguments

when (call.method) {

"openPlatformChannel" -> openPlatformChannel(args as String, result)

else -> result.notImplemented()

}

}

/**

* 开启 flutter 与 native 端的交互通道,用于 flutter 端使用第三方 native-sdk 的能力

* 可以是 BasicMessageChannel、MethodChannel、EventChannel

* [result.success] 通道是否开启成功

*/

private fun openPlatformChannel(channelName: String, result: MethodChannel.Result) {

var success = true

when (channelName) {

TEST_CHANNEL_NAME -> TestTool.openChannel(binaryMessenger)

else -> success = false

}

result.success(success)

}

代码(Android 端第三方 Native-SDK 包装类)

该类用于包装第三方 Native-SDK 所提供的能力,同时持有、维护 Flutter-Android-channel 中在 Android 端的代理 channel 实例;即该类负责 SDK 能力使用及拓展,而 channel 代理类只负责通讯(发起及监听等)

import io.flutter.plugin.common.BinaryMessenger

import kotlinx.coroutines.CoroutineScope

import kotlinx.coroutines.Dispatchers

import kotlinx.coroutines.delay

import kotlinx.coroutines.launch

object TestTool {

private var testChannelProxy: TestChannelProxy? = null

fun openChannel(messenger: BinaryMessenger) {

if (testChannelProxy == null) {

synchronized(TestTool::class.java) {

if (testChannelProxy == null) {

testChannelProxy = TestChannelProxy(messenger)

}

}

}

}

/*fun closeChannel() {

testChannelProxy = null

}*/

fun doSomethingAndListener() {

DialogManager.showLoading()

testChannelProxy?.getBoolean { it ->

DialogManager.hideLoading()

if (it) {

CoroutineScope(Dispatchers.Main).launch {

delay(2000)

testChannelProxy?.onTestFirst("点了")

delay(2000)

testChannelProxy?.onTestSecond()

delay(2000)

testChannelProxy?.onTestThird(true) {

DialogManager.toast(it.toString())

}

}

}

}

}

}

代码(Android 端代理类)

维护与 Flutter 端的交互,使用者不需要知道具体交互逻辑,但是在该代理类中,需要保证每次有回调的交互都能正确回调(不管是状态为成功的回调,还是状态为失败的回调)

import android.os.Handler

import android.os.Looper

import io.flutter.plugin.common.BinaryMessenger

import io.flutter.plugin.common.MethodCall

import io.flutter.plugin.common.MethodChannel

const val TEST_CHANNEL_NAME = "zzq.channel.TestChannelProxy"

class TestChannelProxy(messenger: BinaryMessenger) : MethodChannel.MethodCallHandler {

private val channel: MethodChannel

init {

channel = MethodChannel(messenger, TEST_CHANNEL_NAME)

channel.setMethodCallHandler(this)

}

// ------------------------------ flutter 调用 native ------------------------------

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {

val args = call.arguments

when (call.method) {

"doSomethingAndListener" -> TestTool.doSomethingAndListener()

else -> result.notImplemented()

}

}

/*fun closeChannel() {

channel.setMethodCallHandler(null)

TestTool.closeChannel()

}*/

// ------------------------------ native 调用 flutter ------------------------------

/**

* Methods marked with @UiThread must be executed on the main thread. Current thread: AsyncTask

*/

private fun mainInvokeMethod(method: String, arguments: Any?, callback: MethodChannel.Result?) {

if (Looper.myLooper() == Looper.getMainLooper()) {

channel.invokeMethod(method, arguments, callback)

} else {

Handler(Looper.getMainLooper()).post {

channel.invokeMethod(method, arguments, callback)

}

}

}

fun getString(callback: (string: String?) -> Unit) {

mainInvokeMethod(

"getString",

null,

object : MethodChannel.Result {

override fun success(result: Any?) {

callback(if (result is String) result else null)

}

override fun error(code: String, msg: String?, details: Any?) {

callback(null)

}

override fun notImplemented() {

callback(null)

}

})

}

fun getBoolean(callback: (boolean: Boolean) -> Unit) {

mainInvokeMethod(

"getBoolean",

null,

object : MethodChannel.Result {

override fun success(result: Any?) {

callback(result == true)

}

override fun error(code: String, msg: String?, details: Any?) {

callback(false)

}

override fun notImplemented() {

callback(false)

}

})

}

fun onTestFirst(result: String) {

mainInvokeMethod("onTestFirst", result, null)

}

fun onTestSecond() {

mainInvokeMethod("onTestSecond", null, null)

}

fun onTestThird(result: Boolean, callback: (success: Boolean) -> Unit) {

mainInvokeMethod(

"onTestThird",

result,

object : MethodChannel.Result {

override fun success(result: Any?) {

callback(result == true)

}

override fun error(code: String, msg: String?, details: Any?) {

callback(false)

}

override fun notImplemented() {

callback(false)

}

})

}

}

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: