MethodChannel 原理之 Native -> Java

通过上文分析,我们知道FlutterViewHandlePlatformMessage()实际上是通过JNI的方式最终调用了FlutterJNI.java中的handlePlatformMessage()方法,该方法接受三个来自Native层的参数:

  • channel: String类型,表示Channel名称.
  • message: 字节数组,表示方法调用中的数据,如方法名和参数.
  • replyId: int类型,在将此次调用的响应数据从Java层写回到Native层时用到
public class FlutterJNI {
  private PlatformMessageHandler platformMessageHandler;
    
      @UiThread
  public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {
    this.platformMessageHandler = platformMessageHandler;
  }
    
      // Called by native.
  @SuppressWarnings("unused")
  private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
    if (platformMessageHandler != null) {
      platformMessageHandler.handleMessageFromDart(channel, message, replyId);
    }
  }
}

FlutterJNI类定义了Java层和Flutter C/C++引擎之间的相关接口.此类目前处于实验性质,随着后续的发展可能会被不断的重构和优化,不保证一直存在,不建议开发者调用该类.

为了建立Android应用和Flutter C/C++引擎的连接,需要创建FlutterJNI实例,然后将其attach到Native,常见的使用方法如下:

// 1.创建FlutterJNI实例
FlutterJNI flutterJNI = new FlutterJNI();
// 2.建立和Native层的连接
flutterJNI.attachToNative();
......
// 3.断开和Native层的连接,并释放资源
flutterJNI.detachFromNativeAndReleaseResources();

FlutterJNI中handlePlatformMessage(),在该方法中首先判断platformMessageHandler是否为null,不为null,则调用其handleMessageFromDart()方法.其中platformMessageHandler需要通过FlutterJNI中的setPlatformMessageHandler()方法来设置.在FlutterNativeView中调用的。

public class FlutterNativeView implements BinaryMessenger {

    private final Map<String, BinaryMessageHandler> mMessageHandlers;
    private int mNextReplyId = 1;
    private final Map<Integer, BinaryReply> mPendingReplies = new HashMap<>();

    private final FlutterPluginRegistry mPluginRegistry;
    private FlutterView mFlutterView;
    private FlutterJNI mFlutterJNI;
    private final Context mContext;
    private boolean applicationIsRunning;

    public FlutterNativeView(Context context, boolean isBackgroundView) {
        mContext = context;
        mPluginRegistry = new FlutterPluginRegistry(this, context);
        // 创建FlutterJNI实例
        mFlutterJNI = new FlutterJNI();
        mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
        // 将PlatformMessageHandlerImpl实例赋值给FlutterJNI中的platformMessageHandler属性
        mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
        mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
        attach(this, isBackgroundView);
        assertAttached();
        mMessageHandlers = new HashMap<>();
    }
    
    .......
}

在FlutterNativeView的构造函数中,首先创建FlutterJNI实例mFlutterJNI,然后调用setPlatformMessageHandler()并把PlatformMessageHandlerImpl实例作为参数传入.因此在FlutterJNI的handlePlatformMessage()方法中,最终调用PlatformMessageHandlerImpl实例的handleMessageFromDart()来处理来自Flutter中的消息.

public class FlutterNativeView implements BinaryMessenger {
        private final Map<String, BinaryMessageHandler> mMessageHandlers;
    
        ......
            
        private final class PlatformMessageHandlerImpl implements PlatformMessageHandler {
        // Called by native to send us a platform message.
        public void handleMessageFromDart(final String channel, byte[] message, final int replyId) {
       // 1.根据channel名称获取对应的BinaryMessageHandler对象.每个Channel对应一个Handler对象
            BinaryMessageHandler handler = mMessageHandlers.get(channel);
            if (handler != null) {
                try {
                    // 2.将字节数组对象封装为ByteBuffer对象
                    final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
                    // 3.调用handler对象的onMessage()方法来分发消息
                    handler.onMessage(buffer, new BinaryReply() {
                        private final AtomicBoolean done = new AtomicBoolean(false);
                        @Override
                        public void reply(ByteBuffer reply) {
                            // 4.根据reply的情况,调用FlutterJNI中invokePlatformMessageXXX()方法将响应数据发送给Flutter层
                            if (reply == null) {
                                mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
                            } else {
                                mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
                            }
                        }
                    });
                } catch (Exception exception) {
                    mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
                }
                return;
            }
            mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
        }
}

以Channel名称作为key,以BinaryMessageHandler类型为value.在handleMessageFromDart()方法中,首先根据Channel名称从mMessageHandlers取出对应的二进制消息处理器BinaryMessageHandler,然后将字节数组message封装为ByteBuffer对象,然后调用BinaryMessageHandler实例的onMessage()方法处理ByteBuffer,并进行响应.

BinaryReply是一个接口,主要用来将ByteBuffer类型的响应数据reply从Java层写回到Flutter层.根据reply是否为null,调用FlutterJNI实例不同的方法.

BinaryMessageHandler是如何添加到mMessageHandler中:

public class FlutterNativeView implements BinaryMessenger {
    private final Map<String, BinaryMessageHandler> mMessageHandlers;
    
    ......
        
    @Override
    public void setMessageHandler(String channel, BinaryMessageHandler handler) {
        if (handler == null) {
            mMessageHandlers.remove(channel);
        } else {
            mMessageHandlers.put(channel, handler);
        }
    }
    .......
}
public class MainActivity extends FlutterActivity {
    // 1.定义Channel的名称,该名称作为Channel的唯一标识符
    private static final String CHANNEL = "samples.flutter.io/battery";

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        // 2.创建MethodChannel对象channel
        MethodChannel channel = new MethodChannel(getFlutterView(), CHANNEL);
        // 3.调用MethodChannel实例的setMethodCallHandler()方法为当前channel设置Handler
        channel.setMethodCallHandler(
                new MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, Result result) {
                        // TODO
                    }
                });
    }
}

接下来是 MethodChannel 定义

public final class MethodChannel {
    // 二进制信使
    private final BinaryMessenger messenger;
    // Channel名称
    private final String name;
    // 方法编码
    private final MethodCodec codec;

    public MethodChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }
        
    public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
        assert messenger != null;
        assert name != null;
        assert codec != null;
        this.messenger = messenger;
        this.name = name;
        this.codec = codec;
    }    
    
    ......
        
    public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
        messenger.setMessageHandler(name,
            handler == null ? null : new IncomingMethodCallHandler(handler));
    }
    ......
    private final class IncomingMethodCallHandler implements BinaryMessageHandler {
        private final MethodCallHandler handler;

        IncomingMethodCallHandler(MethodCallHandler handler) {
            this.handler = handler;
        }

        @Override
        public void onMessage(ByteBuffer message, final BinaryReply reply) {
            // 1.使用codec对来自Flutter方法调用数据进行解码,并将其封装为MethodCall对象.
            // MethodCall中包含两部分数据:method表示要调用的方法;arguments表示方法所需参数
            final MethodCall call = codec.decodeMethodCall(message);
            try {
                // 2.调用自定义MethodCallHandler中的onMethodCall方法继续处理方法调用
                handler.onMethodCall(call, new Result() {
                    @Override
                    public void success(Object result) {
                        // 调用成功时,需要回传数据给Flutter层时,使用codec对回传数据result
                        // 进行编码
                        reply.reply(codec.encodeSuccessEnvelope(result));
                    }

                    @Override
                    public void error(String errorCode, String errorMessage, Object errorDetails) {
                        // 调用失败时,需要回传错误数据给Flutter层时,使用codec对errorCode,
                        // errorMessage,errorDetails进行编码
                        reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
                    }

                    @Override
                    public void notImplemented() {
                        // 方法没有实现时,调用该方法后,flutter将会受到相应的错误消息
                        reply.reply(null);
                    }
                });
            } catch (RuntimeException e) {
                Log.e(TAG + name, "Failed to handle method call", e);
                reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
            }
        }
    }
}

在上述代码中,首先使用codec对来自Flutter层的二进制数据进行解码,并将其封装为MethodCall对象,然后调用MethodCallHandler.onMethodCall()方法.

调用过程

Java->Native

public class FlutterJNI {
  private Long nativePlatformViewId;
    
  ......  
  @UiThread
  public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) {
    // 1.检查FlutterJNI是否已经attach到Native层,如若没有则抛出异常  
    ensureAttachedToNative();
    // 2.继续调用nativeInvokePlatformMessageResponseCallback()  
    nativeInvokePlatformMessageResponseCallback(
        nativePlatformViewId,
        responseId,
        message,
        position
    );
  }
    
  private native void nativeInvokePlatformMessageResponseCallback(
     long nativePlatformViewId,
     int responseId,
     ByteBuffer message,
     int position
  );   
    
  ......  
      
  private void ensureAttachedToNative() {
    // FlutterJNI attach到Native层后,会返回一个long类型的值用来初始化nativePlatformViewId  
    if (nativePlatformViewId == null) {
      throw new RuntimeException("Cannot execute operation because FlutterJNI is not attached to native.");
    }
  }

}

当数据需要写回时,数据首先通过codec被编码成ByteBuffer类型,然后调用reply的reply()方法.在reply()方法中,对于非null类型的ByteBuffer,会调用FlutterJNI中的invokePlatformMessageResponseCallback().
在上述invokePlatformMessageResponseCallback()方法中,首先检查当前FlutterJNI实例是否已经attach到Native层,然后调用Native方法nativeInvokePlatformMessageResponseCallback()向JNI层写入数据

void PlatformViewAndroid::InvokePlatformMessageResponseCallback(
    JNIEnv* env,
    jint response_id,
    jobject java_response_data,
    jint java_response_position) {
  if (!response_id)
    return;
  // 1.通过response_id从pending_responses_中取出response  
  auto it = pending_responses_.find(response_id);
  if (it == pending_responses_.end())
    return;
  // 2.GetDirectBufferAddress函数返回一个指向被传入的ByteBuffer对象的地址指针  
  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);
  // 3.从pending_responses_中移除该response  
  pending_responses_.erase(it);
  // 4.调用response的Complete()方法将二进制结果返回
  message_response->Complete(
      std::make_unique<fml::DataMapping>(std::move(response)));
}

Native -> Dart

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);
        // 将Native层的二进制数据data转为Dart中的二进制数据byte_buffer
        Dart_Handle byte_buffer = WrapByteData(std::move(data));
        tonic::DartInvoke(callback.Release(), {byte_buffer});
      }));
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339