自谷歌发布Flutter release版本几天后才开始学习Flutter,实在惭愧。在了解完一些基础知识之后开始尝试将编写的简单Flutter module打包进Android项目中。本文章将尝试过程中遇到的一些问题和笔记记录下来。
本篇文章只是闭门造车的结果,如有任何错误很抱歉!请帮忙指出,多谢了
Android项目依赖Flutter项目
对于已有的Android项目来说,将所有页面都换成flutter页面不太现实,只能从一些简单的页面入手逐个替换。
Flutter项目跟Android工程根文件夹是同级的,它不同于普通的Android module存在于Android工程根目录下。在AndroidStudio中创建Flutter module,也并不会将该项目放到Android项目目录中,而是默认选择Android项目根目录的同级目录下。
在依赖Flutter module的时候,首先需要在项目的setting.gradle
加入如下依赖
include ':app'
//加入下面配置
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
以上配置的Flutter module的位置是出于Android根目录同级目录下,如果Flutter module的路径不同需要另外设置File函数的参数。编译项目,会在Android项目下生成名为flutter的module,正常来说该module不需要去修改代码,只需要在app的build.gradle中依赖该fluttermodule即可。
dependencies {
...
// 加入下面配置
implementation project(':flutter')
}
自此,完成Android项目对Flutter项目的依赖
FlutterActivity
在创建的Flutter项目的.andorid module中,只有一个类,那就是MainActivity类。
其继承自FlutterActivity,运行该Flutter工程时,Android项目的入口就是该MainActivity类。
该FlutterActivity类是Flutter项目的页面入口,Flutter为Android项目提供了FlutterView和FlutterFragment作为展示页面,附着在Activity上面。而FlutterActivity使用的便是FlutterView。
那么,从开发的角度,接下来引出几个问题?
- 继承FlutterActivity只能默认进入Flutter设定的首页?
- Flutter页面的生命周期如何管理?
- Flutter页面与Android原生页面之间如何通讯?
- Flutter页面是如何绘制的?
查看源码
查看FlutterActivity的类声明,该类实现了三个接口
public class FlutterActivity extends Activity implements
Provider, PluginRegistry, ViewFactory {
...
}
这三个接口作用如下
- Provider:只有一个简单的方法,那就是getFlutterView()返回当前Activity中的Flutter页面
- PluginRegistry:插件注册相关的类,以后的文章再详细讲述
- ViewFactory:该接口有三个方法,分别是
public interface ViewFactory { FlutterView createFlutterView(Context var1); FlutterNativeView createFlutterNativeView(); boolean retainFlutterNativeView(); }
- FlutterView createFlutterView(Context context):该方法比较直观,就是生成一个Flutter的页面,供Activity展示。但是并没有在源码中找到引用它的地方。FlutterActivity的实现返回值是null。
- FlutterNativeView createFlutterNativeView():从字面意思是生成一个Flutter的原生View,但是并没有在源码中找到引用它的地方。FlutterActivity的实现返回值也是null。
- boolean retainFlutterNativeView():字面意思,保留Flutter原生页面。是一个boolean类型的值,但是并没有在源码中找到引用它的地方。FlutterActivity的实现返回值是false。
通过查看FlutterActivity所继承的三个接口,我们并没有找到FlutterActivity中直接生成FlutterView的线索,只能从实例变量中查找。
FlutterActivityDelegate
在进行一番阅读之后,发现该委派类。在Android源码中有很多使用委派模式的地方,该处也算是一个。并且,在FlutterActivity中,FlutterActivityDelegate对象会跟随Activity的生命周期方法被调用同名方法。查看FlutterActivityDelegate的源码
- 构造方法
其构造方法需要传入一个Activity对象,还有FlutterActivityDelegate.ViewFactory对象。但在上文已经发现FlutterActivityDelegate.ViewFactory的方法并无引用的地方,这里只需要着重关注Activity对象就好了。public FlutterActivityDelegate(Activity activity, FlutterActivityDelegate.ViewFactory viewFactory) { this.activity = (Activity)Preconditions.checkNotNull(activity); this.viewFactory = (FlutterActivityDelegate.ViewFactory)Preconditions.checkNotNull(viewFactory); }
- 同名生命周期方法:查看FlutterActivityDelegate类源码,该类定义了一些列对象Activity生命周期函数的同名函数。并分别运行在FlutterActivity类的对应生命周期中,由此可见Flutter页面的生命周期是由该委托类处理的。接下来逐个分析生命周期函数
- onCreate:该方法中实现了flutterView的生成。查看代码,由于代码量大,这里只显示关键代码
从FlutterActivity实现的ViewFactory方法我们已经得知,传递给委托类FlutterActivityDelegate实例的ViewFactory并没有生成FlutterView可供FlutterActivityDelegate使用。所以只能继续查看public void onCreate(Bundle savedInstanceState) { ... this.flutterView = this.viewFactory.createFlutterView(this.activity); if (this.flutterView == null) { FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView(); this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView); this.flutterView.setLayoutParams(matchParent); this.activity.setContentView(this.flutterView); this.launchView = this.createLaunchView(); if (this.launchView != null) { this.addLaunchView(); } } ... }
this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView)
之后的代码。
我们可以看到FlutterView继承自SurfaceView,在其构造方法中。如果传递的FlutterNativeView如果为空,那将会重新创建一个默认的FlutterNativeView。接着看public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry, AccessibilityStateChangeListener { public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { super(context, attrs); ... Activity activity = (Activity)this.getContext(); if (nativeView == null) { this.mNativeView = new FlutterNativeView(activity.getApplicationContext()); } else { this.mNativeView = nativeView; } this.mNativeView.attachViewAndActivity(this, activity); ... } }
在这里我们可以看到FlutterNativeView实现了BinaryMessenger接口,而BinaryMessenger是一个数据信息交流对象,其接口声明如下public class FlutterNativeView implements BinaryMessenger { public FlutterNativeView(Context context) { this(context, false); } public FlutterNativeView(Context context, boolean isBackgroundView) { this.mNextReplyId = 1; this.mPendingReplies = new HashMap(); this.mContext = context; this.mPluginRegistry = new FlutterPluginRegistry(this, context); this.attach(this, isBackgroundView); this.assertAttached(); this.mMessageHandlers = new HashMap(); } }
要命的是Flutter框架在Android中还没有注释可以看,只能从官网查看文档。public interface BinaryMessenger { /** *Sends a binary message to the Flutter application. *Parameters: *channel - the name String of the logical channel used for the message. *message - the message payload, a direct-allocated ByteBuffer with the message bytes between position zero and current position, or null. */ void send(String var1, ByteBuffer var2); /** * Sends a binary message to the Flutter application, optionally expecting a reply. * Any uncaught exception thrown by the reply callback will be caught and logged. * <p> * Parameters: * channel - the name String of the logical channel used for the message. * message - the message payload, a direct-allocated ByteBuffer with the message bytes between position zero and current position, or null. * callback - a BinaryMessenger.BinaryReply callback invoked when the Flutter application responds to the message, possibly null. */ void send(String var1, ByteBuffer var2, BinaryMessenger.BinaryReply var3); /** * Registers a handler to be invoked when the Flutter application sends a message to its host platform. * Registration overwrites any previous registration for the same channel name. Use a null handler to deregister. * <p> * If no handler has been registered for a particular channel, any incoming message on that channel will be handled silently by sending a null reply. * <p> * Parameters: * channel - the name String of the channel. * handler - a BinaryMessenger.BinaryMessageHandler to be invoked on incoming messages, or null. */ void setMessageHandler(String var1, BinaryMessenger.BinaryMessageHandler var2); /** * Binary message reply callback. Used to submit a reply to an incoming message from Flutter. * Also used in the dual capacity to handle a reply received from Flutter after sending a message. */ public interface BinaryReply { /** * Handles the specified reply. * Parameters: * reply - the reply payload, a direct-allocated ByteBuffer or null. * Senders of outgoing replies must place the reply bytes between position zero and current position. * Reply receivers can read from the buffer directly. */ void reply(ByteBuffer var1); } /** * Handler for incoming binary messages from Flutter. */ public interface BinaryMessageHandler { /** * Handles the specified message. * Handler implementations must reply to all incoming messages, * by submitting a single reply message to the given BinaryMessenger.BinaryReply. * Failure to do so will result in lingering Flutter reply handlers. The reply may be submitted asynchronously. * <p> * Any uncaught exception thrown by this method will be caught by the messenger implementation and logged, * and a null reply message will be sent back to Flutter. * <p> * Parameters: * message - the message ByteBuffer payload, possibly null. * reply - A BinaryMessenger.BinaryReply used for submitting a reply back to Flutter. */ void onMessage(ByteBuffer var1, BinaryMessenger.BinaryReply var2); } }
这是一个用于在Flutter和Native之间交换数据的接口类,已知FlutterView已经实现了SurfaceView,flutterNativeView负责FlutterView和Flutter之间的通讯,再使用Skia绘制页面。
总结
在阅读完FlutterActivity的部分源码以后,得出了以上几个问题的答案。
- 继承FlutterActivity之后,重写ViewFactory中的方法可以进入不同的FlutterView页面。
- 在FlutterActivityDelegate委托类里,实现了对FlutterActivity和Flutter页面生命周期的管理
- HelloFlutter——MethodChannel(Native&Flutter数据交互)
- FlutterView继承了SurfaceView,使用FlutterNativeView在Android和Flutter之间作为通讯的桥梁,之后调用Skia框架绘制页面。这也是其与RN和其他依赖于WebView的混合开发的框架不同的根源。
本篇文章只是闭门造车的结果,如有任何错误很抱歉!请帮忙指出,多谢了