我的文章讲过:(0085)iOS开发之OC与JS交互高级用法(JavaScriptCore)
前言:我们知道OC 在UIWebView 加载 H5中,常常需要OC 与 H5 之间进行交互调用。采取的方法有:
- WebViewJavascriptBridge
- JavaScriptCore 框架
- 拦截URL
特征:都通过一种中间人进行桥接实现相互调用的。
今天讲的:Flutter与原生OC、Java的交互通信 也是一种桥接的方式实现的。类似WebViewJavascriptBridge、JavaScriptCore
的方式。Flutter 提供了 platform channels ,来和管理你的 Flutter view 的 ViewController 通信和交互数据。平台管道本质上是一个异步通信机制,桥接了 Dart 代码和宿主 ViewController,以及它运行于的 iOS 框架。你可以用平台管道来执行一个原生的函数,或者是从设备的传感器中获取数据。
本文翻译自官方文档:Writing custom platform-specific code
platform channel:平台通道
host;主机(这里指iOS 安卓端代码)
client:客户端(这里指Flutter端代码)
译文:
本指南介绍如何编写特定于平台的自定义代码。某些特定平台的功能可以通过现有的软件包获得;请参见using packages。
Flutter使用一个灵活的系统,允许您在Android平台上使用Java或Kotlin代码调用平台特定API,或者在Objective-C或Swift代码上调用iOS。
Flutter平台特定的API支持不依赖代码生成,而是依赖灵活的消息传递风格:
平台通道
向其host
(这个应用程序的iOS、Android部分)发送消息。host
通过平台通道
监听并接收消息。然后,它使用native programming language
调用任意数量特定平台的API,同时向client
(应用程序的flutter部分)发送响应。⚠️:如果您需要在Java/Kotlin/Objective-C or Swift中使用对应平台的APIs or libraries,本指南将使用
平台通道
机制来说明。但您也可以通过查看defaultTargetPlatform属性,在你的Flutter app中编写特定平台的Dart代码。Platform adaptations 在这个框架中Flutter自动帮我们做了一些特定平台的适配。
消息通过平台通道
在the client (UI) and host (platform) 之间传递,如图所示:
消息和响应是异步传递的,以确保用户界面保持响应。
⚠️注意:即使flutter异步地向Dart发送消息和从Dart接受消息,每当调用通道方法时,必须在平台的主线程上调用该方法。有关更多信息,请参阅线程部分。
On the client
side, MethodChannel (API)
能发送message
并能响应方法回调,On the platform
side,在Android 的(API)MethodChannel
和iOS的(API) FlutterMethodChannel
能接收到方法调用并返回一个结果。这些类允许您用很少的“样板”代码开发平台插件。
补充:如果需要,方法调用也可以反向发送消息,用平台充当客户端在Dart中实现方法。快速动作插件quick_actions就是一个具体的例子。
标准平台通道使用标准消息编解码器,该编解码器支持对类似于JSON的简单值(booleans, numbers, Strings, byte buffers, and List and Maps)进行有效的二进制序列化(有关详细信息,请参阅StandardMessageCodec编解码器)。当发送和接收值时,这些值与消息之间的序列化和反序列化将自动进行。
下面代码演示如何调用特定平台的API来取回并显示当前电池电量。它通过平台消息的一个消息getBatteryLevel()
来使用Android BatteryManager API
和ios device.batteryLevel API
实现的。
该示例将特定平台的代码添加到主应用程序本身中。如果您想为多个应用程序重用特定平台的代码,那么项目创建步骤略有不同(请参见开发包),但是平台通道代码仍然是以相同的方式编写的。
注意:这个例子的完整的可运行源代码(Android with Java and iOS with Objective-C)可以在/examples/platform_channel/找到。For iOS with Swift, see /examples/platform_channel_swift/。
这里我已经有一个工程hello,跳过此步。
a single platform method
来构造通道。通道的客户端和主机端通过通道构造函数中传递的通道名称连接。单个应用程序中使用的所有频道名称必须是唯一的;在频道名称前面加上唯一的“域前缀”,例如:samples.flutter.dev/battery
。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class MethodChannelTestViewState extends State {
static const platform = const MethodChannel('samples.flutter.dev/battery');
// Get battery level.
}
platform
调用invokeMethod
来调用一个字符串标识符为getBatteryLevel
一个具体方法。这个方法在特定平台实现。调用可能会失败,例如,如果平台不支持platform
API(例如在模拟器中运行时),因此请将invokeMethod
调用包装在try catch语句中。用返回的结果来更新setstate内电池级别的用户界面状态。
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('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: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel),
],
),
),
);
}
注意:以下步骤使用Objective-C。如果您喜欢使用Swift,请跳到步骤4b。
首先在Xcode中打开flutter应用程序的iOS主机部分:
AppDelegate.m
AppDelegate.m
对应代码#import
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.dev/battery"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
// Note: this method is invoked on the UI thread.
// TODO
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
AppDelegate
类中@end
之前添加以下方法:- (int)getBatteryLevel {
UIDevice* device = UIDevice.currentDevice;
device.batteryMonitoringEnabled = YES;
if (device.batteryState == UIDeviceBatteryStateUnknown) {
return -1;
} else {
return (int)(device.batteryLevel * 100);
}
}
setMethodCallHandler()
方法。您需要处理调用getBatteryLevel()
以此来测试返回的值,并使用result参数返回成功和错误情况的响应。如果调用了未知的方法,请报告该方法:unknown method is called
。__weak typeof(self) weakSelf = self
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
// Note: this method is invoked on the UI thread.
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Battery info unavailable"
details:nil]);
} else {
result(@(batteryLevel));
}
} else {
result(FlutterMethodNotImplemented);
}
}];
完成:您现在应该可以在iOS上运行该应用程序了。如果使用iOS模拟器,请注意它不支持电池API,并且应用程序显示“电池信息不可用”。
效果如下:这样就实现了Flutter与原生OC的通信。为了更好的验证。我还增加了一个方法getFromOCClientMessage
。
flutter:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
class MethodChannelTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MethodChannelTestView();
}
}
class MethodChannelTestView extends StatefulWidget {
@override
State createState() {
// TODO: implement createState
return MethodChannelTestViewState();
}
}
class MethodChannelTestViewState extends State {
static const platform = const MethodChannel('samples.flutter.dev/battery');
final textColorStyle = TextStyle(color: Colors.deepOrange);
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
// 测试从OC 端传过来的字符串
String _messageFromOC = '默认Flutter';
Future _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
print(batteryLevel);
setState(() {
_batteryLevel = batteryLevel;
});
}
// 测试Flutter 与OC 端通信
Future _getFromOCClientMessage () async {
String message;
try {
final String result = await platform.invokeMethod('getFromOCClientMessage');
message = 'Message 来自 $result % .';
} on PlatformException catch (e) {
message = "Failed to get Message: '${e.message}'.";
}
print('我获取到从OC 传过来的值 :$message');
setState(() {
_messageFromOC = message;
});
}
void _pushSaved() {
print('你哈后!');
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('我的收藏列表'),
actions: [
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel,style: textColorStyle),
SizedBox(
height: 40,
),
RaisedButton(
child: Text('Get Message From OC'),
onPressed: _getFromOCClientMessage,
),
Text(_messageFromOC,style: textColorStyle,),
],
),
),
);
}
}
OC 端代码:
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
FlutterMethodChannel *batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.dev/battery"
binaryMessenger:controller];
__weak typeof(self) weakSelf = self;
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
// Note: this method is invoked on the UI thread.
if ([@"getBatteryLevel" isEqualToString:call.method])
{
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE" message:@"Battery info unavailable" details:nil]);
} else {
result(@(batteryLevel));
}
} else if ([@"getFromOCClientMessage" isEqualToString:call.method]) {
NSString *testMethod = [weakSelf getFromOCClientMessage];
result(testMethod);
} else {
result(FlutterMethodNotImplemented);
}
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (int)getBatteryLevel {
UIDevice* device = UIDevice.currentDevice;
device.batteryMonitoringEnabled = YES;
if (device.batteryState == UIDeviceBatteryStateUnknown) {
return -1;
} else {
return (int)(device.batteryLevel * 100);
}
}
- (NSString *)getFromOCClientMessage {
return @"我来自OC client!!!";
}
@end
不再翻译和Step 4a 相同。只是改用swift语法实现即可。
同理:
MainActivity.java
MainActivity.java
对应代码import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
// Note: this method is invoked on the main thread.
// TODO
}
});
}
}
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;
import android.os.Bundle;
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
setMethodCallHandler()
方法。您需要处理调用getBatteryLevel()
以此来测试返回的值,并使用result参数返回成功和错误情况的响应。如果调用了未知的方法,请报告该方法:unknown method is called
。@Override
public void onMethodCall(MethodCall call, Result result) {
// Note: this method is invoked on the main thread.
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';
class MethodChannelTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MethodChannelTestView();
}
}
class MethodChannelTestView extends StatefulWidget {
@override
State createState() {
// TODO: implement createState
return MethodChannelTestViewState();
}
}
class MethodChannelTestViewState extends State {
static const platform = const MethodChannel('samples.flutter.dev/battery');
final textColorStyle = TextStyle(color: Colors.deepOrange);
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
// 测试从OC 端传过来的字符串
String _messageFromOC = '默认Flutter';
// 测试从AD 端传过来的字符串
String _messageFromAD = '默认Flutter';
Future _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
print(batteryLevel);
setState(() {
_batteryLevel = batteryLevel;
});
}
// 测试Flutter 与OC 端通信
Future _getFromOCClientMessage () async {
String message;
try {
final String result = await platform.invokeMethod('getFromOCClientMessage');
message = 'Message 来自 $result % .';
} on PlatformException catch (e) {
message = "Failed to get Message: '${e.message}'.";
}
print('我获取到从OC 传过来的值 :$message');
setState(() {
_messageFromOC = message;
});
}
// 测试Flutter 与AD 端通信
Future _getFromADClientMessage () async {
String message;
try {
final String result = await platform.invokeMethod('getFromADClientMessage');
message = 'Message 来自 $result % .';
} on PlatformException catch (e) {
message = "Failed to get Message: '${e.message}'.";
}
print('我获取到从AD 传过来的值 :$message');
setState(() {
_messageFromAD = message;
});
}
void _pushSaved() {
print('你哈后!');
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('我的收藏列表'),
actions: [
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel,style: textColorStyle),
SizedBox(
height: 40,
),
RaisedButton(
child: Text('Get Message From OC'),
onPressed: _getFromOCClientMessage,
),
Text(_messageFromOC,style: textColorStyle,),
SizedBox(
height: 40,
),
RaisedButton(
child: Text('Get Message From Andriod'),
onPressed: _getFromADClientMessage,
),
Text(_messageFromAD,style: textColorStyle,),
],
),
),
);
}
}
Java 端
package com.example.hello;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
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;
import android.util.Log;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
public void onMethodCall(MethodCall call, Result result) {
String method = call.method;
//Log.d(method, "The onStart() event");
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else if (call.method.equals("getFromADClientMessage")) {
String message = getFromADClientMessage();
result.success(message) ;
} else {
result.notImplemented();
}
}
});
GeneratedPluginRegistrant.registerWith(this);
}
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
private String getFromADClientMessage() {
return "我来自Android";
}
}
效果如图:
完成了Flutter与原生OC、Java的交互通信!???!