Flutter跟Native相互通信Platform Channels

前言

最近在在學(xué)習(xí)flutter語言流炕,發(fā)現(xiàn)flutter需要跟原生混合開發(fā)赁遗,混合開發(fā)中其實原生很多框架代碼都已經(jīng)寫好了曼追,比如網(wǎng)絡(luò)框架顽决,或者想要調(diào)用原生系統(tǒng)的東西短条,比如獲取手機電量啊,其實flutter是直接可以調(diào)用native端的代碼才菠,可以跟原生通信茸时,通信的名詞稱為:Platform Channels(平臺通道)。Flutter平臺特定的API支持不依賴于代碼生成赋访,而是依賴于靈活的消息傳遞的方式:應(yīng)用的Flutter部分通過平臺通道(platform channel)將消息發(fā)送到其應(yīng)用程序的所在的宿主(iOS或Android)可都。
宿主監(jiān)聽的平臺通道,并接收該消息蚓耽。然后它會調(diào)用特定于該平臺的API(使用原生編程語言) - 并將響應(yīng)發(fā)送回客戶端渠牲,即應(yīng)用程序的Flutter部分

框架概述: 平臺通道

使用平臺通道在客戶端(Flutter UI)和宿主(平臺)之間傳遞消息,如下圖所示:

image

上圖中用到了MethodChannel田晚,其實flutter是有三種通信類型嘱兼,分別是:

* BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息,這個用的比較少

* MethodChannel:用于傳遞方法調(diào)用(method invocation)通常用來調(diào)用native中某個方法

* EventChannel: 用于數(shù)據(jù)流(event streams)的通信。有監(jiān)聽功能贤徒,比如電量變化之后直接推送數(shù)據(jù)給flutter端芹壕。

三種Channel之間互相獨立,各有用途接奈,但它們在設(shè)計上卻非常相近踢涌。每種Channel均有三個重要成員變量:

* name: String類型,代表Channel的名字序宦,也是其唯一標(biāo)識符睁壁。

* messager:BinaryMessenger類型,代表消息信使互捌,是消息的發(fā)送與接收的工具潘明。

* codec: MessageCodec類型或MethodCodec類型,代表消息的編解碼器秕噪。

注意:以上圖片箭頭是雙向的钳降,也就是flutter和native是可以相互調(diào)用通信。

具體原理深度理解可以看這篇文章:channel原理篇

channel通信的數(shù)據(jù)類型

雙方通信傳遞數(shù)據(jù)只能是如下類型數(shù)據(jù)腌巾,你不能傳遞一個自己建立的javabean過去遂填。

具體使用

1.MethodChannel的使用

1.1-Native端寫法

  • 這個代碼是我封裝了一個HttpMethodChannel類铲觉,用于調(diào)用原生網(wǎng)絡(luò)框架的,實現(xiàn)MethodCallHandler吓坚,重寫onMethodCall方法撵幽,在構(gòu)造方法里面new一個MethodChannel類,需要傳遞兩個參數(shù)礁击,一個是FlutterView盐杂,以為底層原理其實就是通過FlutterNativeView調(diào)用JNI方法把數(shù)據(jù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)傳遞給flutter的。第二個參數(shù)是一個唯一的字符串HTTP_CHANNEL客税,到時flutter端也需要使用這個字符串况褪。可以直接考過去更耻。

  • 接著httpChannel.setMethodCallHandler(this)把httpMethodChannel傳進(jìn)去测垛。

/**
 * 構(gòu)造網(wǎng)絡(luò)通信渠道
 * Created by wuminjian on 2018/12/13.
 */
public class HttpMethodChannel implements MethodChannel.MethodCallHandler {
    private static final String TAG = "HttpMethodChannel";

    private static final String HTTP_CHANNEL = "com.lingan.seeyou/http";

    private MethodChannel httpChannel;

    private Context context;

    private HttpMethodChannel(Context context, FlutterView flutterView) {
        this.context = context;
        httpChannel = new MethodChannel(flutterView, HTTP_CHANNEL);
        httpChannel.setMethodCallHandler(this);

    }

    /**
     * 暴露到外面的靜態(tài)create類
     */
    public static HttpMethodChannel create(Context context, FlutterView flutterView) {
        return new HttpMethodChannel(context, flutterView);
    }

    @Override
    public void onMethodCall(MethodCall methodCall,MethodChannel.Result result) {
        String url = null;
        String param;

        if (methodCall.hasArgument(HttpConstant.HTTP_URL)) {
            url = methodCall.argument(HttpConstant.HTTP_URL);
        }
        if (methodCall.hasArgument(HttpConstant.HTTP_PARAMS)) {
            param = methodCall.argument(HttpConstant.HTTP_PARAMS);
        }
        if (TextUtils.isEmpty(url)) {
            result.success("");
            return;
        }
        switch (methodCall.method) {
            //get請求
            case HTTP_GET:
                HttpController.getInstance().httpGet(url, null,result);
                break;
            //post請求
            case HTTP_POST:
                ToastUtils.showToast(context, "post網(wǎng)絡(luò)請求");
                result.success("我準(zhǔn)備開始post請求了");
                break;
        }

}
  • 接下來我們看看onMethodCall方法,在Flutter發(fā)送請求時秧均, onMethodCall
    方法會執(zhí)行食侮。onMethodCall有兩個入?yún)ⅲ?
    • MethodCall 中有關(guān)當(dāng)前請求的信息,例如調(diào)用方法的名字HTTP_GET這個變量“get”其實就是flutter那邊傳過來的方法名字告訴我它需要請求的是網(wǎng)絡(luò)的get請求操作目胡,methodCall.argument是獲取flutter那邊傳遞過來的其他參數(shù)數(shù)據(jù)锯七,比如網(wǎng)絡(luò)請求中的url、headers誉己、boday等數(shù)據(jù)眉尸。

    • Result對象是回調(diào)數(shù)據(jù)給flutter中使用的,有三個方法:

      1. result.success(data);     成功的時候巨双,返回數(shù)據(jù)調(diào)用
      2. result.error()            失敗的情況下噪猾,調(diào)用
      3. result.notImplemented();  這個是代表比如:我上面的列子中HTTP_GET方法沒有實現(xiàn)的話,可以告訴flutter方法沒有實現(xiàn)
      
      

1.2Flutter端寫法


class ListWigetState extends State<ListWiget> {

  List subjects = [];
  MethodChannel platform = const MethodChannel(ChannelUtils.HTTP_CHANNEL);

  @override
  void initState() {
    loadNativeData();
  }

  • 首頁也是構(gòu)造一個MethodChannel對象筑累,里面的參數(shù)就是我們在原生那邊定個的字符串袱蜡,記住一定是要一樣的。

  • 接下來就是用MethodChannel對象調(diào)用的代碼:

    /**
    * 加載原生網(wǎng)絡(luò)請求
    */
    loadNativeData() async {
    var responseBody;
    try {
      var params = {"a": 1};
      responseBody = await platform.invokeMethod("GET",{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null});
    
      print("zzzz: "+responseBody);
    
      var convertDataToJson = jsonDecode(responseBody)["subjects"];
      setState(() {
        subjects = convertDataToJson;
      });
    } on PlatformException catch (e) {
      print(e.toString());
    }
    }
    
  • responseBody = await platform.invokeMethod("GET",{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null});

  • 這行代碼就是通過通道來調(diào)用Native方法了慢宗。注意這里的await關(guān)鍵字是異步的坪蚁,所以這里必須要使用await關(guān)鍵字,"GET"就是我們之前提的傳遞的方法名字,代表是get請求還是post請求镜沽,{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null}這個是我們需要傳遞過去的參數(shù)敏晤。
    在上面Native代碼中我們把獲取到的數(shù)據(jù)是通過result.success();返回給Flutter。這里await表達(dá)式執(zhí)行完成以后就直接賦值給responseBody變量了缅茉。

1.3雙向調(diào)用通信

上面我們說的是flutter調(diào)用Native端的用法茵典,看最上面的圖我們知道其實都雙向的調(diào)用,即Native端也可以調(diào)用flutter的.

  • 舉個例子宾舅,我們想從Native端請求Flutter端的一個getFlutterName方法獲取一個字符串统阿。在Flutter端你需要給MethodChannel設(shè)置一個MethodCallHandler:

    platform.setMethodCallHandler(platformCallHandler);
    
    Future<dynamic> platformCallHandler(MethodCall call) async {
        switch (call.method) {
                case "getFlutterName":
                return "Flutter name flutter";
                break;
        }
    }
    
    
  • 在Native端,只需要讓對應(yīng)的的channel調(diào)用invokeMethod就行了:

    channel.invokeMethod("getFlutterName", null, new MethodChannel.Result() {
          @Override
          public void success(Object o) {
            // 這里就會輸出 "Flutter name flutter"
            Log.i("debug", o.toString());
          }
          @Override
          public void error(String s, String s1, Object o) {
          }
          @Override
          public void notImplemented() {
          }
        });
    
  • 上圖中的三個方法就是我前面提到的三個方法“成功”筹我、“失敗”扶平、“沒有實現(xiàn)”的三個方法的回調(diào)。其實說白了就是反過來調(diào)用而已蔬蕊。

2.EventChannel的使用

EventChannel的使用我們也以官方獲取電池電量的demo為例结澄,手機的電池狀態(tài)是不停變化的。我們要把這樣的電池狀態(tài)變化由Native及時通過EventChannel來告訴Flutter岸夯。這種情況用之前講的MethodChannel辦法是不行的麻献,這意味著Flutter需要用輪詢的方式不停調(diào)用getBatteryLevel來獲取當(dāng)前電量,顯然是不正確的做法猜扮。而用EventChannel的方式勉吻,則是將當(dāng)前電池狀態(tài)"推送"給Flutter.

2.1EventChannel-Native端寫法

先看我們熟悉的Native端怎么來創(chuàng)建EventChannel, 還是封裝一個FlutterEventChannel類,然后在MainActivity.onCreate中調(diào)用FlutterEventChannel的create方法把FlutterView傳進(jìn)來,代碼如下:

public class FlutterEventChannel implements EventChannel.StreamHandler {

    private static final String TAG = "FlutterEventChannel";
    private static final String EVENT_CHANNEL_NAME = "com.meetyou.flutter/event";

    private FlutterEventChannel(FlutterView flutterView) {
        EventChannel eventChannel = new EventChannel(flutterView, EVENT_CHANNEL_NAME);
        eventChannel.setStreamHandler(this);
    }

    public static FlutterEventChannel create(FlutterView flutterView) {
        return new FlutterEventChannel(flutterView);
    }

    private EventChannel.EventSink eventSink;

    /**
     * 暴露出去供界面?zhèn)鲾?shù)據(jù)到Flutter
     */
    public void sendEvent(Object data) {
        if (eventSink != null) {
            eventSink.success(data);
        } else {
            LogUtils.e(TAG, "===== FlutterEventChannel.eventSink 為空 需要檢查一下 =====");
        }
    }

    @Override
    public void onListen(Object o, EventChannel.EventSink eventSink) {
        this.eventSink = eventSink;
    }

    @Override
    public void onCancel(Object o) {
        eventSink = null;
    }
}

和MethodChannel類似旅赢,我們也是直接new一個EventChannel實例齿桃,并給它設(shè)置了一個StreamHandler類型的回調(diào)。其中onCancel代表對面不再接收煮盼,這里我們應(yīng)該做一些clean up的事情短纵。而 onListen則代表通道已經(jīng)建好,Native可以發(fā)送數(shù)據(jù)了僵控。注意onListen里帶的EventSink這個參數(shù)香到,后續(xù)Native發(fā)送數(shù)據(jù)都是經(jīng)過EventSink的”ㄆ疲看代碼:

private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) {
    return new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);

        if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
          events.error("UNAVAILABLE", "Charging status unavailable", null);
        } else {
          boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                               status == BatteryManager.BATTERY_STATUS_FULL;
          // 把電池狀態(tài)發(fā)給Flutter
          events.success(isCharging ? "charging" : "discharging");
        }
      }
    };
  }

上面的代碼就是監(jiān)聽了點亮改變的廣播悠就,在onReceive函數(shù)內(nèi),系統(tǒng)發(fā)來電池狀態(tài)廣播以后泛烙,在Native這里轉(zhuǎn)化為約定好的字符串理卑,然后通過調(diào)用events.success();發(fā)送給Flutter。Native端的代碼就是這樣蔽氨,接下來看Flutter端藐唠。

2.2EventChannel-Flutter端寫法

首先還是在State內(nèi)創(chuàng)建EventChannel,然后在initState的時候打開這個channel:

static const EventChannel eventChannel =
      const EventChannel('com.meetyou.flutter/event');
      
@override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

收到event以后的處理是在_onEvent函數(shù)里:

void _onEvent(Object event) {
    setState(() {
      _chargingStatus =
          "Battery status: ${event == 'charging' ? '' : 'dis'}charging.";
    });
  }

  void _onError(Object error) {
    setState(() {
      _chargingStatus = 'Battery status: unknown.';
    });
  }

從Native端傳過來的"charging"/"discharging"字符串直接就是入?yún)vent.

以上就是整個channel的基本用法,其實整個代碼可以做成一個plugin插件打包上傳到pub上面鹉究,供大家使用宇立,目前我在發(fā)布插件包的時候,最后一步上傳的時候自赔,google那邊提示超時妈嘹,我是已經(jīng)掛了代理的,不知道為何绍妨?润脸?有知道的朋友請留言告訴我柬脸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市毙驯,隨后出現(xiàn)的幾起案子倒堕,更是在濱河造成了極大的恐慌,老刑警劉巖爆价,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垦巴,死亡現(xiàn)場離奇詭異,居然都是意外死亡铭段,警方通過查閱死者的電腦和手機骤宣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來序愚,“玉大人憔披,你說我怎么就攤上這事≌乖耍” “怎么了活逆?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拗胜。 經(jīng)常有香客問我蔗候,道長,這世上最難降的妖魔是什么埂软? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任锈遥,我火速辦了婚禮,結(jié)果婚禮上勘畔,老公的妹妹穿的比我還像新娘所灸。我一直安慰自己,他們只是感情好炫七,可當(dāng)我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布爬立。 她就那樣靜靜地躺著,像睡著了一般万哪。 火紅的嫁衣襯著肌膚如雪侠驯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天奕巍,我揣著相機與錄音吟策,去河邊找鬼。 笑死的止,一個胖子當(dāng)著我的面吹牛檩坚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼匾委,長吁一口氣:“原來是場噩夢啊……” “哼拖叙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起剩檀,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤憋沿,失蹤者是張志新(化名)和其女友劉穎沪猴,沒想到半個月后采章,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡担租,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年抵怎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片反惕。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姿染,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悬赏,我是刑警寧澤狡汉,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布闽颇,位于F島的核電站盾戴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兵多。R本人自食惡果不足惜尖啡,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望中鼠。 院中可真熱鬧可婶,春花似錦、人聲如沸援雇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至具温,卻和暖如春蚕涤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铣猩。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工揖铜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人达皿。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓天吓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親峦椰。 傳聞我的和親對象是個殘疾皇子龄寞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 前言 我們都知道Flutter開發(fā)的app是可以同時在iOS和Android系統(tǒng)上運行的。顯然Flutter需要有...
    HowHardCanItBe閱讀 20,607評論 12 27
  • 開篇 開局一張圖汤功,其他全靠_物邑? 目前flutter框架還比較新,又是谷歌家的東西滔金,所以網(wǎng)上的文章基本都是講安卓和f...
    華南犀牛閱讀 17,833評論 22 47
  • 少點套路餐茵,發(fā)現(xiàn)自己的核心價值 2017-2-19星期六野狼魂(東莞)晴 早上的時候科阎,有朋友在微信給我發(fā)信息:“一直...
    野狼周高祥閱讀 261評論 0 0
  • 時隔半年許,未見初心安钟病。 夜深人靜時萧恕,忽想初心夢。 深知夢移位肠阱,今愿撿夢回票唆。 望吾心不亂,方能喚初心屹徘。
    薄荷加冰要多心涼閱讀 133評論 2 1
  • 不是每一個鬼魂都能順利走出迷魂谷走趋,就像不每一個鬼魂都能投胎到一個好人家一樣。 (一) “喂噪伊,你們需不需要幫助安净汀?”...
    星宿海閱讀 546評論 0 1