MethodChannel 原理之 Native -> Java

通過上文分析,我們知道FlutterViewHandlePlatformMessage()實(shí)際上是通過JNI的方式最終調(diào)用了FlutterJNI.java中的handlePlatformMessage()方法,該方法接受三個(gè)來自Native層的參數(shù):

  • channel: String類型,表示Channel名稱.
  • message: 字節(jié)數(shù)組,表示方法調(diào)用中的數(shù)據(jù),如方法名和參數(shù).
  • replyId: int類型,在將此次調(diào)用的響應(yīng)數(shù)據(jù)從Java層寫回到Native層時(shí)用到
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++引擎之間的相關(guān)接口.此類目前處于實(shí)驗(yàn)性質(zhì),隨著后續(xù)的發(fā)展可能會被不斷的重構(gòu)和優(yōu)化,不保證一直存在,不建議開發(fā)者調(diào)用該類.

為了建立Android應(yīng)用和Flutter C/C++引擎的連接,需要創(chuàng)建FlutterJNI實(shí)例,然后將其attach到Native,常見的使用方法如下:

// 1.創(chuàng)建FlutterJNI實(shí)例
FlutterJNI flutterJNI = new FlutterJNI();
// 2.建立和Native層的連接
flutterJNI.attachToNative();
......
// 3.斷開和Native層的連接,并釋放資源
flutterJNI.detachFromNativeAndReleaseResources();

FlutterJNI中handlePlatformMessage(),在該方法中首先判斷platformMessageHandler是否為null,不為null,則調(diào)用其handleMessageFromDart()方法.其中platformMessageHandler需要通過FlutterJNI中的setPlatformMessageHandler()方法來設(shè)置.在FlutterNativeView中調(diào)用的。

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);
        // 創(chuàng)建FlutterJNI實(shí)例
        mFlutterJNI = new FlutterJNI();
        mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
        // 將PlatformMessageHandlerImpl實(shí)例賦值給FlutterJNI中的platformMessageHandler屬性
        mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
        mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
        attach(this, isBackgroundView);
        assertAttached();
        mMessageHandlers = new HashMap<>();
    }
    
    .......
}

在FlutterNativeView的構(gòu)造函數(shù)中,首先創(chuàng)建FlutterJNI實(shí)例mFlutterJNI,然后調(diào)用setPlatformMessageHandler()并把PlatformMessageHandlerImpl實(shí)例作為參數(shù)傳入.因此在FlutterJNI的handlePlatformMessage()方法中,最終調(diào)用PlatformMessageHandlerImpl實(shí)例的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.根據(jù)channel名稱獲取對應(yīng)的BinaryMessageHandler對象.每個(gè)Channel對應(yīng)一個(gè)Handler對象
            BinaryMessageHandler handler = mMessageHandlers.get(channel);
            if (handler != null) {
                try {
                    // 2.將字節(jié)數(shù)組對象封裝為ByteBuffer對象
                    final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
                    // 3.調(diào)用handler對象的onMessage()方法來分發(fā)消息
                    handler.onMessage(buffer, new BinaryReply() {
                        private final AtomicBoolean done = new AtomicBoolean(false);
                        @Override
                        public void reply(ByteBuffer reply) {
                            // 4.根據(jù)reply的情況,調(diào)用FlutterJNI中invokePlatformMessageXXX()方法將響應(yīng)數(shù)據(jù)發(fā)送給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()方法中,首先根據(jù)Channel名稱從mMessageHandlers取出對應(yīng)的二進(jìn)制消息處理器BinaryMessageHandler,然后將字節(jié)數(shù)組message封裝為ByteBuffer對象,然后調(diào)用BinaryMessageHandler實(shí)例的onMessage()方法處理ByteBuffer,并進(jìn)行響應(yīng).

BinaryReply是一個(gè)接口,主要用來將ByteBuffer類型的響應(yīng)數(shù)據(jù)reply從Java層寫回到Flutter層.根據(jù)reply是否為null,調(diào)用FlutterJNI實(shí)例不同的方法.

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的唯一標(biāo)識符
    private static final String CHANNEL = "samples.flutter.io/battery";

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        // 2.創(chuàng)建MethodChannel對象channel
        MethodChannel channel = new MethodChannel(getFlutterView(), CHANNEL);
        // 3.調(diào)用MethodChannel實(shí)例的setMethodCallHandler()方法為當(dāng)前channel設(shè)置Handler
        channel.setMethodCallHandler(
                new MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, Result result) {
                        // TODO
                    }
                });
    }
}

接下來是 MethodChannel 定義

public final class MethodChannel {
    // 二進(jìn)制信使
    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方法調(diào)用數(shù)據(jù)進(jìn)行解碼,并將其封裝為MethodCall對象.
            // MethodCall中包含兩部分?jǐn)?shù)據(jù):method表示要調(diào)用的方法;arguments表示方法所需參數(shù)
            final MethodCall call = codec.decodeMethodCall(message);
            try {
                // 2.調(diào)用自定義MethodCallHandler中的onMethodCall方法繼續(xù)處理方法調(diào)用
                handler.onMethodCall(call, new Result() {
                    @Override
                    public void success(Object result) {
                        // 調(diào)用成功時(shí),需要回傳數(shù)據(jù)給Flutter層時(shí),使用codec對回傳數(shù)據(jù)result
                        // 進(jìn)行編碼
                        reply.reply(codec.encodeSuccessEnvelope(result));
                    }

                    @Override
                    public void error(String errorCode, String errorMessage, Object errorDetails) {
                        // 調(diào)用失敗時(shí),需要回傳錯(cuò)誤數(shù)據(jù)給Flutter層時(shí),使用codec對errorCode,
                        // errorMessage,errorDetails進(jìn)行編碼
                        reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
                    }

                    @Override
                    public void notImplemented() {
                        // 方法沒有實(shí)現(xiàn)時(shí),調(diào)用該方法后,flutter將會受到相應(yīng)的錯(cuò)誤消息
                        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層的二進(jìn)制數(shù)據(jù)進(jìn)行解碼,并將其封裝為MethodCall對象,然后調(diào)用MethodCallHandler.onMethodCall()方法.

調(diào)用過程

Java->Native

public class FlutterJNI {
  private Long nativePlatformViewId;
    
  ......  
  @UiThread
  public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) {
    // 1.檢查FlutterJNI是否已經(jīng)attach到Native層,如若沒有則拋出異常  
    ensureAttachedToNative();
    // 2.繼續(xù)調(diào)用nativeInvokePlatformMessageResponseCallback()  
    nativeInvokePlatformMessageResponseCallback(
        nativePlatformViewId,
        responseId,
        message,
        position
    );
  }
    
  private native void nativeInvokePlatformMessageResponseCallback(
     long nativePlatformViewId,
     int responseId,
     ByteBuffer message,
     int position
  );   
    
  ......  
      
  private void ensureAttachedToNative() {
    // FlutterJNI attach到Native層后,會返回一個(gè)long類型的值用來初始化nativePlatformViewId  
    if (nativePlatformViewId == null) {
      throw new RuntimeException("Cannot execute operation because FlutterJNI is not attached to native.");
    }
  }

}

當(dāng)數(shù)據(jù)需要寫回時(shí),數(shù)據(jù)首先通過codec被編碼成ByteBuffer類型,然后調(diào)用reply的reply()方法.在reply()方法中,對于非null類型的ByteBuffer,會調(diào)用FlutterJNI中的invokePlatformMessageResponseCallback().
在上述invokePlatformMessageResponseCallback()方法中,首先檢查當(dāng)前FlutterJNI實(shí)例是否已經(jīng)attach到Native層,然后調(diào)用Native方法nativeInvokePlatformMessageResponseCallback()向JNI層寫入數(shù)據(jù)

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函數(shù)返回一個(gè)指向被傳入的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.調(diào)用response的Complete()方法將二進(jìn)制結(jié)果返回
  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層的二進(jìn)制數(shù)據(jù)data轉(zhuǎn)為Dart中的二進(jìn)制數(shù)據(jù)byte_buffer
        Dart_Handle byte_buffer = WrapByteData(std::move(data));
        tonic::DartInvoke(callback.Release(), {byte_buffer});
      }));
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悼粮,一起剝皮案震驚了整個(gè)濱河市甸昏,隨后出現(xiàn)的幾起案子投蝉,更是在濱河造成了極大的恐慌枚尼,老刑警劉巖挽鞠,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杆逗,死亡現(xiàn)場離奇詭異午乓,居然都是意外死亡磅摹,警方通過查閱死者的電腦和手機(jī)滋迈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來户誓,“玉大人饼灿,你說我怎么就攤上這事〉勖溃” “怎么了碍彭?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悼潭。 經(jīng)常有香客問我庇忌,道長,這世上最難降的妖魔是什么舰褪? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任皆疹,我火速辦了婚禮,結(jié)果婚禮上占拍,老公的妹妹穿的比我還像新娘略就。我一直安慰自己,他們只是感情好晃酒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布表牢。 她就那樣靜靜地躺著,像睡著了一般掖疮。 火紅的嫁衣襯著肌膚如雪初茶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天浊闪,我揣著相機(jī)與錄音恼布,去河邊找鬼。 笑死搁宾,一個(gè)胖子當(dāng)著我的面吹牛折汞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盖腿,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼爽待,長吁一口氣:“原來是場噩夢啊……” “哼损同!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸟款,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤膏燃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后何什,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體组哩,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年处渣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伶贰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罐栈,死狀恐怖黍衙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荠诬,我是刑警寧澤琅翻,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站浅妆,受9級特大地震影響望迎,放射性物質(zhì)發(fā)生泄漏障癌。R本人自食惡果不足惜凌外,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涛浙。 院中可真熱鬧康辑,春花似錦、人聲如沸轿亮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽我注。三九已至按咒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間但骨,已是汗流浹背励七。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奔缠,地道東北人掠抬。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像校哎,于是被迫代替她去往敵國和親两波。 傳聞我的和親對象是個(gè)殘疾皇子瞳步,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345