####建立插件java
flutter create --org [包前缀]--template=plugin -a kotlin [plugin_name(同时也会做为包的后缀)]android
参数说明:c++
org 包前缀git
template: 插件类型,目前支持建立三种类型的flutter工程(参考flutter create -h)json
a 代表andoid插件语言类型:默认 java 能够改成kotlinswift
i 代表IOS插件语言类型:默认ObjectC 可修改成swiftapi
最后一个参数: 插件名称。也会做为报名的最后一部分。建议使用下划线形式。这个名称会在如下文件内出现:缓存
pubspec.yaml 做为插件名称。外部引用此插件也是用这个名字。
pubspec.yam 中的 flutter 部分,主要用来自动生成相关代码。
plugin:
androidPackage: com.sd.test_plugin
pluginClass: TestPlugin
lib下根目录中的同名文件:
对外暴露的插件文件,能够写逻辑,也能够做为统一入口。export但愿导出的功能
method名称 static const MethodChannel _channel = const MethodChannel('test_plugin');
test下的测试文件。
example下的pubspec.yaml文件。
android文件夹下:setting.gradle,build.gradle.
AndroidManifest中包名。
src中的包结构。
插件桥接原生的类(java,kotlin)文件。此文件会自动将 “_” 变为驼峰形式命名
插件类中的channel 名称,用来和dart端通讯
val channel = MethodChannel(registrar.messenger(), "test_plugin")
复制代码
所以建议生成文件时采用下划线分隔,命名。若是已经生成了插件,想修更名字,记得仔细排查上述文件。注意:methodChannel中的名称必须保证惟一性。最好增长一个包前缀,防止和其余三方插件冲突。此处能够单独修改,不影响插件自己的名称。安全
官方给出的开发Package方法,适用于生成一个较为通用的插件。即和具体业务关系不是很大。逻辑相对独立。好比获取包名,http。打点等。若是和APP业务耦合较多,我的建议采用动态注册插件的方法。bash
从上面命令生成的模版工程能够看出。插件的本质就是原生端(Android,IOS),经过Method和dart端进行通讯。将信息(数据)传递到Dart端。所以只要肯定好信息通道(methodChannel的名称),彻底能够直接在代码中编写相应逻辑。
// 在承载相应 Flutter 内容的 Activity 中 注册MethodChannel。
class MainActivity() : FlutterActivity() {
private val CHANNEL = "com.sd.example.customPlugin"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
// new Method时,会进行channel的绑定。
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
switch (methodCall.method) {
case "getInformation":
// getInformation
result.success("information");
break;
default:
result.notImplemented();
}
}
}
}
复制代码
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('com.sd.example.customPlugin');
platform.invokeMethod("getInformation").then((value) {
// 获取到 “information” 字符串
print(value);
});
}
复制代码
若是仔细观察系统生成插件的代码能够发现以下内容
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
// 注册各个插件
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
复制代码
在注册插件以前,判断插件是否已经注册过。避免重复注册,浪费资源。(重复注册, 会在Map中覆盖以前注册过的channel,能够参考DartMessenger中setMessageHandler方法的实现)建议参照官方实现方法对动态注册过程进行优化。
public static void registerCustomPlugin(PluginRegistry registry) {
// 声明一个Key用来判断是否在此registry中注册过此插件。不必和methodChannel中的名字相同,每个注册过程有一个名字便可。
String key = JtSdk.application.getPackageName() + ".customPlugin";
if (!registry.hasPlugin(key)) {
new MethodChannel(registry.registrarFor(key).messenger(),
Application.getPackageName() + "/nativePlugin").setMethodCallHandler(
// 注意:此方法调用在单独线程中,小心线程同步问题
(methodCall, result) -> {
switch (methodCall.method) {
case "getInformation":
// getInformation
result.success("information");
break;
default:
result.notImplemented();
break;
}
});
}
}
复制代码
#####出场角色介绍
构造方法
MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)
复制代码
BinaryMessenger: 原生Native通讯角色。主要实现类就是FlutterNativeView。上面的构造方法中,能够看出每一个MethodChannel适合一个Messenger绑定的。若是新启动了一个FlutterActivity,默认状况下,是须要从新注册全部插件的(Boost由于复用了FlutterNativeView,所以不须要从新注册)
MethodCodec: 消息编解码器。负责编解码MethodCall,和返回结果。使数据能够经过Native(c++)层在原生,Dart端进行传递。实现类有两种:
各平台类型对应关系
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
int, if 64 bits not enough | java.math.BigInteger | FlutterStandardBigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
建立完成插件以后,就须要在两端编写相应的逻辑代码。一个简单的插件,Dart端几乎不须要编写代码,只负责调用便可。惟一须要注意的是Flutter和原生端运行在不一样的线程中。所以全部跨端的调用都是异步执行的。
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('com.sd.example.customPlugin');
// invodeMethod返回的是一个Future<T> 类型
platform.invokeMethod("getInformation").then((value) {
// 获取到 “information” 字符串
print(value);
});
}
复制代码
错误写法
getInformation() async {
return await platform.invokeMethod("getInformation");
}
test() {
Strng information = getInformation();
print(information);
}
复制代码
原生端主要实现的方法,就在 onMethodCall 中,针对不一样的method,执行不一样逻辑,并经过result,返回结果。
@Override
public void onMethodCall(MethodCall methodCall, Result result) {
switch (methodCall.method) {
case "method1":
var param1 = methodCall.argument("param1"),
var param2 = methodCall.argument("param2");
result.success(param1 + param2);
break;
case "method2":
try{
// do business
result.success(res);
} catch (e) {
result.error("101", "系统错误", "错误缘由");
}
break;
default:
result.notImplemented();
break;
}
复制代码
须要注意如下几点
####使用方法:
和使用第三方库相同,在pubspec.yaml 文件中添加相关依赖。
dependencies:
#经过git形式进行依赖
jt_base_http:
git:
url: ssh://gerrit.rong360.com:29418/flutter_plugins
path: jt_base_http
# 经过版本号依赖
json_annotation: ^2.4.0
# 经过相对路径依赖
jt_base_log:
path: ../
复制代码
而后建立methodChannel,调用相应方法便可。
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('com.sd.example.customPlugin');
platform.invokeMethod("getInformation").then((value) {
// 获取到 “information” 字符串
print(value);
});
}
复制代码
dart -> 原生
// dart自己是单线程模型,对与原生的调用必须经过异步方式执行。
platform.invokeMethod("xxx") 方法自己也是返回一个Future对象。
const platform = const MethodChannel('channel_name');
await platform.invokeMethod("xxx");
-> invokeMethod源码
platform_channel.dart
@optionalTypeArgs
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != null);
// 关键代码,经过 BinaryMessenger 以ByteData形式发送 message
final ByteData result = await binaryMessenger.send(
// channel_name
name,
// ByteData 包含方法名称和相关参数。不管使用Json仍是Standard codec。都会编码为二进制
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name');
}
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}
->
binary_messenger.dart
@override
Future<ByteData> send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
// 真正发送message的方法
return _sendPlatformMessage(channel, message);
}
Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
// 注意:Completer 其内部包含一个future对象,供dart isolate执行。至于这个future对象什么时候结束,又completer 本身控制
final Completer<ByteData> completer = Completer<ByteData>();
// 发送message方法到JNI层,同时封装一个回调 PlatformMessageResponseCallback 用来接受原生端的respone,并结束这个 future
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}
->
window.dart
// callback定义
typedef PlatformMessageResponseCallback = void Function(ByteData data);
// JNI方法。
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
复制代码
到这里,咱们能够知道,invokeMethod方法,其实就是包装一些请求参数(methodname,params),经过codec加密。并返回一个 Future 放在dart event queue中执行,至于这个Future什么时候结束,彻底由native端什么时候调用complete决定。
继续JNI层,看下咱们的请求到底去了哪里。切入点是'Window_sendPlatformMessage'
lib/ui/window/window.cc
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true}
->
engine/lib/ui/window/window.cc
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
Dart_Handle data_handle) {
UIDartState* dart_state = UIDartState::Current();
if (!dart_state->window()) {
return tonic::ToDart(
"Platform messages can only be sent from the main isolate");
}
fml::RefPtr<PlatformMessageResponse> response;
if (!Dart_IsNull(callback)) {
response = fml::MakeRefCounted<PlatformMessageResponseDart>(
tonic::DartPersistentValue(dart_state, callback),
dart_state->GetTaskRunners().GetUITaskRunner());
}
if (Dart_IsNull(data_handle)) {
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(name, response));
} else {
tonic::DartByteData data(data_handle);
const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
// 通知具体 client 处理message
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
}
省略其余过程。平台判断等
->
platform_view_android.cc
// |PlatformView|
void PlatformViewAndroid::HandlePlatformMessage(
fml::RefPtr<flutter::PlatformMessage> message) {
JNIEnv* env = fml::jni::AttachCurrentThread();
fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
if (view.is_null())
return;
int response_id = 0;
if (auto response = message->response()) {
// 缓存一下respone。等待客户端处理完成在通知回Dart
response_id = next_response_id_++;
pending_responses_[response_id] = response;
}
auto java_channel = fml::jni::StringToJavaString(env, message->channel());
if (message->hasData()) {
fml::jni::ScopedJavaLocalRef<jbyteArray> message_array(
env, env->NewByteArray(message->data().size()));
env->SetByteArrayRegion(
message_array.obj(), 0, message->data().size(),
reinterpret_cast<const jbyte*>(message->data().data()));
message = nullptr;
// This call can re-enter in InvokePlatformMessageXxxResponseCallback.
// 调用原生方法处理 message
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
message_array.obj(), response_id);
} else {
message = nullptr;
// This call can re-enter in InvokePlatformMessageXxxResponseCallback.
// 调用原生方法处理 message
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
nullptr, response_id);
}
}
platform_view_android_jni.cc
// 原生端方法名为 io/flutter/embedding/engine/FlutterJNI.handlePlatformMessage()
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
"(Ljava/lang/String;[BI)V");
void FlutterViewHandlePlatformMessage(JNIEnv* env,
jobject obj,
jstring channel,
jobject message,
jint responseId) {
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
responseId);
FML_CHECK(CheckException(env));
}
复制代码
消息发送到Java端后,天然须要Java端处理方法,并将结果返回给dart
先看 java端注册Messanger方法
简单版本,不须要flutter create
// messenger通常就是 FlutterView
new MethodChannel(messenger, "channel_name").setMethodCallHandler(
new MethodChannel.MethodCallHandler(){
onMethodCal((@NonNull MethodCall call, @NonNull MethodChannel.Result result){
// 根据call中的method(方法名)arguments(参数)执行操做。
// 调用result中的api: success,error,notImplemented。发送结果到dart
}
})
setMethodCallHandler时 实际调用了
@UiThread
public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null :
new MethodChannel.IncomingMethodCallHandler(handler)); // 对handler的封装,内部管理什么时候调用onMethodCall。并传递Result。
}
messenger就是上面提到的FlutterView。其内部又直接调用了 FlutterNativeView 的 setMessageHandler 方法
FlutterNativeView.java
@UiThread
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
this.mNativeView.setMessageHandler(channel, handler);
}
复制代码
// 注:关于 FlutterNativeView 他就是FlutterView(原生View)和native Fluter 引擎通讯的关键对象。官方所给的混合方案中。生成的 Flutter 类 在 createView 的时候都会建立一个新的FlutterNativeView,对于内存和性能都有所损耗。FlutterBoost方案中,在建立新页面的时候复用了FutterView和FlutterNativeView。所以能够提升部分性能。
以后又陆续调用了 DartExecutor, DartMessenger的setMessageHandler 方法。最终 handler 以Map的形式,保存在DartMessenger中的messageHandlers map中,key是以前提到的 "channel_name" .
仔细阅读DartMessenger 类,能够发现一个 handleMessageFromDart 方法,这个方法,就是Native端(FlutterViewHandlePlatformMessage)最终调用的java方法
public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
Log.v("DartMessenger", "Received message from Dart over channel '" + channel + "'");
BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
if (handler != null) {
try {
Log.v("DartMessenger", "Deferring to registered handler to process message.");
ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
// 关键函数,将dart端发送过来的 message 传递给最终的 Handler
handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
} catch (Exception var6) {
Log.e("DartMessenger", "Uncaught exception in binary message listener", var6);
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
} else {
Log.v("DartMessenger", "No registered handler for message. Responding to Dart with empty reply message.");
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
}
复制代码
后续也就是一直调用到咱们以前设置的 onMethodCall 方法了。
还有一个疑问,就是以前dart端发送消息的时候,执行了一个 Futture 这个 Futture 须要持有它的 Completer对象调用complete方法,才能中止。这个方法何时调用呢?
以前提过 setMethodCall里,对与Handler有一个封装类 MethodChannel.IncomingMethodCallHandler(handler) 分析一下他的 onMesage 方法 就有答案了
@UiThread
public void onMessage(ByteBuffer message, final BinaryReply reply) {
// 解码MethodCall 注意要使用对应的Codec
MethodCall call = MethodChannel.this.codec.decodeMethodCall(message);
try {
this.handler.onMethodCall(call, new MethodChannel.Result() {
// 加密结果,并返回给Dard
public void success(Object result) {
reply.reply(MethodChannel.this.codec.encodeSuccessEnvelope(result));
}
public void error(String errorCode, String errorMessage, Object errorDetails) {
reply.reply(MethodChannel.this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
}
public void notImplemented() {
reply.reply((ByteBuffer)null);
}
});
} catch (RuntimeException var5) {
Log.e("MethodChannel#" + MethodChannel.this.name, "Failed to handle method call", var5);
reply.reply(MethodChannel.this.codec.encodeErrorEnvelope("error", var5.getMessage(), (Object)null));
}
}
复制代码
能够看到,只要咱们调用了result的任意方法(success,error,notImplemented),都会经过这个reply 方法发送一个消息出去,通知最开始提到的Completer对象执行 complete。
简单看一下调用链。
DartMessenger.Reply 对象。
public void reply(@Nullable ByteBuffer reply) {
if (this.done.getAndSet(true)) {
throw new IllegalStateException("Reply already submitted");
} else {
if (reply == null) {
this.flutterJNI.invokePlatformMessageEmptyResponseCallback(this.replyId);
} else {
this.flutterJNI.invokePlatformMessageResponseCallback(this.replyId, reply, reply.position());
}
}
}
又回到了FlutterJNI中。将事件通知到JNI层。
platform_view_android.cc(以前DispatchMessage的类)
void PlatformViewAndroid::InvokePlatformMessageResponseCallback(
JNIEnv* env,
jint response_id,
jobject java_response_data,
jint java_response_position) {
if (!response_id)
return;
// 找到正在等待处理的请求(以前HandlePlaformMessage时作的缓存),也就是dart端发送过来的message请求
auto it = pending_responses_.find(response_id);
if (it == pending_responses_.end())
return;
uint8_t* response_data =
static_cast<uint8_t*>(env->GetDirectBufferAddress(java_response_data));
std::vector<uint8_t> response = std::vector<uint8_t>(
response_data, response_data + java_response_position);
auto message_response = std::move(it->second);
// 清楚记录
pending_responses_.erase(it);
// 调用Complete方法。
message_response->Complete(
std::make_unique<fml::DataMapping>(std::move(response)));
}
platform_message_response_dart.cc
void PlatformMessageResponseDart::Complete(std::unique_ptr<fml::Mapping> data) {
if (callback_.is_empty())
return;
FML_DCHECK(!is_complete_);
is_complete_ = true;
ui_task_runner_->PostTask(fml::MakeCopyable(
[callback = std::move(callback_), data = std::move(data)]() mutable {
std::shared_ptr<tonic::DartState> dart_state =
callback.dart_state().lock();
if (!dart_state)
return;
tonic::DartState::Scope scope(dart_state);
Dart_Handle byte_buffer = WrapByteData(std::move(data));
// 释放callback 将结果传到 dart 端
tonic::DartInvoke(callback.Release(), {byte_buffer});
}));
}
复制代码