博主相关文章列表
Flutter 框架实现原理
Flutter 框架层启动源码剖析
Flutter 页面更新流程剖析
Flutter 事件处理源码剖析
Flutter 路由源码剖析
Flutter 安卓平台源码剖析(一)
Flutter 自定义控件之RenderObject
做技术,只有弄懂了原理,才能遇事不慌,手中无码,心中有码。这篇文章主要研究Flutter 在安卓平台上的启动流程源码。
当我们创建一个Flutter app工程时,打开android目录下的源码,会发现有一个MainActivity
继承自FlutterActivity
,整个MainActivity
非常简单,只在onCreate
下加了一行代码GeneratedPluginRegistrant.registerWith(this)
,那么FlutterActivity
又是何方神圣呢?FlutterActivity
的源码在Flutter SDK的jar包中,想要研究Flutter源码,第一件事就是需要下载一套SDK源码,我们可以在GitHub上下载 engine源码
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
engine\src\flutter\shell\platform\android\io\flutter\app\FlutterActivity.java
省略部分源码,删除注释后代码如下
public class FlutterActivity extends Activity implements
FlutterView.Provider, PluginRegistry, ViewFactory {
private static final String TAG = "FlutterActivity";
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate = delegate;
private final FlutterView.Provider viewProvider = delegate;
private final PluginRegistry pluginRegistry = delegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventDelegate.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
eventDelegate.onStart();
}
@Override
protected void onResume() {
super.onResume();
eventDelegate.onResume();
}
@Override
protected void onDestroy() {
eventDelegate.onDestroy();
super.onDestroy();
}
// ...省略部分源码...
}
可以看到FlutterActivity
继承自Activity
,并实现了三个接口,FlutterActivity
的生命周期方法,均由一个代理类FlutterActivityDelegate
处理。
engine\src\flutter\shell\platform\android\io\flutter\app\FlutterActivityDelegate.java
@Override
public void onCreate(Bundle savedInstanceState) {
// 根据当前系统版本设置沉浸式状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
}
// 获取intent传入的参数信息
String[] args = getArgsFromIntent(activity.getIntent());
// 初始化一些参数配置信息,包括打包的flutter代码路径、应用存储目录、引擎缓存目录等
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
flutterView = viewFactory.createFlutterView(activity);
// 真正创建contentView的地方
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();
}
}
if (loadIntent(activity.getIntent())) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
runBundle(appBundlePath);
}
}
我们自然是要找到onCreate
方法,以上代码我增加了一点注释,我们要快速定位到关键代码,什么是关键代码,看到了activity.setContentView(flutterView);
,这里就是关键代码,终于见到了我们熟悉的setContentView
了。这里我们不禁要问,这个flutterView
到底是个什么View?
一开始我们就知道ViewFactory
是FlutterActivity
实现的,这里createFlutterView
方法实现也在FlutterActivity
里,但这个方法始终返回空,再看createFlutterNativeView
方法,仍然是返回空
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
这里真正的flutterView
实际上是通过flutterView = new FlutterView(activity, null, nativeView)
这行代码new
出来的,然后传递给setContentView
接下来直接看到FlutterView
源码(省略部分代码)
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
private static final String TAG = "FlutterView";
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
Activity activity = getActivity(getContext());
if (activity == null) {
throw new IllegalArgumentException("Bad context");
}
if (nativeView == null) {
mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
mNativeView = nativeView;
}
dartExecutor = mNativeView.getDartExecutor();
flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();
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);
mActivityLifecycleListeners = new ArrayList<>();
mFirstFrameListeners = new ArrayList<>();
// Create all platform channels
navigationChannel = new NavigationChannel(dartExecutor);
keyEventChannel = new KeyEventChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
systemChannel = new SystemChannel(dartExecutor);
settingsChannel = new SettingsChannel(dartExecutor);
// Create and setup plugins
PlatformPlugin platformPlugin = new PlatformPlugin(activity, platformChannel);
addActivityLifecycleListener(platformPlugin);
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mTextInputPlugin = new TextInputPlugin(this, dartExecutor);
androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel, mTextInputPlugin);
androidTouchProcessor = new AndroidTouchProcessor(flutterRenderer);
// Send initial platform information to Dart
sendLocalesToDart(getResources().getConfiguration());
sendUserPlatformSettingsToDart();
}
到这里就明白了,FlutterView
实际上就是安卓中的SurfaceView
,因为SurfaceView
是双缓冲的,可以在子线程更新UI,性能高效,因此通常是用来做游戏开发、视频直播。经过简单的源码分析,我们大致能明白Flutter在安卓上的实现方式,整个Flutter开发的app都是在一个Activity
中进行渲染的,这就有点像现在前端流行的所谓单页应用。
还个方法中还创建了各种平台插件和platform channel
,用于Flutter层和原生代码之间的数据传递。
现在我们再回过头来看一下刚刚漏过的一些代码,看看它们做了些什么事
onCreate
函数中有调用FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args)
/**
* Blocks until initialization of the native system has completed.
*/
public static void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
}
if (sSettings == null) {
throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
}
if (sInitialized) {
return;
}
try {
sResourceExtractor.waitForCompletion();
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY);
// ...省略...
if (sSettings.getLogTag() != null) {
shellArgs.add("--log-tag=" + sSettings.getLogTag());
}
String appBundlePath = findAppBundlePath(applicationContext);
String appStoragePath = PathUtils.getFilesDir(applicationContext);
String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
nativeInit(applicationContext, shellArgs.toArray(new String[0]),
appBundlePath, appStoragePath, engineCachesPath);
sInitialized = true;
} catch (Exception e) {
Log.e(TAG, "Flutter initialization failed.", e);
throw new RuntimeException(e);
}
}
该方法注释已经明确告诉我们,这是一个阻塞的方法,直到底层初始化完成。这意味着该方法执行会影响app的启动速度。总的来说,该方法做了几件事,它保证初始化操作在主线程运行,调用sResourceExtractor.waitForCompletion()
完成资源文件的提取工作,拼接所有相关的shellArgs
参数,包括intent
中的参数,配置dart
代码编译产物appBundle路径,应用存储、引擎缓存目录等信息,
最后通过执行nativeInit
函数在c++层初始化这些信息
接下来,还有一个地方值得一说,在创建FlutterView
之后,调用了一个createLaunchView
方法
private View createLaunchView() {
if (!showSplashScreenUntilFirstFrame()) {
return null;
}
final Drawable launchScreenDrawable = getLaunchScreenDrawableFromActivityTheme();
if (launchScreenDrawable == null) {
return null;
}
final View view = new View(activity);
view.setLayoutParams(matchParent);
view.setBackground(launchScreenDrawable);
return view;
}
这个方法,其实就是在Flutter启动后添加一个splash view
。大家知道,在Flutter应用启动之后,会有一小段白屏的时间,之所以会白屏,就是这个方法导致的。目前的解决办法,就是手动添加一个设计好的闪屏页,平滑的过度一下。那么如何修改默认的白屏页,设置我们自己的splash view
呢?
private Drawable getLaunchScreenDrawableFromActivityTheme() {
TypedValue typedValue = new TypedValue();
if (!activity.getTheme().resolveAttribute(
android.R.attr.windowBackground,
typedValue,
true)) {
return null;
}
if (typedValue.resourceId == 0) {
return null;
}
try {
return activity.getResources().getDrawable(typedValue.resourceId);
} catch (NotFoundException e) {
Log.e(TAG, "Referenced launch screen windowBackground resource does not exist");
return null;
}
}
可以看到,这个Drawable
其实是从主题的windowBackground
中取的,我们打开app工程中的styles.xml
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
- "android:windowBackground"
>@drawable/launch_background
style>
看到,windowBackground
其实是指定为launch_background
,在drawable
文件夹下找到它
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
layer-list>
这里默认背景确实被设置成了白色,并且还友好的给出了一个设置图片的示例,将注释的代码打开,就可以设置自己的splash view
我们知道安卓app启动时,首先调用的是Application
的onCreate
,现在来看一看Flutter应用的Application做了些什么
flutter\shell\platform\android\io\flutter\app\FlutterApplication.java
public class FlutterApplication extends Application {
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity() {
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity) {
this.mCurrentActivity = mCurrentActivity;
}
}
整个FlutterApplication
比较简单,主要调用了FlutterMain.startInitialization(this)
开启初始化
public static void startInitialization(@NonNull Context applicationContext, @NonNull Settings settings) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("startInitialization must be called on the main thread");
}
// Do not run startInitialization more than once.
if (sSettings != null) {
return;
}
sSettings = settings;
long initStartTimestampMillis = SystemClock.uptimeMillis();
initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);
System.loadLibrary("flutter");
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
nativeRecordStartTimestamp(initTimeMillis);
}
该方法注释表明,它是用来初始化底层的Flutter 引擎的,主要做了几件事,
在FlutterApplication
中加载了引擎的so,我们不禁要问,那Flutter引擎又是在哪创建的,怎么与原生Java代码关联起来的呢?
在FlutterView
构造方法中创建了FlutterNativeView
,看到FlutterNativeView
的构造方法
public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
mFlutterJNI = new FlutterJNI();
mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
this.dartExecutor = new DartExecutor(mFlutterJNI);
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
attach(this, isBackgroundView);
assertAttached();
}
在这里主要创建了FlutterJNI
对象,并调用了一个关键方法attach
,一路跟踪该方法调用
private void attach(FlutterNativeView view, boolean isBackgroundView) {
mFlutterJNI.attachToNative(isBackgroundView);
dartExecutor.onAttachedToJNI();
}
flutter\shell\platform\android\io\flutter\embedding\engine\FlutterJNI.java
public void attachToNative(boolean isBackgroundView) {
ensureRunningOnMainThread();
ensureNotAttachedToNative();
nativePlatformViewId = nativeAttach(this, isBackgroundView);
}
private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView);
最后发现调用了一个native
方法,并将FlutterJNI
实例对象自身传入了C++
层。这里,我们怎么才能找到native
方法所对应的C++源码呢?
我这里就介绍一种最简单的傻瓜式方法,不需要太多技巧,大家可以下载一个FileLocatorPro工具,它是Windows平台上的文本搜索神器,建议最好安装一个。
为了搜索更快,我们不仅仅将下载的Flutter engine源码根路径设置进去,还要缩写一点范围,我这里将源码根目录下的flutter\shell\platform\android
设置为搜索路径,另外还可以在文件名称一栏填入*.cc
,表示仅搜索以.cc
作为后缀的文件,这里.cc
是C++源文件后缀名。最后我们秒搜到了匹配的内容
flutter\shell\platform\android\platform_view_android_jni.cc
static const JNINativeMethod flutter_jni_methods[] = {
// Start of methods from FlutterNativeView
{
.name = "nativeAttach",
.signature = "(Lio/flutter/embedding/engine/FlutterJNI;Z)J",
.fnPtr = reinterpret_cast<void*>(&AttachJNI),
}
这是一个结构体数组,可以看到nativeAttach
对应的函数指针是AttachJNI
,我们继续在当前文件中找到AttachJNI
函数,它就是Java层nativeAttach
方法的具体实现
// Called By Java
static jlong AttachJNI(JNIEnv* env,
jclass clazz,
jobject flutterJNI,
jboolean is_background_view) {
fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
auto shell_holder = std::make_unique<AndroidShellHolder>(
FlutterMain::Get().GetSettings(), java_object, is_background_view);
if (shell_holder->IsValid()) {
return reinterpret_cast<jlong>(shell_holder.release());
} else {
return 0;
}
}
这里的C++代码主要干了一件事,就是通过make_unique
来创建了一个AndroidShellHolder
对象,因此我们需要找到AndroidShellHolder
类的构造方法
flutter/shell/platform/android/android_shell_holder.cc
AndroidShellHolder::AndroidShellHolder(
flutter::Settings settings,
fml::jni::JavaObjectWeakGlobalRef java_object,
bool is_background_view)
: settings_(std::move(settings)), java_object_(java_object) {
static size_t shell_count = 1;
auto thread_label = std::to_string(shell_count++);
FML_CHECK(pthread_key_create(&thread_destruct_key_, ThreadDestructCallback) ==
0);
if (is_background_view) {
thread_host_ = {thread_label, ThreadHost::Type::UI};
} else {
thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
ThreadHost::Type::IO};
}
// ...省略...
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr<fml::TaskRunner> gpu_runner;
fml::RefPtr<fml::TaskRunner> ui_runner;
fml::RefPtr<fml::TaskRunner> io_runner;
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
if (is_background_view) {
auto single_task_runner = thread_host_.ui_thread->GetTaskRunner();
gpu_runner = single_task_runner;
ui_runner = single_task_runner;
io_runner = single_task_runner;
} else {
gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
ui_runner = thread_host_.ui_thread->GetTaskRunner();
io_runner = thread_host_.io_thread->GetTaskRunner();
}
flutter::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // gpu
ui_runner, // ui
io_runner // io
);
shell_ =
Shell::Create(task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
platform_view_ = weak_platform_view;
FML_DCHECK(platform_view_);
is_valid_ = shell_ != nullptr;
// ...省略...
}
这个方法里面代码比较多,省略部分代码,我们看到最重要的几个地方,首先这里有四个线程,除了当前线程作为platform
线程,还创建了三个新的线程
gpu
线程ui
线程io
线程四个线程分别持有一个TaskRunner
对象,后续会通过这些TaskRunner
来将一些操作放到对应的线程中去执行。
Platform Task Runner
的线程可以理解为是主线程,它不仅仅处理与Engine交互,它还处理来自平台的消息。
UI Task Runner
被用于执行Dart root isolate代码,简单说也就是Dart语言的主线程。我们所编写的Dart代码基本就是在这个线程运行。因此该线程繁忙会导致UI卡顿,如果有繁重的计算任务,如加密、解压缩等,应当在Dart中另起一个isolate
来执行代码。需要注意,另启的isolate
是不能与Flutter引擎交互的,表现在开发中,也就是不能在新创建的isolate
中调用插件,例如在新的isolate
操作SQlite数据库,如有这种需求,可以使用FlutterIsolate 库
GPU Task Runner
用于执行设备GPU的相关调用。主要是配置管理每一帧绘制所需要的GPU资源。如果该线程卡顿,直接导致程序卡顿。通常来说用平台代码和Dart代码都无法直接操作到该线程
IO Runner
的主要是从图片存储(如磁盘)中读取压缩的图片格式,将图片数据进行处理,为GPU Runner的渲染做好准备。也就是处理与磁盘IO相关的事务
最后,看到以下函数的调用,此处即是创建引擎的地方
shell_ =
Shell::Create(task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
大家有兴趣可以找相关源码,继续跟踪一下源码,看看引擎是如何创建的
\flutter\shell\common\shell.cc
通过反编译Flutter生成的apk,我们很清楚的知道,Dart代码编译后的产物,包括相关的资源文件,实际上都是打包到安卓的assets
目录下的,那么Dart代码是在哪加载执行的呢?
在FlutterActivityDelegate
的onCreate
方法的最后有如下代码
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
runBundle(appBundlePath);
}
这里首先获取资源文件提取后在应用所属目录下的路径,然后调用runBundle
进行加载执行。runBundle
方法最终调用FlutterJNI
中的一个native方法
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativePlatformViewId,
@NonNull String[] prioritizedBundlePaths,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager
);
FlutterMain.ensureInitializationComplete
方法调用
如需要获取完整的Flutter全栈式开发课程,请访问以下地址
Flutter全栈式开发之Dart 编程指南
Flutter 全栈式开发指南