-
FlutterActivity
我們知道南誊,原生連接Flutter需要用到繼承FlutterActivity或者FlutterFragment锻煌,以FlutterActivity為例,它的onCreate如下:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { switchLaunchThemeForNormalTheme(); super.onCreate(savedInstanceState); delegate = new FlutterActivityAndFragmentDelegate(this); delegate.onAttach(this); delegate.onRestoreInstanceState(savedInstanceState); lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); configureWindowForTransparency(); setContentView(createFlutterView()); configureStatusBarForFullscreenFlutterExperience(); }
可見(jiàn),自定義的子類(lèi)的onCreate中必須要先調(diào)用super.onCreate方法完成FlutterActivity的默認(rèn)配置。
之前我們解析了createFlutterView方法焙格,知道了FlutterEngine渲染的UI通過(guò)FlutterView來(lái)添加到Activity中,現(xiàn)在我們來(lái)看前面的FlutterActivityAndFragmentDelegate夷都,和原生通信的邏輯也從它開(kāi)始眷唉。
-
FlutterActivityAndFragmentDelegate
首先看一下它的onAttach方法:
void onAttach(@NonNull Context context) { ensureAlive(); if (flutterEngine == null) { setupFlutterEngine(); } if (host.shouldAttachEngineToActivity()) { flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle()); } platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine); host.configureFlutterEngine(flutterEngine); }
首先會(huì)調(diào)用setupFlutterEngine方法保證先創(chuàng)建FlutterEngine,然后調(diào)用host的configureFlutterEngine方法來(lái)對(duì)FlutterEngine進(jìn)行相關(guān)配置囤官,比如可以重寫(xiě)FlutterActivity的子類(lèi)的configureFlutterEngine來(lái)添加自定義的Plugin冬阳。
看一下setupFlutterEngine方法:
void setupFlutterEngine() { // First, check if the host wants to use a cached FlutterEngine. String cachedEngineId = host.getCachedEngineId(); if (cachedEngineId != null) { flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId); isFlutterEngineFromHost = true; if (flutterEngine == null) { throw new IllegalStateException( "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '" + cachedEngineId + "'"); } return; } // Second, defer to subclasses for a custom FlutterEngine. flutterEngine = host.provideFlutterEngine(host.getContext()); if (flutterEngine != null) { isFlutterEngineFromHost = true; return; } // Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our // FlutterView. flutterEngine = new FlutterEngine( host.getContext(), host.getFlutterShellArgs().toArray(), /*automaticallyRegisterPlugins=*/ false, /*willProvideRestorationData=*/ host.shouldRestoreAndSaveState()); isFlutterEngineFromHost = false; }
在方法的一開(kāi)始,首先會(huì)通過(guò)FlutterEngineCache.getInstance().get(cachedEngineId)方法嘗試從緩存中取FlutterEngine党饮,所以我們可以在子類(lèi)Activity中重寫(xiě)getCachedEngineId方法來(lái)設(shè)置在每一次重新打開(kāi)該Activity的時(shí)候都能使用之前的FlutterEngine摩泪,這會(huì)極大地提高性能。getCachedEngineId方法在FlutterActivity中有默認(rèn)實(shí)現(xiàn):
@Override @Nullable public String getCachedEngineId() { return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID); }
可以看到劫谅,你也可以通過(guò)intent的方式傳遞cachedEngineId见坑。
當(dāng)然我們需要事先創(chuàng)建FlutterEngine然后把它put到FlutterEngineCache中,這一步你可以放在Application類(lèi)的初始化中捏检,但不是更好的做法荞驴,我們接著往下看。
如果cache中沒(méi)有拿到之前使用過(guò)的FlutterEngine的話就會(huì)嘗試調(diào)用子類(lèi)Activity重寫(xiě)的provideFlutterEngine方法來(lái)獲取贯城,所以你可以重寫(xiě)這個(gè)方法然后在這里創(chuàng)建FlutterEngine熊楼,然后可以在這里把它放到cache中,這就是比上面放在Application中更好的做法能犯,為什么呢鲫骗?因?yàn)榉旁贏pplication中的話,F(xiàn)lutterEngine并不一定會(huì)被使用踩晶,有可能使用過(guò)程中從沒(méi)有打開(kāi)過(guò)Flutter頁(yè)面执泰,這就會(huì)導(dǎo)致始終持有這個(gè)FlutterEngine對(duì)象,造成資源浪費(fèi)渡蜻;還有一個(gè)好處就是這樣一來(lái)每個(gè)頁(yè)面都有自己的獨(dú)立的FlutterEngine术吝,每個(gè)FlutterEngine的業(yè)務(wù)相對(duì)獨(dú)立而且不需要維護(hù)其他Flutter業(yè)務(wù)的相關(guān)信息计济。當(dāng)然如果是App的所有頁(yè)面都是Flutter頁(yè)面或者原生頁(yè)面占很少一部分的時(shí)候在Application中配置會(huì)更好。
最后一步則是new一個(gè)新的FlutterEngine對(duì)象排苍,context和host相關(guān)聯(lián)沦寂。
使用緩存FlutterEngine的作用一個(gè)是明顯地提高頁(yè)面打開(kāi)速度,再一個(gè)會(huì)保存之前Flutter頁(yè)面的操作信息淘衙。
需要注意的是传藏,如果使用緩存id的話,則需要手動(dòng)設(shè)置切入點(diǎn)彤守,哪怕是main的默認(rèn)入口也要手動(dòng)設(shè)置漩氨。因?yàn)樵贔lutterActivity的onStart方法中調(diào)用了一個(gè)doInitialFlutterViewRun方法:
private void doInitialFlutterViewRun() { // Don't attempt to start a FlutterEngine if we're using a cached FlutterEngine. if (host.getCachedEngineId() != null) { return; } ... ... String appBundlePathOverride = host.getAppBundlePath(); if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) { appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath(); } // Configure the Dart entrypoint and execute it. DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint( appBundlePathOverride, host.getDartEntrypointFunctionName()); flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); }
可見(jiàn),如果使用緩存id的話則不會(huì)走到下面的默認(rèn)切入點(diǎn)設(shè)置代碼遗增。
-
FlutterPlugin
我們這里分析的是和原生通信,所以必須要看FlutterPlugin款青,它是通信的橋梁。
前面我們說(shuō)到可以重寫(xiě)FlutterActivity的configureFlutterEngine方法來(lái)配置Plugin:
@Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { flutterEngine.getPlugins().add((FlutterPlugin) getPlugin()); super.configureFlutterEngine(flutterEngine); }
getPlugins方法獲取的是FlutterEngineConnectionRegistry,它的add方法中有如下代碼:
if (has(plugin.getClass())) { return; } plugins.put(plugin.getClass(), plugin); plugin.onAttachedToEngine(pluginBinding);
可見(jiàn)會(huì)按照plugin的class作為key保存到plugins中勘天,然后就調(diào)用plugin的onAttachedToEngine方法龙助,所以我們通常都會(huì)重寫(xiě)這個(gè)方法,在里面初始化各種Channel康震。
下面看一個(gè)demo:
class MyHomePlugin : FlutterPlugin, EventChannel.StreamHandler, MethodChannel.MethodCallHandler { private var batteryEventChannel : EventChannel? = null private var methodChannel : MethodChannel? = null private var batteryEventSink : EventChannel.EventSink? = null override fun onAttachedToEngine(binding : FlutterPlugin.FlutterPluginBinding) { batteryEventChannel = EventChannel(binding.binaryMessenger, "battery") batteryEventChannel?.setStreamHandler(this) methodChannel = MethodChannel(binding.binaryMessenger, "allMethod") methodChannel?.setMethodCallHandler(this) } override fun onDetachedFromEngine(binding : FlutterPlugin.FlutterPluginBinding) { batteryEventChannel?.setStreamHandler(null) batteryEventChannel = null methodChannel?.setMethodCallHandler(null) methodChannel = null } override fun onListen(arguments : Any?, events : EventChannel.EventSink?) { batteryEventSink = events events?.success(666) //events?.error("007","Wrong~",null) //events?.endOfStream() } override fun onCancel(arguments : Any?) { batteryEventSink?.endOfStream() batteryEventSink = null } override fun onMethodCall(call : MethodCall, result : MethodChannel.Result) { when(call.method){ "getName"->{ LogUtil.d(this,"getName() has been called!") } } } }
對(duì)于EventChannel來(lái)說(shuō)燎含,調(diào)用setStreamHandler方法設(shè)置一個(gè)EventChannel.StreamHandler,重寫(xiě)onListen和onCancel方法后即可接收Flutter發(fā)送的數(shù)據(jù)腿短,onListen回調(diào)中屏箍,使用一個(gè)變量持有events的引用,這樣在需要發(fā)送給Flutter的時(shí)候直接使用batteryEventSink發(fā)送即可橘忱,和MethodChannel不同赴魁,EventSink使用success、error和endOfStream方法發(fā)送數(shù)據(jù)钝诚,分別對(duì)應(yīng)Flutter監(jiān)聽(tīng)端的幾個(gè)回調(diào)颖御,F(xiàn)lutter端的監(jiān)聽(tīng)寫(xiě)法是:
@override void initState() { super.initState(); eventChannel.receiveBroadcastStream().listen( (event) { onReceiveBatteryChange(event); }, onError: onReceiveBatteryWrong, ); }
這里調(diào)用了listen方法之后就會(huì)回調(diào)到上面原生代碼的onListen方法中,使用方法參數(shù)events調(diào)用相關(guān)方法后凝颇,最終又會(huì)回調(diào)到這里listen方法里設(shè)置的對(duì)應(yīng)回調(diào)函數(shù)潘拱。
對(duì)于MethodChannel來(lái)說(shuō),以Flutter端發(fā)送拧略、原生接收的方向來(lái)看芦岂,F(xiàn)lutter端的發(fā)送代碼是:
Future result = await methodChannel.invokeMethod("getName");
原生接收需要調(diào)用setMethodCallHandler給MethodChannel設(shè)置MethodChannel.MethodCallHandler,重寫(xiě)其onMethodCall回調(diào)方法處理接收邏輯垫蛆。反過(guò)來(lái)盔腔,原生發(fā)送、Flutter接收的方向也是一樣的,只不過(guò)設(shè)置的內(nèi)容交換一下即可弛随。
-
總結(jié)
本文分析了FlutterActivity瓢喉、FlutterPlugin、Channel之間的連接關(guān)系舀透,以及FlutterEngine的緩存策略栓票,并知道了如何使用相關(guān)Channel來(lái)使Flutter端和原生交互起來(lái),至于EventChannel和MethodChannel的原理涉及的代碼比較多愕够,另起一篇來(lái)整理走贪。
Flutter原生通信原理概覽
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哄尔,“玉大人假消,你說(shuō)我怎么就攤上這事×虢樱” “怎么了富拗?”我有些...
- 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鸣戴。 經(jīng)常有香客問(wèn)我媒峡,道長(zhǎng),這世上最難降的妖魔是什么葵擎? 我笑而不...
- 正文 為了忘掉前任谅阿,我火速辦了婚禮,結(jié)果婚禮上酬滤,老公的妹妹穿的比我還像新娘签餐。我一直安慰自己,他們只是感情好盯串,可當(dāng)我...
- 文/花漫 我一把揭開(kāi)白布氯檐。 她就那樣靜靜地躺著,像睡著了一般体捏。 火紅的嫁衣襯著肌膚如雪冠摄。 梳的紋絲不亂的頭發(fā)上糯崎,一...
- 那天,我揣著相機(jī)與錄音河泳,去河邊找鬼沃呢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拆挥,可吹牛的內(nèi)容都是我干的薄霜。 我是一名探鬼主播,決...
- 文/蒼蘭香墨 我猛地睜開(kāi)眼纸兔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惰瓜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汉矿,我...
- 序言:老撾萬(wàn)榮一對(duì)情侶失蹤崎坊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后洲拇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奈揍,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年呻待,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片队腐。...
- 正文 年R本政府宣布为严,位于F島的核電站敛熬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏第股。R本人自食惡果不足惜应民,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夕吻。 院中可真熱鬧诲锹,春花似錦、人聲如沸涉馅。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稚矿。三九已至庸诱,卻和暖如春捻浦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桥爽。 一陣腳步聲響...
- 正文 我出身青樓母剥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親形导。 傳聞我的和親對(duì)象是個(gè)殘疾皇子环疼,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 目錄介紹 01.flutter和原生之間交互 02.MethodChanel流程 03.MethodChanel使...
- Flutter使用了一個(gè)靈活的系統(tǒng),允許您調(diào)用特定平臺(tái)的API朵耕,F(xiàn)lutter平臺(tái)特定的API支持不依賴于代碼生成...
- 開(kāi)發(fā)集成環(huán)境[?] Flutter (Channel stable, v1.12.13+hotfix.9, on ...
- 一. 調(diào)用原生功能 1.1. Camera 某些應(yīng)用程序可能需要使用移動(dòng)設(shè)備進(jìn)行拍照或者選擇相冊(cè)中的照片炫隶,F(xiàn)lut...
- 概述我們知道,在配置FlutterEngine的時(shí)候阎曹,有兩種方式可以設(shè)置初始路由伪阶,這個(gè)初始路由就是flutter第...