網站首頁 編程語言 正文
前言
flutter可以說是當下最流行的跨平臺技術了,其最突出的
網上可以搜到的文章,大多數都是flutter的用法,即使介紹其實現原理的,也直接深入源碼直接解讀,造成只有一定功能的讀者才能理解。
本文希望以最通俗易解的方式介紹flutter的實現原理,也許不會介紹的深入或者詳細,但是一定能讓讀者知道flutter的基本實現原理。
本文基于flutter2.0的源碼進行原理分析,3.0的源碼有些許變動,但整體流程是一樣的。
一.安卓原生界面繪制的流程
原生繪制流程
有另外的一個系列文章來講原生的界面,為了方便讀者閱讀,本文會簡略描述一下整個流程。
其主要流程是在每次sync的時候去執行測量(measure),布局(layout),繪制(draw)的流程。
而draw的時候時候,核心是利用canvas執行各種繪制命令,并且把這些命令轉換為buffer記錄,最終發送給WMS層,然后轉交給SurfaceFlinger,由其做最終的合成和渲染。
SurfaceView繪制流程
另外也許你還聽說過另外一種可以在子線程渲染的控件:surfaceView。我們的視頻播放器,高頻繪制的自定義View都是由其實現的。
其主要流程圖如下:
其原理其實和第一種方式類似,區別就是在于少了measure,layout的流程。而是自己去計算坐標,然后直接進入draw的流程,通過canvas寫入native的數據buffer內存中,最后統一發送給WMS進行進入渲染的流程。
而Flutter的實現原理,其實就和surfaceView類似。
二.Flutter上界面繪制的流程
flutter有混合開發和純flutter開發兩種。純flutter使用的是FlutterActivity,而混合開發一般使用的是FlutterView。我們先看一下使用FlutterActivity的方式。
FlutterActivity中的流程
首先看一下FlutterActivity的實現,發現其核心流程都交給了FlutterActivityDelegate處理,所以我們直接看Delegate的onCreate方法:
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();
}
}
...
}
主要流程就是創建一個flutterView,添加到contentView中,所以其實無論哪種方式,最終都是由flutterView來實現的。
FlutterView中的實現
首先我們看一下FlutterView類,發現其繼承自SurfaceView,這也回應了我們上面的描述,其核心實現原理就是基于surfaceView實現的。
其構造方法如下:非核心代碼已做了刪減處理
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
...
//創建在native層的處理對象,相關繪制邏輯其實都是在native層處理的,Java層只負責傳入
this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
//創建dart的解釋器
this.dartExecutor = this.mNativeView.getDartExecutor();
//創建渲染對象
this.flutterRenderer = new FlutterRenderer(this.mNativeView.getFlutterJNI());
//native層的view對象進行綁定
this.mNativeView.attachViewAndActivity(this, activity);
//由于是surfaceView,所以在surface創建好之后傳入naitve
this.mSurfaceCallback = new Callback() {
public void surfaceCreated(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
}
public void surfaceDestroyed(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceDestroyed();
}
};
this.getHolder().addCallback(this.mSurfaceCallback);
//
this.mActivityLifecycleListeners = new ArrayList();
this.mFirstFrameListeners = new ArrayList();
this.navigationChannel = new NavigationChannel(this.dartExecutor);
this.keyEventChannel = new KeyEventChannel(this.dartExecutor);
this.lifecycleChannel = new LifecycleChannel(this.dartExecutor);
this.localizationChannel = new LocalizationChannel(this.dartExecutor);
this.platformChannel = new PlatformChannel(this.dartExecutor);
this.systemChannel = new SystemChannel(this.dartExecutor);
this.settingsChannel = new SettingsChannel(this.dartExecutor);
final PlatformPlugin platformPlugin = new PlatformPlugin(activity, this.platformChannel);
this.addActivityLifecycleListener(new ActivityLifecycleListener() {
public void onPostResume() {
platformPlugin.updateSystemUiOverlays();
}
});
this.mImm = (InputMethodManager)this.getContext().getSystemService("input_method");
PlatformViewsController platformViewsController = this.mNativeView.getPluginRegistry().getPlatformViewsController();
this.mTextInputPlugin = new TextInputPlugin(this, this.dartExecutor, platformViewsController);
this.androidKeyProcessor = new AndroidKeyProcessor(this.keyEventChannel, this.mTextInputPlugin);
this.androidTouchProcessor = new AndroidTouchProcessor(this.flutterRenderer);
this.mNativeView.getPluginRegistry().getPlatformViewsController().attachTextInputPlugin(this.mTextInputPlugin);
this.sendLocalesToDart(this.getResources().getConfiguration());
this.sendUserPlatformSettingsToDart();
其構造方法中,主要流程就是各種功能的初始化,以及完成surface和native的綁定。
我們可以看到下面這樣代碼,就是把surface傳入了native層。
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
所以看到這里,我們可以做這樣的推測了:
flutter原理其實就類似于surfaceView的實現。通過傳遞surface到native層,然后通過這個surface獲取到canvas,寫入渲染buffer,最終通知到WMS完成繪制的整個流程。
native流程
onSurfaceCreated的創建最終會走到native層platform_view_android_jni_impl.cpp中的SurfaceCreated()方法。
static void SurfaceCreated(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jobject jsurface) {
// Note: This frame ensures that any local references used by
// ANativeWindow_fromSurface are released immediately. This is needed as a
// workaround for https://code.google.com/p/android/issues/detail?id=68174
fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(
ANativeWindow_fromSurface(env, jsurface));
ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window));
}
這里很簡單,創建native層的Window對象,調用NotifyCreated方法繼續傳入。
走到platform_view.cc的NotifyCreated方法如下:
void PlatformViewAndroid::NotifyCreated(
fml::RefPtr<AndroidNativeWindow> native_window) {
if (android_surface_) {
//1
InstallFirstFrameCallback();
...
}
//2
PlatformView::NotifyCreated();
}
該方法中主要做了兩件事:
第一件:回調java的onFirstFrame方法;
第二件:啟動渲染流程。
NotifyCreated中,主要是交給delegate_去處理:
void PlatformView::NotifyCreated() {
std::unique_ptr<Surface> surface;
...
delegate_.OnPlatformViewCreated(std::move(surface));
}
這個delegate_其實是shell對象,則會調用到shell.cc的OnPlatformViewCreated方法:
// |PlatformView::Delegate|
void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) {
TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated");
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
...
//這里主要是一系列的判斷,避免死鎖
const bool should_post_raster_task =
!task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread();
fml::AutoResetWaitableEvent latch;
//UI線程執行,渲染的流程
auto raster_task =
fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_,
rasterizer = rasterizer_->GetWeakPtr(), //
surface = std::move(surface)]() mutable {
if (rasterizer) {
// Enables the thread merger which may be used by the external view
// embedder.
rasterizer->EnableThreadMergerIfNeeded();
rasterizer->Setup(std::move(surface));
}
waiting_for_first_frame.store(true);
});
...
auto ui_task = [engine = engine_->GetWeakPtr()] {
if (engine) {
engine->OnOutputSurfaceCreated();
}
};
...
//啟動各種渲染的流程
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task);
latch.Wait();
if (!should_post_raster_task) {
// See comment on should_post_raster_task, in this case the raster_task
// wasn't executed, and we just run it here as the platform thread
// is the raster thread.
raster_task();
}
}
這個方法中,主要就是各種檢查,包括一些鎖機制的判斷,最后通知engine啟動去渲染surface了
三.總結
Flutter的簡單實現原理
Flutter的簡單實現原理其實就類似于surfaceView的實現。
surfaceView中往buffer中寫入渲染數據是通過java層的canvas實現的,而在flutter中是通過native層實現。flutter就是在native層接收到surface,然后通過surface獲取到native層的canvas,對buffer進行寫入,最終通知到WMS完成繪制的整個流程。
當然,詳細的原理還包含了事件流程是如何分發的,如何翻譯dart成可執行的代碼,如何解釋編譯的產物等等,由于篇幅限制,本篇就不詳細展開了,后續會逐漸寫文章進行原理分析。
Flutter的幾個高頻問題
1.為什么主要流程使用jni實現?用Java實現是否可以?
我的理解是其實java實現也是完全可以的,但是要知道flutter是跨平臺的。如果用java的話,那么在安卓上是沒問題的,但是如果在IOS勢必又要用OC在寫一套邏輯,這樣造成重復的工作量。而使用C來編寫,任意平臺其實都是可以通用的,降低開發成本,而且更不容易出現差異。之前和螞蟻金服antv(螞蟻數據可視化團隊)的朋友聊天時,他們也是類似的考慮,底層邏輯使用C實現,安卓/IOS/PC等只做上層的接口封裝和兼容。
2.為什么使用dart而不使用其他語言?
這個我的理解是用JS應該也是可以的,或者說java也可以。但是又都不夠好。
如果是用java的話,flutter的熱部署功能就無法實現,java類加載機制有緩存,一旦加載就無法被替換。當然不是絕對的,可以通過替換classLoader的方式進行替換,類似于tomcat的熱部署。但如果這樣,實現成本就會及其的高,而且性能不佳。
使用js的話,實現熱部署肯定是沒有問題,但問題就在于生產環境,其實更需要的是效率。JIT的編譯方式效率肯定是比不過AOT的。
而dart同時支持AOT和JIT兩種方式,自然是最優的選擇。
原文鏈接:https://blog.csdn.net/rzleilei/article/details/126165230
相關推薦
- 2022-04-22 Element UI 表格操作列按鈕顯示不全
- 2023-02-15 清理或刪除docker無用鏡像的操作方法_docker
- 2022-04-22 Jmeter之控制線程執行到某個結果時退出執行(第二種解決方案)
- 2022-07-02 less與sass(scss)的區別
- 2022-09-10 親自教你在netty中使用TCP協議請求DNS服務器的詳細過程_服務器其它
- 2021-12-02 Docker跨服務器通信Overlay解決方案(上)之?Consul單實例_docker
- 2022-03-18 docker容器啟動設置固定IP的實現_docker
- 2022-08-20 Linux常用高頻命令_linux shell
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支