前面一篇介绍了承载Flutter的Activity的初始化过程,通过分析FlutterActivityDelegate我们知道了FlutterView是真正运行Flutter App的地方,所以这一篇文章主要来看一看FlutterView的功能。
一 FlutterView 类结构
先看一下FlutterView的UML图。
- 继承SurfaceView,它的内部有两个线程即主线程和渲染线程,使用渲染线程中向屏幕上绘图可以避免主线程阻塞,从而提高了程序的反应速度。使用了双缓冲机制。
- 实现BinaryMesaenger接口, 这个接口定义了Android代码和Flutter代码直接通信的方式,此接口还有2个内部接口。 FlutterView实现了这个接口,拥有了和flutter通信的的能力
- 实现TextureRegistry接口,这个接口主要是创建一个SurfaceTexture对象,使用SurfaceTextureEntry包装起来,拥有唯一的ID。
- 实现AccessibilityStateChangeListener接口,主要用来监听系统accessibility 状态变化的。
成员变量
private final DartExecutor dartExecutor;
private final NavigationChannel navigationChannel;
private final KeyEventChannel keyEventChannel;
private final LifecycleChannel lifecycleChannel;
private final SettingsChannel settingsChannel;
private final SystemChannel systemChannel;
private final InputMethodManager mImm;
private final TextInputPlugin mTextInputPlugin;
private final AndroidKeyProcessor androidKeyProcessor;
private final SurfaceHolder.Callback mSurfaceCallback;
private final ViewportMetrics mMetrics;
private final AccessibilityManager mAccessibilityManager;
private final MethodChannel mFlutterLocalizationChannel;
private final List<ActivityLifecycleListener> mActivityLifecycleListeners;
private final List<FirstFrameListener> mFirstFrameListeners;
private final AtomicLong nextTextureId = new AtomicLong(0L);
private FlutterNativeView mNativeView;
private final AnimationScaleObserver mAnimationScaleObserver;
private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not
private InputConnection mLastInputConnection;
上面列举出了FlutterView中的成员变量,大致分类:
- Channel: 有一系列了的Channel,在Flutter中,Channel是用Android和Flutter进行通信的管道,他们都是基于BinaryMesaenger进行通信。后面会单独介绍Channel通信,这里只需要知道FlutterView中有一系列Channel可以让Android和Flutter进行交互。
- 系统相关:InputMethodManager、AndroidKeyProcessor、SurfaceHolder.Callback、AccessibilityManager、AnimationScaleObserver、InputConnection 这些成员变量都是和系统相关,通知Flutter处理输入、按键等操作以及一些系统状态。
- FlutterNativeView: 这个是我们在Activity初始化见过,后面单独分析。
简单的从类的结构来看Flutter主要提供了和Flutter交互的的能力,以及为Flutter提供了Android系统的一些信息。
二 FlutterView创建
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
在FlutterActivity初始化的时候,会通过FlutterActivityDelegate来初始化Flutter Engine,然后就会创建FlutterView。主要看看FlutterView的构造函数
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView)
这个构造函数内容比较多,我们分段来看:
1. 创建FlutterNativeView
如果外部没有传入FlutterNativeView,内部会自动创建一个。看似只有一行带代码,其实里面做了很多事情。
Activity activity = (Activity) getContext(); if (nativeView == null) { mNativeView = new FlutterNativeView(activity.getApplicationContext()); } else { mNativeView = nativeView; }
FlutterView实现了BinaryMeeenger接口, 先看一下创建的过程:
public FlutterNativeView(Context context, boolean isBackgroundView) { mContext = context; mPluginRegistry = new FlutterPluginRegistry(this, context); mFlutterJNI = new FlutterJNI(); mFlutterJNI.setRenderSurface(new RenderSurfaceImpl()); mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl()); mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl()); attach(this, isBackgroundView); assertAttached(); mMessageHandlers = new HashMap<>(); }
主要创建了FlutterPluginRegistry和FlutterJNI 对象,这是FlutterNativeView最重要的两个成员变量:
- FlutterPluginRegistry: 这个类实现了PluginRegistry的全部接口,是真正实现Plugin注册功能的类。 在FlutterActivity和FlutterActivityDelegate都实现了这个接口,但最终都是调用到了这里。
- FlutterJNI: 这个类是调用Engine层c++方法的一个封装类。所有Flutter JNI的调用都集合在这个类中。 一个FlutterJNI对象是和一个Platform View相关联,所以包含一个nativePlatformViewId来标识
创建完这2个对象之后,调用了FlutterJNI下面的个方法,这3个方法创建了3个内部类实例,设置给了FlutterJNI的成员变量。 使用这3个成员变量的方法并没有被java代码调用,注释上写被native调用,应该是在C++层的Engine被调用。主要是一些Flutter Engine通知Android 程序一些事件,比如渲染第一帧、Flutter发送过来的消息、Engine的生命周期。
public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurface) {
this.renderSurface = renderSurface;
}
public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) {
this.platformMessageHandler = platformMessageHandler;
}
public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) {
engineLifecycleListeners.add(engineLifecycleListener);
}
然后调用了attach方法
private void attach(FlutterNativeView view, boolean isBackgroundView) { mFlutterJNI.attachToNative(isBackgroundView); }
内部还是调用FlutterJNI的方法,最终调用了native的方法获得了一个nativePlatformViewId
@UiThread public void attachToNative(boolean isBackgroundView) { ensureNotAttachedToNative(); nativePlatformViewId = nativeAttach(this, isBackgroundView); } private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView);
2. 创建成员变量
mIsSoftwareRenderingEnabled = mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled(); mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); mMetrics = new ViewportMetrics(); mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; setFocusable(true); setFocusableInTouchMode(true); mNativeView.attachViewAndActivity(this, activity); mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { assertAttached(); mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface()); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { assertAttached(); mNativeView.getFlutterJNI().onSurfaceChanged(width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { assertAttached(); mNativeView.getFlutterJNI().onSurfaceDestroyed(); } }; getHolder().addCallback(mSurfaceCallback); mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); mActivityLifecycleListeners = new ArrayList<>(); mFirstFrameListeners = new ArrayList<>();
这里比较杂,主要做了几件事:
- 获取Flutter是否启用软件渲染
- 创建AnimationScaleObserver用于监听手机时候开启了动画
- 创建ViewportMetrics,主要用来保存一下显示相关的信息,比如屏幕高宽、像素密度等
- 关联FlutterNativeView和activity (里面具体步骤省略)
- 监听SurfaceView的回调,并且通过FlutterNativeView通知给Engine
3. 配置平台Plugin和Channel
dartExecutor = new DartExecutor(mNativeView.getFlutterJNI()); // Configure the platform plugins and flutter channels. navigationChannel = new NavigationChannel(dartExecutor); keyEventChannel = new KeyEventChannel(dartExecutor); lifecycleChannel = new LifecycleChannel(dartExecutor); systemChannel = new SystemChannel(dartExecutor); settingsChannel = new SettingsChannel(dartExecutor); mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE); PlatformPlugin platformPlugin = new PlatformPlugin(activity); MethodChannel flutterPlatformChannel = new MethodChannel(this, "flutter/platform", JSONMethodCodec.INSTANCE); flutterPlatformChannel.setMethodCallHandler(platformPlugin); addActivityLifecycleListener(platformPlugin); mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); mTextInputPlugin = new TextInputPlugin(this); androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel);
这一部分创建了很多平台使用的channel,用于和Flutter之间通信,而且大多数的channel都传入了一个DartExecutor对象,它实现了BinaryMessenger接口,具有和Flutter通信的能力,所以这些Platform相关的Channel并没有使用FlutterNativeView 作为Messenger, 而是使用了DartExecutor, 他提供了执行指定Dart代码的能力(通过DartEntrypoint指定要执行的方法和代码的位置)。
public class DartExecutor implements BinaryMessenger
最后使用上面的channel把相关的一些信息传递给了Flutter(Dart)
setLocales(getResources().getConfiguration()); sendUserPlatformSettingsToDart();
三 执行Flutter程序
创建了FlutterView,通过FlutterViewNative、FlutterJNI 、Flutter Channel, 当前的FlutterView或者说当前的Activity已经具有了和Flutter Engine交互的能力。接下来就是运行Flutter程序。
args.bundlePaths = bundlePaths.toArray(new String[0]); args.entrypoint = "main"; flutterView.runFromBundle(args);
FlutterActivityDelegate中通过调用FlutterView的runFromBundle方法来执行。
private void preRun() { resetAccessibilityTree(); } private void postRun() { } public void runFromBundle(FlutterRunArguments args) { assertAttached(); preRun(); mNativeView.runFromBundle(args); postRun(); }
FlutterView里调用的是FlutterNativeView的方法
public void runFromBundle(FlutterRunArguments args) { boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0; if (args.bundlePath == null && !hasBundlePaths) { throw new AssertionError("Either bundlePath or bundlePaths must be specified"); } else if ((args.bundlePath != null || args.defaultPath != null) && hasBundlePaths) { throw new AssertionError("Can't specify both bundlePath and bundlePaths"); } else if (args.entrypoint == null) { throw new AssertionError("An entrypoint must be specified"); } if (hasBundlePaths) { runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath); } else { runFromBundleInternal(new String[] {args.bundlePath, args.defaultPath}, args.entrypoint, args.libraryPath); } }
这里主要检查了一下传入的参数,必须传递bundle文件路径和entrypoint 。使用runFromBundleInternal来处理
private void runFromBundleInternal(String[] bundlePaths, String entrypoint, String libraryPath) { assertAttached(); if (applicationIsRunning) throw new AssertionError( "This Flutter engine instance is already running an application"); mFlutterJNI.runBundleAndSnapshotFromLibrary( bundlePaths, entrypoint, libraryPath, mContext.getResources().getAssets() ); applicationIsRunning = true; }
最终调用FlutterJNI让Engine来运行Flutter bundle。Flutter就会开始执行Dart程序,在FlutterView的SurfaceView绘制UI。
四 其他功能
FlutterView作为运行Flutter程序的容器,还有许多重要的功能,下面只简单的列举,因为每一个部都是可以单独去研究的。
生命周期
FlutterActivityDelegate会把Activity的声明周期都通知给FlutterView,所以FlutterView也有类似的声明周期的回调方法。在回调方法中FlutterView通过LifecycleChannel把声明周期的信息同步给了Flutter。
public void onPause() {
lifecycleChannel.appIsInactive();
}
public void onPostResume() {
updateAccessibilityFeatures();
for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
listener.onPostResume();
}
lifecycleChannel.appIsResumed();
}
public void onStop() {
lifecycleChannel.appIsPaused();
}
触屏事件处理
FlutterView重写了触屏相关的事件,中间手机了一些点击信息,最后都通过FlutterJNI().dispatchPointerDataPacket 发送给了Flutter, 这些后续研究触屏事件是在具体研究。
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isAttached()) {
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
requestUnbufferedDispatch(event);
}
final int kPointerDataFlagBatched = 1;
int pointerCount = event.getPointerCount();
ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kPointerBytesPerField);
packet.order(ByteOrder.LITTLE_ENDIAN);
int maskedAction = event.getActionMasked();
int pointerChange = getPointerChangeForAction(event.getActionMasked());
if (maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet);
} else if (maskedAction == MotionEvent.ACTION_UP || maskedAction == MotionEvent.ACTION_POINTER_UP) {
for (int p = 0; p < pointerCount; p++) {
if (p != event.getActionIndex()) {
if (event.getToolType(p) == MotionEvent.TOOL_TYPE_FINGER) {
addPointerForIndex(event, p, kPointerChangeMove, kPointerDataFlagBatched, packet);
}
}
}
addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet);
} else {
for (int p = 0; p < pointerCount; p++) {
addPointerForIndex(event, p, pointerChange, 0, packet);
}
}
if (packet.position() % (kPointerDataFieldCount * kPointerBytesPerField) != 0) {
throw new AssertionError("Packet position is not on field boundary");
}
mNativeView.getFlutterJNI().dispatchPointerDataPacket(packet, packet.position());
return true;
}
@Override
public boolean onHoverEvent(MotionEvent event) {
if (!isAttached()) {
return false;
}
boolean handled = handleAccessibilityHoverEvent(event);
if (!handled) {
// TODO(ianh): Expose hover events to the platform,
// implementing ADD, REMOVE, etc.
}
return handled;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (!event.isFromSource(InputDevice.SOURCE_CLASS_POINTER) ||
event.getActionMasked() != MotionEvent.ACTION_HOVER_MOVE ||
!isAttached()) {
return super.onGenericMotionEvent(event);
}
int pointerChange = getPointerChangeForAction(event.getActionMasked());
ByteBuffer packet = ByteBuffer.allocateDirect(
event.getPointerCount() * kPointerDataFieldCount * kPointerBytesPerField);
packet.order(ByteOrder.LITTLE_ENDIAN);
// ACTION_HOVER_MOVE always applies to a single pointer only.
addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet);
if (packet.position() % (kPointerDataFieldCount * kPointerBytesPerField) != 0) {
throw new AssertionError("Packet position is not on field boundary");
}
mNativeView.getFlutterJNI().dispatchPointerDataPacket(packet, packet.position());
return true;
}
和Flutter通信
FlutterView实现了BinaryMessenger接口,但是内部实际是调用FlutterNativeView的方法。关于和Flutter通信,后面也会单独研究。
@Override
public void send(String channel, ByteBuffer message) {
send(channel, message, null);
}
@Override
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}
mNativeView.send(channel, message, callback);
}
@Override
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
mNativeView.setMessageHandler(channel, handler);
}
对于FlutterNativeView来说,它是调用了FlutterJNI.dispatchPlatformMessage来发送消息
public void send(String channel, ByteBuffer message, BinaryReply callback) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.send called on a detached view, channel=" + channel);
return;
}
int replyId = 0;
if (callback != null) {
replyId = mNextReplyId++;
mPendingReplies.put(replyId, callback);
}
if (message == null) {
mFlutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
} else {
mFlutterJNI.dispatchPlatformMessage(
channel,
message,
message.position(),
replyId
);
}
}
五 总结
这一篇主要介绍了FlutterView初始化的流程和其他一些功能。FlutterView是比较重要的一个类:
- FlutterView提供了SurfaceView来绘制Flutter的UI
- 同时把Android的生命周期、点击事件等信息发送给Flutter。
- FlutterView并不负责直接和Flutter Engine通信,具体的通信功能在FlutterNativeView
- FlutterNativeView内部有一个FlutterJNI,它负责调用Flutter Engine的C++代码。