Flutter和Android的混合跳轉

1、項目特點

項目是Flutter作為主工程,將Android module或SDK作為模塊嵌入到flutter中,與通常所熟悉的Android(或iOS)工程將flutter 為module嵌入到工程中有所不同。

2、業務需求

任意界面間的跳轉,不管是flutter頁面Android頁面亦或是SDK中的頁面,需要實現相互間的任意跳轉且按照順序返回(例如進入時A-B-C-D-C-D-B,返回時是B-D-C-D-C-B-A,不考慮啟動模式)

3、實現方式探索

我們都知道Android 要打開一個Flutter頁面(簡稱為A頁面吧),需要借助與FlutterEngine來開啟一個新的任務棧。

startActivity(FlutterActivity.withNewEngine().initialRoute("/second").build(ThreeActivity.this));

這樣可以開啟在Flutter代碼中注冊路由為“/second”的A頁面,但這個頁面加載速度會有些慢,因為每次都需要創建新的FlutterEngine,因此我們可以優化代碼為:

public class MyApplication extends FlutterApplication {@Overridepublic void onCreate() {super.onCreate();FlutterEngine flutterEngineInit = new FlutterEngine(this);//flutterEngineInit.// 開始執行Dart代碼以預熱FlutterEngineflutterEngineInit.getNavigationChannel().setInitialRoute("/second");flutterEngineInit.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());// 緩存FlutterActivity要使用的FlutterEngineFlutterEngineCache.getInstance().put("/second", flutterEngineInit);}
}

自定義Application并在AndroidManifest.xml中注冊,在自定義的application的onCreate()方法中將頁面提前綁定到創建好的FlutterEngine上,并放入到緩存中

    <applicationandroid:label="untitled"android:name=".MyApplication"android:icon="@mipmap/ic_launcher"android:usesCleartextTraffic="true"></application>

這樣在打開A頁面時就可以,從緩存中獲取,加載體驗會明顯優于使用.withNewEngine()創建并打開A頁面

        jumpTv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {startActivity(FlutterActivity.withCachedEngine("/second").build(ThreeActivity.this));}});

注意:需要在AndroidManifest.xml中注冊FlutterActivity

       <activityandroid:name="io.flutter.embedding.android.FlutterActivity"android:theme="@style/LaunchTheme"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize"/>

到這里你可以在Android中打開A頁面了。

4、遇到問題

但有一個很大的問題,如果A頁面需要與Android交互怎么辦?每次打開A頁面都是放在一個新的FlutterEngine中,FlutterEngine是相互隔離的,channel無法互通。而我們的項目作為一個flutter項目,APP啟動時,已經創建了FlutterEngine,且channel全部建立在這個engine中,上述方法打開的頁面是無法使用這些channel;并且flutter B頁面中也可以通過navigation.push打卡A頁面,那A頁面所在的FlutterEngine,即為B頁面所在的FlutterEngine。如此情況A頁面的在調用channel時會報如下錯誤:

2023-11-15 15:56:51.504 1528-10332/com.example.untitled E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: MissingPluginException(No implementation found for method jumpToAndroidWebviewPage on channel samples.flutter.jumpto.android)#0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:165:7)<asynchronous suspension>

提示我在A頁面中調用的channel,沒有找到對應的實現。這是因為Android通過FlutterEngine開啟的A頁面,新的任務棧中沒有對應的實現。如何解決這個問題呢?

MethodChannel一般會是這樣寫:


public class MainActivity extends FlutterActivity  implements MethodChannel.MethodCallHandler{@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {super.configureFlutterEngine(flutterEngine);MethodChannel commonMethodChannel = new MethodChannel(flutterEngine.getDartExecutor(), "samples.flutter.jumpto.android");commonMethodChannel.setMethodCallHandler(this);}@Overridepublic void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {}
}

我注意到在創建channel的時候,接收兩個參數一個是messenger,一個是name。name好理解就是channel名稱,自己定義。而messenger,我們是通過flutterEngine.getDartExecutor()來獲取的一個BinaryMessenger的實現類對象。 也就是說我們只要能拿到每次打開A頁面的FlutterEngine對象,那就好解決這個問題了。

我們創建一個channel的管理類,方便我們在每個engine中實現channel(代碼有優化空間)

public class ChannelManage implements MethodChannel.MethodCallHandler, EventChannel.StreamHandler{private FlutterEngine flutterEngine;private Context context;EventChannel.EventSink jumpevents;public ChannelManage(FlutterEngine flutterEngine, Context context){this.flutterEngine = flutterEngine;this.context = context;MethodChannel commonMethodChannel = new MethodChannel(flutterEngine.getDartExecutor(), "samples.flutter.jumpto.android");commonMethodChannel.setMethodCallHandler(this);}@Overridepublic void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {if (call.method.equals("jumpToAndroidPage")) {Intent intent=new Intent(context, ContentActivity.class);context.startActivity(intent);result.success("跳轉");}else if(call.method.equals("jumpToAndroidSDKPage")){Intent intent=new Intent(context, JumpActivity.class);context.startActivity(intent);result.success("跳轉");}else if(call.method.equals("jumpToAndroidWebviewPage")){Intent intent=new Intent(context, ThreeActivity.class);context.startActivity(intent);result.success("跳轉");} else {result.notImplemented();}}@Overridepublic void onListen(Object arguments, EventChannel.EventSink events) {events.success("Android");jumpevents=events;}@Overridepublic void onCancel(Object arguments) {}
}

步驟3中的代碼改下:

我們在application中已經緩存了A頁面和綁定的FlutterEngine,從緩存那取出來,獲取對應的messenger設置給ChannelManage,這樣A頁面的Channel也可以使用啦。

        jumpTv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {new ChannelManage(FlutterEngineCache.getInstance().get("/second"),ThreeActivity.this);startActivity(FlutterActivity.withCachedEngine("/second").build(ThreeActivity.this));}});

但是不要高興太早,還記得我們要的需求不?任意界面間的跳轉,不管是flutter頁面Android頁面亦或是SDK中的頁面。

按照上面的方式假如有一個Android activityB 通過上述方法打開了flutter A頁面,A頁面又通過Channel新開了activityB頁面,activityB又打開了A頁面,(這里例子比較極端,現實中A頁面和activityB中間可能有其他頁面,但不妨礙會出現循環打開的場景)那會是什么樣子呢?

童年Andy 2023-11-11 16.19.28

我發現在返回到A頁面倒數第二次打開的地方,屏幕上沒有任何界面,且不能響應系統的手勢放回。這肯定是不行啊,沒有按原路返回啊,更大的問題是APP不能用了啊。

這是怎么回事呢?

其實是因為用了FlutterEngineCache,我們每次取到的都是一個FlutterEngine,那么當多次使用時,FlutterEngine就被放在了最后一次出現的位置,原位置將是一個空的engine,那這怎么玩。

再回過頭來看下,如果每次都新建engine開啟A頁面呢?

回顧下上面的代碼(中間寫的廢話太多了)

startActivity(FlutterActivity.withNewEngine().initialRoute("/second").build(ThreeActivity.this));

這肯定沒問題了吧,試下。靠,Channel又報錯了.......。

那就想法搞到每次的FlutterEngine不就行了嗎?

看API....,哎呀,這玩意沒有get?FlutterEngine的API啊。這怎么玩。

但是如果我寫一個MFlutterActivity?extends?FlutterActivity,然后重寫configureFlutterEngine方法不就可以了嗎?(MainActivity不就是這樣搞的嗎)

startActivity(MFlutterActivity.withNewEngine().initialRoute("/second").build(ThreeActivity.this));
public class MFlutterActivity extends FlutterActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overridepublic void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {new ChannelManage(flutterEngine,this);}}

注意:需要在AndroidManifest.xml中注冊MFlutterActivity

來一把試試,結果不行。這里的configureFlutterEngine不會回調,不應該啊,MainActivity為什么就可以呢?

想不通啊想不通。翻源代碼。

發現如下注釋(走了很多彎路,不贅述了,也嘗試過反射獲取engine對象,我沒有成功,小伙伴們可以試試)

    /*** Constructor that allows this {@code NewEngineIntentBuilder} to be used by subclasses of* {@code FlutterActivity}.** <p>Subclasses of {@code FlutterActivity} should provide their own static version of {@link* #withNewEngine()}, which returns an instance of {@code NewEngineIntentBuilder} constructed* with a {@code Class} reference to the {@code FlutterActivity} subclass, e.g.:** <p>{@code return new NewEngineIntentBuilder(MyFlutterActivity.class); }*/public NewEngineIntentBuilder(@NonNull Class<? extends FlutterActivity> activityClass) {this.activityClass = activityClass;}

啥意思?這意思是不是要自己重寫withNewEngine方法?干吧,直接把系統的代碼粘貼復制一下試試。

/*** Creates an {@link NewEngineIntentBuilder}, which can be used to configure an {@link Intent} to* launch a {@code FlutterActivity} that internally creates a new {@link* io.flutter.embedding.engine.FlutterEngine} using the desired Dart entrypoint, initial route,* etc.** @return The engine intent builder.*/@NonNullpublic static NewEngineIntentBuilder withNewEngine() {return new NewEngineIntentBuilder(FlutterActivity.class);}

APP起來,起來吧!!!

童年Andy 2023-11-11 14.30.39

5、總結:

實現flutter和Activity間的任意跳轉一定要實現對Channel的統一管理和自定義MFlutterActivity繼承FlutterActivity,用MFlutterActivity去執行加載flutter頁面的代碼開啟新的engine,且一定要重寫withNewEngine方法,否則Activity的生命周期以及configureFlutterEngine是不會執行的,也就沒辦法拿到engine,以便為統一管理的Channel設置messenger。

6、原代碼

最后上demo代碼,自取。

flutter_android_jump: 實現flutter工程下嵌入SDK、Android module后,flutter與Android界面間的任意跳轉和通信

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/163484.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/163484.shtml
英文地址,請注明出處:http://en.pswp.cn/news/163484.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

工作中死循環害死人

背景&#xff1a;研發的一段代碼&#xff0c;循環一直沒有跳出&#xff0c;導致其他依賴邏輯有問題&#xff0c;生產事故導致9萬左右數據不正常。 這里while&#xff08;true&#xff09;真的不要輕易用 &#xff0c;后來研發改動限制mysql的id切分步長&#xff0c;控制不會有數…

去大連發展還是去蘇州

公司要搬到蘇州&#xff0c;你是跟隨公司去蘇州發展&#xff0c;還是留在大連另尋出路&#xff1f;

【RtpRtcp】1: webrtc m79:audio的ChannelReceive 創建并使用

m79中,RtpRtcp::Create 的調用很少 不知道誰負責創建ChannelReceiveclass ChannelReceive : public ChannelReceiveInterface,public MediaTransportAudioSinkInterface {接收編碼后的音頻幀:接收rtcp包:

linux端無法magic上網,該怎么處理

可以打開網址&#xff1a;登錄 — iKuuu VPN 不用重新安裝&#xff0c;再次配置一下即可。

深入了解前饋網絡、CNN、RNN 和 Hugging Face 的 Transformer 技術!

一、說明 本篇在此對自然語言模型做一個簡短總結&#xff0c;從CNN\RNN\變形金剛&#xff0c;和抱臉的變形金剛庫說起。 二、基本前饋神經網絡&#xff1a; 讓我們分解一個基本的前饋神經網絡&#xff0c;也稱為多層感知器&#xff08;MLP&#xff09;。此代碼示例將&#xff1…

Web應用系統的小安全漏洞及相應的攻擊方式

1 寫作目的 本文講述一個簡單的利用WebAPI來進行一次基本沒有破壞力的“黑客”行為。 主要目的如下&#xff1a; 了解什么叫安全漏洞知道什么是api了解一些獲取api的工具通過對API的認識了解白盒接口測試基本概念和技術 免責聲明&#xff1a; 本文主要是以學習交流為目的…

C++ ,VCPKG那些事

玩過C都知道&#xff0c;熟悉三方庫對開發工作的重要性&#xff0c;尋找同步更新、穩定、權威的庫源更是每一位開發者經常要做的功課&#xff0c;諸如赫赫有名的boost,google SDK、騰迅sdk、阿里庫&#xff0c;vcpkg等等&#xff0c;這里要說的就是VCPKG&#xff0c;以下是記錄…

盤點63個Python登錄第三方源碼Python愛好者不容錯過

盤點63個Python登錄第三方源碼Python愛好者不容錯過 學習知識費力氣&#xff0c;收集整理更不易。 知識付費甚歡喜&#xff0c;為咱碼農謀福利。 鏈接&#xff1a;https://pan.baidu.com/s/1l7oooH9YovHmWzQ_58FRdg?pwd8888 提取碼&#xff1a;8888 項目名稱 A headless…

11-23 SSM4

Ajax 同步請求 &#xff1a;全局刷新的方式 -> synchronous請求 客戶端發一個請求&#xff0c;服務器響應之后你客戶端才能繼續后續操作&#xff0c;請求二響應完之后才能發送后續的請求&#xff0c;依次類推 有點&#xff1a;服務器負載較小&#xff0c;但是由于服務器相應…

Vue3+Ts實現聊天機器人(chatBot-附代碼)

一&#xff1a;項目介紹 本次實驗主要涉及到的技術是 Vue3 Ts&#xff0c;當然其中也有部分是 Vue2 格式的代碼以及 json 和 CSS 布局等。本來是想仿照 文心一言 來開發的一個聊天機器人案例。結果由于時間不足&#xff0c;可能只是做出來了一個半成品。不過核心功能是有的。由…

淺談安科瑞智能照明系統在馬來西亞國家石油公司項目的應用

摘要&#xff1a;隨著社會經濟的發展及網絡技術、通信技術的提高&#xff0c;人們對照明設計提出了新的要求&#xff0c;它不僅要控制照明光源的發光時間、 亮度&#xff0c;而且與其它系統來配合不同的應用場合做出相應的燈光場景。本文介紹了馬亞西亞石油公司智能照明項目的應…

tp8 使用rabbitMQ(2)工作隊列

代碼的參數說明在 第一小節的代碼中&#xff0c;如果需要可移步到第一節中查看 工作隊列 工作隊列&#xff08;又稱&#xff1a;任務隊列——Task Queues&#xff09;是為了避免等待一些占用大量資源、時間的操作。當我們把任務&#xff08;Task&#xff09;當作消息發送到隊列…

推薦一款png圖片打包plist工具pngPackerGUI_V2.0

png圖片打包plist工具&#xff0c;手把手教你使用pngPackerGUI_V2.0 此軟件是在pngpacker_V1.1軟件基礎之后&#xff0c;開發的界面化操作軟件&#xff0c;方便不太懂命令行的小白快捷上手使用。1.下載并解壓縮軟件&#xff0c;得到如下目錄&#xff0c;雙擊打開 pngPackerGUI.…

《第一行代碼:Android》第三版-2.4.3循環語句 for循環

本節主要講for 循環&#xff0c;主要就是創建個區間&#xff0c;然后用for 來遍歷。 /*** You can edit, run, and share this code.* play.kotlinlang.org*/fun main() {println("Hello, world!!!")for(i in 1..10)//表示 0 到10 但是包括10 &#xff0c;即數學上的…

使用paddleocr進行OCR文字識別

1 OCR介紹 OCR&#xff08;Optical Character Recognition&#xff09;即光學字符識別&#xff0c;是一種將不同類型的文檔&#xff08;如掃描的紙質文件、PDF文件或圖像文件中的文本&#xff09;轉換成可編輯和可搜索的數據的技術。OCR技術能夠識別和轉換印刷或手寫文字&…

Python接口自動化測試——如何搭建測試環境

前言 接口測試的方式有很多&#xff0c;比如可以用工具&#xff08;jmeter,postman&#xff09;之類&#xff0c;也可以自己寫代碼進行接口測試&#xff0c;工具的使用相對來說都比較簡單&#xff0c;重點是要搞清楚項目接口的協議是什么&#xff0c;然后有針對性的進行選擇&a…

NF是哪個國家品牌?韓國NF-耐福功放芯片介紹

NF是韓國一家專注于數字音頻芯片領域公司旗下的數字功放IC品牌&#xff0c;2008年開始進入中國市場&#xff0c;ISweek工采網作為其代理商&#xff1b;在國內簡稱&#xff1a;韓國NF&#xff0c;又被稱耐福、耐福NTP功放芯片&#xff1b;國內長虹、TCL、海信、小豹AI音箱、騰訊…

大數據-之LibrA數據庫系統告警處理(ALM-37001 MPPDBServer實例Redo日志缺失)

告警解釋 當DN主實例有未同步到DN備實例的xlog日志被刪除時&#xff0c;產生該告警。 告警屬性 告警ID 告警級別 可自動清除 37001 嚴重 是 告警參數 參數名稱 參數含義 ServiceName 產生告警的服務名稱 RoleName 產生告警的角色名稱 HostName 產生告警的主機名…

shell從服務列表中讀取參數并檢查服務

一、shell腳本 #!/usr/bin/env bash# 讀取文檔數據 while read -r line do# 提取服務名和命令service_name$(echo "$line" | awk {print $1})command$(echo "$line" | awk {$1""; print substr($0,2)})#echo "服務名: $service_name"…

SQL進階學習

1.[NISACTF 2022]join-us sql報錯注入和聯合注入 過濾&#xff1a; as IF rand() LEFT by updatesubstring handler union floor benchmark COLUMN UPDATE & sys.schema_auto_increment_columns && 11 database case AND right CAST FLOOR left updatexml DATABA…