-
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(); }
可见,自定义的子类的onCreate中必须要先调用super.onCreate方法完成FlutterActivity的默认配置。
之前我们解析了createFlutterView方法,知道了FlutterEngine渲染的UI通过FlutterView来添加到Activity中,现在我们来看前面的FlutterActivityAndFragmentDelegate,和原生通信的逻辑也从它开始。
-
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); }
首先会调用setupFlutterEngine方法保证先创建FlutterEngine,然后调用host的configureFlutterEngine方法来对FlutterEngine进行相关配置,比如可以重写FlutterActivity的子类的configureFlutterEngine来添加自定义的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; }
在方法的一开始,首先会通过FlutterEngineCache.getInstance().get(cachedEngineId)方法尝试从缓存中取FlutterEngine,所以我们可以在子类Activity中重写getCachedEngineId方法来设置在每一次重新打开该Activity的时候都能使用之前的FlutterEngine,这会极大地提高性能。getCachedEngineId方法在FlutterActivity中有默认实现:
@Override @Nullable public String getCachedEngineId() { return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID); }
可以看到,你也可以通过intent的方式传递cachedEngineId。
当然我们需要事先创建FlutterEngine然后把它put到FlutterEngineCache中,这一步你可以放在Application类的初始化中,但不是更好的做法,我们接着往下看。
如果cache中没有拿到之前使用过的FlutterEngine的话就会尝试调用子类Activity重写的provideFlutterEngine方法来获取,所以你可以重写这个方法然后在这里创建FlutterEngine,然后可以在这里把它放到cache中,这就是比上面放在Application中更好的做法,为什么呢?因为放在Application中的话,FlutterEngine并不一定会被使用,有可能使用过程中从没有打开过Flutter页面,这就会导致始终持有这个FlutterEngine对象,造成资源浪费;还有一个好处就是这样一来每个页面都有自己的独立的FlutterEngine,每个FlutterEngine的业务相对独立而且不需要维护其他Flutter业务的相关信息。当然如果是App的所有页面都是Flutter页面或者原生页面占很少一部分的时候在Application中配置会更好。
最后一步则是new一个新的FlutterEngine对象,context和host相关联。
使用缓存FlutterEngine的作用一个是明显地提高页面打开速度,再一个会保存之前Flutter页面的操作信息。
需要注意的是,如果使用缓存id的话,则需要手动设置切入点,哪怕是main的默认入口也要手动设置。因为在FlutterActivity的onStart方法中调用了一个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); }
可见,如果使用缓存id的话则不会走到下面的默认切入点设置代码。
-
FlutterPlugin
我们这里分析的是和原生通信,所以必须要看FlutterPlugin,它是通信的桥梁。
前面我们说到可以重写FlutterActivity的configureFlutterEngine方法来配置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);
可见会按照plugin的class作为key保存到plugins中,然后就调用plugin的onAttachedToEngine方法,所以我们通常都会重写这个方法,在里面初始化各种Channel。
下面看一个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!") } } } }
对于EventChannel来说,调用setStreamHandler方法设置一个EventChannel.StreamHandler,重写onListen和onCancel方法后即可接收Flutter发送的数据,onListen回调中,使用一个变量持有events的引用,这样在需要发送给Flutter的时候直接使用batteryEventSink发送即可,和MethodChannel不同,EventSink使用success、error和endOfStream方法发送数据,分别对应Flutter监听端的几个回调,Flutter端的监听写法是:
@override void initState() { super.initState(); eventChannel.receiveBroadcastStream().listen( (event) { onReceiveBatteryChange(event); }, onError: onReceiveBatteryWrong, ); }
这里调用了listen方法之后就会回调到上面原生代码的onListen方法中,使用方法参数events调用相关方法后,最终又会回调到这里listen方法里设置的对应回调函数。
对于MethodChannel来说,以Flutter端发送、原生接收的方向来看,Flutter端的发送代码是:
Future result = await methodChannel.invokeMethod("getName");
原生接收需要调用setMethodCallHandler给MethodChannel设置MethodChannel.MethodCallHandler,重写其onMethodCall回调方法处理接收逻辑。反过来,原生发送、Flutter接收的方向也是一样的,只不过设置的内容交换一下即可。
-
总结
本文分析了FlutterActivity、FlutterPlugin、Channel之间的连接关系,以及FlutterEngine的缓存策略,并知道了如何使用相关Channel来使Flutter端和原生交互起来,至于EventChannel和MethodChannel的原理涉及的代码比较多,另起一篇来整理。
网友评论