河南省法制建设研究会网站,北外网院网站建设作业,小米手机官方网站,wordpress安装用户名1、项目特点
项目是Flutter作为主工程#xff0c;将Android module或SDK作为模块嵌入到flutter中#xff0c;与通常所熟悉的Android#xff08;或iOS#xff09;工程将flutter 为module嵌入到工程中有所不同。
2、业务需求
任意界面间的跳转#xff0c;不管是flutter页…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:labeluntitledandroid:name.MyApplicationandroid:iconmipmap/ic_launcherandroid:usesCleartextTraffictrue/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:nameio.flutter.embedding.android.FlutterActivityandroid:themestyle/LaunchThemeandroid:configChangesorientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiModeandroid:hardwareAcceleratedtrueandroid:windowSoftInputModeadjustResize/
到这里你可以在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 intentnew Intent(context, ContentActivity.class);context.startActivity(intent);result.success(跳转);}else if(call.method.equals(jumpToAndroidSDKPage)){Intent intentnew Intent(context, JumpActivity.class);context.startActivity(intent);result.success(跳转);}else if(call.method.equals(jumpToAndroidWebviewPage)){Intent intentnew Intent(context, ThreeActivity.class);context.startActivity(intent);result.success(跳转);} else {result.notImplemented();}}Overridepublic void onListen(Object arguments, EventChannel.EventSink events) {events.success(Android);jumpeventsevents;}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}.** pSubclasses 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界面间的任意跳转和通信