Embedder
Flutter가 네이티브 플랫폼에 연결하기 위한 핵심 요소로 전체 Flutter 아키텍처의 하단에 위치하여 Engine
생성, 관리, 소멸을 담당하고 Engine
UI를 그리기 위한 인터페이스도 제공합니다. 최하층? 이 기사에서는 자세히 분석합니다.
Embedder 키 클래스 분석
Embedder의 시작 프로세스를 공식적으로 분석하기 전에 특정 프로세스를 분석할 때 Embedder의 논리와 목적을 이해할 수 있도록 Embedder의 주요 클래스와 그 관계를 명확히 해야 합니다. Embedder 레이어의 키 클래스 구조는 그림 4-1에 나와 있습니다.
Embedder에서 FlutterActivity
와 FlutterFragment
개발자들이 가장 많이 접하는 클래스 예를 들어 Counter
기본적으로 생성되는 App에서는 에서 MainActivity
상속됨 이 FlutterActivity
두 클래스의 부모 클래스 Activity
이며 Fragment
안드로이드에서 원스크린 UI를 구현하는데 사용되는 공통 단위 따라서 이 두 클래스의 책임은 Flutter Engine이 그린 UI를 표시하는 것입니다. 그렇다면 이 두 클래스 아래에 숨겨진 논리는 무엇입니까?
FlutterActivity
와 의 대부분의 논리와 책임은 FlutterFragment
동일하므로 동일한 논리 처리를 위한 객체(이하 참조)를 공동으로 보유하고 , 자체 기능 추상화를 위한 인터페이스를 공동으로 구현 FlutterActivityAndFragmentDelegate
합니다 .Delegate
Host
Delegate
두 가지 주요 클래스를 보유하고 있습니다. FlutterEngine
전자 는 Embedder에서 Flutter Engine ( )의 호출 및 관리를 FlutterView
담당 하고 후자는 Flutter Engine에서 UI 데이터의 화면 표시 를 담당합니다 . Engine 의 기능도 사용하게 되므로 보류도 합니다 .libflutter.so
FlutterView
FlutterEngine
또한 특정 구현 FlutterView
도 개최됩니다 . 이름에서 알 수 있듯이 Flutter UI를 렌더링하기 위한 인터페이스이며 세 가지 구현이 있습니다.RenderSurface
RenderSurface
FlutterSurfaceView
Android 기반SurfaceView
구현은 최고의 성능을 갖지만 Android의 View Hierarchy에는 없으며 일반적으로 전체 페이지 Flutter UI 표시에 사용되며 기본적으로 선호됩니다.FlutterTextureView
AndroidTextureView
구현을 기반으로 성능은 전자보다 좋지 않지만 사용자 경험은 Android의 일반 사용자 경험에 더 가깝습니다View
. 이는 Flutter가 기본 UI에 포함된 일부 시나리오 에 더 적합합니다.FlutterImageView
일반적으로 플랫폼 보기가 존재하는 시나리오에서 사용됩니다.
RenderSurface
FlutterRender
엔진의 관련 그리기 기능은 개체를 통해 호출되며 FlutterRender
엔진의 그리기 기능은 Embedder에서 사용하기 편리한 추상화 및 캡슐화됩니다. 또한 엔진에서 Dart Runtime 관련 로직이 호출 FlutterEngine
됩니다 . DartExecutor
이 두 클래스에서 RenderSurface
UI 관련 업무와 DartExecutor
로직 관련 업무를 담당하지만 결국 Engine의 특정 Native 메서드를 호출하게 되므로 FlutterJNI
대부분의 Embedder(Java 코드)와 Engine(C++ 코드) 상호 호출 인터페이스.
다음 개념은 다음 콘텐츠에 여러 번 나타납니다. Surface、SurfaceTexture、SurfaceView、TextureView
. 혼동을 피하기 위해 여기에 통일된 설명이 제공됩니다.
Surface
렌더링 버퍼의 핸들(Handle)을 나타내는 비교적 추상적인 개념으로, 일반적으로SurfaceTexture、MediaRecorder
렌더링 데이터의 소비자가 생성하고(예를 들어) 렌더링 데이터의 생산자에게 매개변수로 전달됩니다(예를 들어OpenGL ES
) .SurfaceTexture
Surface
텍스처 와 결합 됩니다OpenGL ES
. 즉,OpenGL ES
그리기 명령에 의해 생성된 텍스처에는SurfaceTexture
일반적인 렌더링 출력인 출력이 필요합니다. 여기에는 렌더링 데이터의 생산자와 소비자 연결을 담당하는SurfaceTexture
내부 인스턴스가 포함되어 있습니다 .BufferQueue
정상적인 상황에서BufferQueue
등은 생산자로 사용되고OpenGL ES
다른 사람은TextureView
소비자로 사용됩니다.TextureView
이 클래스는View
와 를 결합하여 텍스처 데이터를 소비하고 재정의된 메서드의 형태로 화면에 표시SurfaceTexture
할 수 있습니다 .SurfaceTexture
View
draw
SurfaceView
이전에 Android API에 등장하여TextureView
사용중인 일반적인 것과 일치View
하지만 하위 계층에서 자체 독립성을 가지며 이것의 렌더링을 별도의 스레드Surface
에서 수행 할 수 있다는 장점이 있습니다 . 컨텍스트 환경을 렌더링할 때 자신만의 환경을 가질 수 있습니다. 이는 이벤트에 대한 기본 스레드의 응답에 영향을 주지 않기 때문에 게임 및 비디오와 같은 일부 성능 관련 응용 프로그램에 매우 유용합니다. 그러나 이것이 기본값이 아니기 때문에 단점도 있습니다. 속성에 의해 표시가 제어되지 않으므로 변환, 확대/축소 등을 할 수 없으며 다른 위치에 배치할 수 없으며 일부 기능이 포함되어 있습니다. 쓸 수 없다.Surface
Surface
View Tree
View
ViewGroup
View
일반적으로 TextureView
유연하지만 성능은 낮고 SurfaceView
약간의 제한이 있지만 성능은 더 효율적입니다. SurfaceView
Flutter UI의 경우 독립적인 전체 화면 인터페이스(기본값)인 경우 먼저 렌더링 출력으로 사용해야 합니다 .
또한 Flutter에는 Android 플랫폼에서만 지원되는 가상 디스플레이라는 가상 TextureLayer
디스플레이 모드가 있습니다.Platform View
위의 내용은 Embedder의 구조를 공간의 관점에서 위에서 아래로, 테이블에서 내부로 분석한 다음 전체 Flutter 시작 프로세스에서 Embedder의 역할과 기능을 시간의 관점에서 분석합니다(참고: 대부분의 다음 콘텐츠는 이 순서를 따르며 먼저 공간 구조를 분석한 다음 타이밍 프로세스를 분석합니다.
준비 단계 시작
여기서는 flutter create
명령을 사용하여 기본적으로 생성된 앱을 Counter
예로 들어 보겠습니다. 시작 후 목록 4-1과 같이 콜백 FlutterApplication
인이 먼저 트리거됩니다.onCreate
// 代码清单4-1 engine/shell/platform/android/io/flutter/app/FlutterApplication.java
public class FlutterApplication extends Application {
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterInjector.instance().flutterLoader().startInitialization(this);// 注意,这里依赖注入式的设计,通过解耦以提升代码的可测试性
}
}
위의 로직이 결국 호출하게 될 메서드는 Listing 4-2에 나와 FlutterLoader
있습니다 startInitialization
.
// 代码清单4-2 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
public void startInitialization(@NonNull Context applicationContext) {
startInitialization(applicationContext, new Settings());
}
public void startInitialization(@NonNull Context applicationContext, @NonNull
Settings settings) {
if (this.settings != null) {
return; } // 防止多次初始化
if (Looper.myLooper() != Looper.getMainLooper()) {
throw ... } // 检查是否在主线程
// 确保使用的是Global Context,如果使用某个Activity的Context,可能导致内存泄漏
final Context appContext = applicationContext.getApplicationContext();
this.settings = settings;
initStartTimestampMillis = SystemClock.uptimeMillis(); // 用于统计启动耗时
flutterApplicationInfo = ApplicationInfoLoader.load(appContext); // 加载Manifest信息
VsyncWaiter.getInstance((WindowManager) appContext // 初始化Vsync监听
.getSystemService(Context.WINDOW_SERVICE)).init(); // 见代码清单4-3
Callable<InitResult> initTask = ...... // 见代码清单4-4
initResultFuture = Executors.newSingleThreadExecutor().submit(initTask);
// 开始执行
}
settings
위의 논리에서 변수가 다중 초기화를 방지하도록 설정되어 있는지 먼저 확인합니다 . Flutter의 초기화는 기본 스레드에서 수행되어야 하기 때문에 여기에는 다중 스레드 검사가 없습니다 . 그렇지 않으면 예외가 발생합니다. 여기서 메인 스레드에서 초기화하는 주된 이유는 Embedder의 주요 책임이 엔진에 UI를 그리기 위한 인터페이스를 제공하는 데 반해 Android의 UI 관련 작업은 메인 스레드에서 수행해야 하기 때문입니다. , 즉 렌더링 파이프라인은 다음 내용에서 찾을 수 있습니다. 의 세 트리 구성은 실제로 Android의 메인 스레드에서 수행되지 않습니다.
다음으로 개체는 나중에 콘텐츠 획득 및 합산을 ApplicationContext
위해 획득됩니다 .여기서 한 가지 세부 사항은 호출을 강제하여 사용 되도록 하는 것 입니다 . 와 같은 구성 요소가 직접 사용되는 경우 호출자에 대한 강력한 참조가 유지되므로 메모리 누수 위험이 있습니다. 다음에 완료될 초기화 , 구체적인 로직은 코드 목록 4-3에 나와 있습니다. 마지막으로 비동기 태스크가 초기화되어 즉시 실행됩니다.구체적인 로직은 Listing 4-4에 나와 있습니다.ApplicationInfo
SystemService
getApplicationContext
Application
Context
Activity
Context
SystemService
VsyncWaiter
Task
// 代码清单4-3 engine/shell/platform/android/io/flutter/view/VsyncWaiter.java
public void init() {
FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
// 见代码清单5-31
float fps = windowManager.getDefaultDisplay().getRefreshRate();
FlutterJNI.setRefreshRateFPS(fps);
}
위 로직의 핵심은 엔진이 능동적으로 호출할 객체를 초기화하고 값을 할당하는 것입니다. 사용 시나리오는 엔진에 렌더링해야 하는 UI 프레임이 있을 때 실행하지 않는 것 FlutterJNI
입니다 . AsyncWaitForVsyncDelegate
즉시, 그러나 Embedder를 통해 모니터를 등록하고 신호가 도착한 후 다음 Vsync 렌더링이 시작될 때까지 기다립니다.
initTask
변수의 특정 내용을 계속 분석해 보겠습니다 .
// 代码清单4-4 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
Callable<InitResult> initTask =
new Callable<InitResult>() {
@Override
public InitResult call() {
ResourceExtractor resourceExtractor = initResources(appContext);
flutterJNI.loadLibrary(); // 加载libflutter.so,触发JNI_OnLoad,见代码清单4-20
Executors.newSingleThreadExecutor().execute(
new Runnable() {
// 异步预初始化字体管理器
@Override public void run() {
flutterJNI.prefetchDefaultFontManager();
}
});
if (resourceExtractor != null) {
// 阻塞当前线程
resourceExtractor.waitForCompletion();
}
return new InitResult(
PathUtils.getFilesDir(appContext),
PathUtils.getCacheDirectory(appContext),
PathUtils.getDataDirectory(appContext));
}
};
ResourceExtractor
위의 논리에서 코드 목록 4-5와 같이 리소스 추출을 위해 개체가 먼저 초기화됩니다 . 둘째, 로드됩니다 libflutter.so
. 즉 Flutter Engine
, 이 메서드는 시스템 콜백을 트리거합니다. 즉 JNI_OnLoad
, 이것은 시작 프로세스 중에 엔진에 의해 트리거되는 첫 번째 로직입니다. ResourceExtractor
마지막으로 개체의 작업이 완료될 때까지 스레드가 차단됩니다 .
// 代码清单4-5 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
private ResourceExtractor initResources(@NonNull Context applicationContext) {
ResourceExtractor resourceExtractor = null;
// 注意,只有Debug/JIT模式下构建产物才需要提取逻辑,Release模式产物形式为libapp.so
// 可直接进行动态链接,详见后面内容分析
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
// 初始化变量
final String dataDirPath = PathUtils.getDataDirectory(applicationContext);
final String packageName = applicationContext.getPackageName();
final PackageManager packageManager = applicationContext.getPackageManager();
final AssetManager assetManager = applicationContext.getResources().getAssets();
resourceExtractor =
new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);
// Flutter SDK和业务代码构建的产物(Kernel)被提取到文件,Flutter Engine负责
resourceExtractor // 映射到内存,assets目录下的文件无法直接映射,所以需要提取
.addResource(fullAssetPathFrom(flutterApplicationInfo.vmSnapshotData))
.addResource(fullAssetPathFrom(flutterApplicationInfo.isolateSnapshotData))
.addResource(fullAssetPathFrom(DEFAULT_KERNEL_BLOB));
resourceExtractor.start(); // 本质上是启动一个AsyncTask
}
return resourceExtractor;
}
위의 로직은 ResourceExtractor
초기화 로직이며 그 주요 기능은 mode 와 mode 의 내부 저장소에 디렉토리에 있는 리소스 파일을 추출하는 Debug
것 입니다. 파일은 메모리 매핑 형태로 로드되지만 본질적으로 압축된 패키지의 일부이며 물리적 경로가 없으므로 관련 리소스의 추출을 완료하고 반환해야 합니다. 엔진의 초기화 논리 이전의 실제 물리적 경로 특정 추출 및 복사 논리도 일부 타임스탬프 확인을 수행하므로 여기에서 자세히 설명하지 않겠습니다. 위 파일 리소스의 사용은 Listing 4-68에서 자세히 분석됩니다.JIT
assets
Kernel
assets
zip
요약하면 FlutterApplication
에서 onCreate
수행되는 주요 작업은 세 가지입니다.
- 후속 엔진이 시스템에 등록할 수 있도록 개체를
FlutterJNI
제공 합니다 . 신호 모니터링은 Flutter 렌더링 파이프라인의 프레임 드로잉 프로세스를 구동합니다.AsyncWaitForVsyncDelegate
Vsync
- 로드
libflutter.so
, 트리거JNI_OnLoad
- 초기화 하여 리소스
ResourceExtractor
추출Asset
FlutterEngine 초기화
Flutter Embedder 의 클래스를 참조
FlutterEngine
하고 Flutter Engine은 Flutter Embedder 및 Flutter Framework 에 상대적인 해당 로직을 참조합니다 .Java
libflutter.so
다음으로, 메인 쓰레드는 FlutterActivity
라이프 사이클 콜백에 들어가게 되는데, 먼저 onCreate
코드 목록 4-6에 구체적인 로직이 나와 있다.
// 代码清单4-6 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme(); // 主题切换
super.onCreate(savedInstanceState);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this); // 创建FlutterEngine
delegate.onRestoreInstanceState(savedInstanceState); // 状态存储入口
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); // 生命周期
configureWindowForTransparency(); // 配置窗口
setContentView(createFlutterView()); // 创建FlutterView,并设为根View,见代码清单4-12
configureStatusBarForFullscreenFlutterExperience(); // 全屏状态栏
}
위의 로직에서 switchLaunchThemeForNormalTheme
메서드는 시작 화면 페이지의 테마에서 메인 페이지의 테마로 전환한 다음 새 Delegate
객체를 생성 FlutterActivity
하고 자체 로직의 대부분을 이 객체에 맡기고 onAttach
메서드는 다음 FlutterEngine
을 완료합니다. 초기화. configureWindowForTransparency
메서드는 투명 테마를 구성하는 역할을 하며 메서드 createFlutterView
를 호출하여 Flutter가 실제로 그리는 루트 View를 반환 합니다 . 이 부분은 나중에 자세히 분석하겠습니다.FlutterActivityAndFragmentDelegate
onCreateView
View
Activity
먼저 onAttach
Listing 4-7과 같이 메서드를 분석합니다.
// 代码清单4-7 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
void onAttach(@NonNull Context context) {
ensureAlive();
if (flutterEngine == null) {
setupFlutterEngine(); // 初始化FlutterEngine,见代码清单4-8
}
if (host.shouldAttachEngineToActivity()) {
// 通知Activity Attach完成
flutterEngine.getActivityControlSurface()
.attachToActivity(this, host.getLifecycle());
}
// PlatformPlugin会提供宿主基础的原生能力,如剪贴板
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
// 为Host提供一个感知FlutterEngine初始化完成的时机
host.configureFlutterEngine(flutterEngine); // 开发者一般在此进行Platform Channel注册等操作
}
FlutterEngine
위의 로직은 개체를 인스턴스화하고 관련 멤버 변수의 구성을 완료하는 것으로 Embedder에서 명시되는 초기화를 담당하며 FlutterEngine
, 나중에 자세히 분석할 것입니다. 그러면 생성이 완료되어 PlatformPlugin
기본 FlutterActivity
구현을 제공합니다. 그런 다음 등록된 attachToActivity
플러그인 이 호스트( )와 바인딩을 완료했음을 ActivityControlSurface
메소드를 통해 인터페이스를 구현한 모든 플러그인에 알립니다 . 마지막으로 메소드를 통해 콜백을 제공하며 일반적으로 개발자는 등록 과 같은 콜백에 관련된 초기화 작업을 완료해야 합니다 .FlutterEngine
Activity
Attach
configureFlutterEngine
FlutterActivity
FlutterEngine
Platform View、Platform Channel
FlutterEngine
다음으로 코드 목록 4-8과 같이 생성 과정, 즉 setupFlutterEngine
메서드의 논리에 대한 심층 분석입니다 .
// 代码清单4-8 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
void setupFlutterEngine() {
String cachedEngineId = host.getCachedEngineId();
if (cachedEngineId != null) {
// 第1步,如果指明使用缓存,则尝试获取
flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
isFlutterEngineFromHost = true;
if (flutterEngine == null) {
throw new IllegalStateException .....} // 找不到缓存,抛出异常
return;
}
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) {
// 第2步,如果Host提供了Engine,则直接使用
isFlutterEngineFromHost = true;
return;
}
flutterEngine = new FlutterEngine( // 第3步,新建一个FlutterEngine实例
host.getContext(),
host.getFlutterShellArgs().toArray(), // 外部参数
/*automaticallyRegisterPlugins=*/ false,
host.shouldRestoreAndSaveState());
isFlutterEngineFromHost = false;
}
FlutterEngine
위의 논리는 초기화를 완료하기 위해 세 가지 경우로 나뉩니다 .
- 첫 번째 경우 제공된 캐시에 대해 해당 캐시
id
를Host
검색하고 그렇지 않은 경우 다음 논리를 계속하는 대신 예외가 발생합니다.id
FlutterEngine
- 두 번째 경우에는 인스턴스를 직접
Host
제공 하면FlutterEngine
직접 사용할 수 있습니다. - 세 번째 경우 위의 두 가지 조건이 충족되지 않으면 새
FlutterEngine
인스턴스가 생성됩니다.
FlutterEngine
처음 두 경우의 구체적인 인스턴스를 제어할 수 있으므로 ( 또는 를 Host
구현하여 ) 해당 필드는 입니다 .getCachedEngineId
provideFlutterEngine
isFlutterEngineFromHost
true
참고: 획득에
CachedEngineId
실패한 시나리오의 경우FlutterEngine
예외가 발생하지 않으면 다음 논리를 계속하십시오.인스턴스가 성공적으로 설정될 수 있음이 보장되지만 사용자의 논리는FlutterEngine
캐시된 개체FlutterEngine
의 일부 고유한 기능 에 따라 달라질 수 있습니다. 결론을 덮기 위해 예외를 던지면 실제로 위험(Engine
사용자가 기대하는 것과 실제 수익 사이의 불일치)이 후속 문제 해결의 어려움을 증가시키므로 여기에서 예외가 직접 발생합니다.
일반적으로 기본 로직은 새 FlutterEngine
인스턴스를 생성하는 것이며 결국 트리거될 생성자는 Listing 4-9에 나와 있습니다.
// 代码清单4-9 engine/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
public FlutterEngine( ...... ) {
AssetManager assetManager;
try {
// 优先使用一个独立的AssetManager
assetManager = context.createPackageContext(context.getPackageName(), 0).
getAssets();
} catch (NameNotFoundException e) {
assetManager = context.getAssets(); // 若失败则使用当前的
} // 第1步,DartExecutor创建,负责Embedder和Framework的通信
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
// 设置FlutterJNI中处理Framework消息的PlatformMessageHandler
this.dartExecutor.onAttachedToJNI();
DeferredComponentManager deferredComponentManager =
FlutterInjector.instance().deferredComponentManager();
// 第2步,提供给Framework的平台能力的封装
accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
// SKIP
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
this.flutterJNI = flutterJNI;
if (flutterLoader == null) {
flutterLoader = FlutterInjector.instance().flutterLoader();
}
if (!flutterJNI.isAttached()) {
// 第3步,FlutterEngine的启动逻辑
flutterLoader.startInitialization(context.getApplicationContext()); // 见代码清单4-2
flutterLoader.ensureInitializationComplete(context, dartVmArgs); // 见代码清单4-10
} // 第4步,设置FlutterJNI的关键成员字段
flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
flutterJNI.setPlatformViewsController(platformViewsController);
flutterJNI.setLocalizationPlugin(localizationPlugin);
flutterJNI.setDeferredComponentManager(
FlutterInjector.instance().deferredComponentManager());
if (!flutterJNI.isAttached()) {
// 至此,Embedder侧完全准备好和Engine交互
attachToJni(); // 建立Embedder和Engine的连接,见代码清单4-24
} // 第5步,Engine绘制相关能力的初始化
this.renderer = new FlutterRenderer(flutterJNI); // 该类代表了Engine绘制相关的能力
this.platformViewsController = platformViewsController;
this.platformViewsController.onAttachedToJNI();
this.pluginRegistry = new FlutterEngineConnectionRegistry(
context.getApplicationContext(), this, flutterLoader);
if (automaticallyRegisterPlugins) {
registerPlugins(); // 自动完成插件的注册
}
}
위 논리에서,
- 1단계는 생성을 완료
DartExecutor
하고 이 개체는 Embedder와 Framework 간의 통신을 담당합니다. - 두 번째 단계는 많은 플랫폼 기능 캡슐화 클래스의 초기화를 완료할 것이며
Platform Channel
, 대부분은 의 보조 캡슐화이므로 여기에서 자세히 설명하지 않겠습니다. ensureInitializationComplete
세 번째 단계에서는 메서드를 통해 초기화 매개변수의 구성이 완료됩니다FlutterEngine
.. Embedder가 아직 초기화를 완료FlutterEngine
하지 않았기 때문에 이때 대부분의 주요 클래스가 아직 생성되지 않았음을 유의하십시오.FlutterJNI
- 네 번째 단계는
FlutterJNI
개체의 해당 멤버를 초기화하고attachJni
메서드를 호출하여 Embedder와 Engine의 바인딩을 완료하는 것으로 엔진의 대부분의 로직은attachJni
메서드에 의해 트리거되며 이에 대해서는 나중에 자세히 분석할 것입니다. FlutterRenderer
5단계는 초기화 및Platform View
관련 초기화 작업을 완료합니다Plugin
.
FlutterEngine
다음으로 코드리스트 4-10과 같이 초기화 작업을 계속해서 분석한다 .
// 代码清单4-10 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
public void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
if (initialized) {
return; } // 已经完成
if (Looper.myLooper() != Looper.getMainLooper()) {
...... }
if (settings == null) {
...... } // 没有调用startInitialization,退出
try {
InitResult result = initResultFuture.get(); // 阻塞至代码清单4-4完成
List<String> shellArgs = new ArrayList<>(); // 开始拼接各种启动参数
// SKIP
if (args != null) {
Collections.addAll(shellArgs, args); } // 透传外部参数
String kernelPath = null;
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
// SKIP Kernel Snapshot的名称及位置
} else {
// Release模式,so文件的名称,默认为libapp.so
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME
+ "=" + flutterApplicationInfo.aotSharedLibraryName);
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME // 兜底逻辑,传入完整路径
+ "=" + flutterApplicationInfo.nativeLibraryDir
+ File.separator + flutterApplicationInfo.aotSharedLibraryName);
}
// SKIP 其他各种参数
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
flutterJNI.init(applicationContext, shellArgs.toArray(new String[0]), // 见代码清单4-23
kernelPath, result.appStoragePath, result.engineCachesPath,
initTimeMillis); // Embedder初始化至此的耗时
initialized = true; // FlutterLoader 初始化完成
} catch (Exception e) {
...... }
}
위의 논리에서 일련의 매개 변수는 주로 JNI
를 호출하여 초기화되고 전달됩니다 Engine
. 그 중 주목해야 할 것은 Framework
관련 Dart
비즈니스 코드의 탑재다. 모드 에서는 Debug
3개의 필드가 로드되며 해당 값과 기능은 다음과 같습니다.
-
SNAPSHOT_ASSET_PATH_KEY
(snapshot-asset-path
): 기본값은flutter_assets
다음 두 리소스의 경로를 나타냅니다. -
VM_SNAPSHOT_DATA_KEY
(vm-snapshot-data
): 기본값은 데이터 세그먼트와 코드 세그먼트vm_snapshot_data
입니다 .vm isolate
-
ISOLATE_SNAPSHOT_DATA_KEY
(isolate-snapshot-data
): 기본값은 기본 (비즈니스 코드 및 해당 종속성 ) 의 데이터 세그먼트 및 코드 세그먼트를isolate_snapshot_data
나타냅니다 .isolate
Framework
Release
모드 에서는 로드만 되며 AOT_SHARED_LIBRARY_NAME
기본값은 and 의 libapp.so
합과 같지만 전자는 모드의 vm_snapshot_data
산물 이고 후자의 두 가지는 모드의 파일 직렬화 산물 입니다 . 형식 파일이므로 코드 목록 4-11과 같이 관련 도구를 통해 내용을 볼 수 있습니다.isolate_snapshot_data
Release
AOT
Debug
Kernel
libapp.so
elf
목록 4-11 app.so
파일의 내부 구조
$ greadelf -s --wide libapp.so
Symbol table '.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00001000 12 FUNC GLOBAL DEFAULT 1 _kDartBSSData
2: 00002000 9616 FUNC GLOBAL DEFAULT 2 _kDartVmSnapshotInstructions
3: 00005000 24400 FUNC GLOBAL DEFAULT 3 _kDartVmSnapshotData
4: 0000b000 0x1ed9d8 FUNC GLOBAL DEFAULT 4 _kDartIsolateSnapshotInstructions
5: 001f9000 0x175860 FUNC GLOBAL DEFAULT 5 _kDartIsolateSnapshotData
위의 정보 중 _kDartIsolateSnapshotInstructions
대부분의 논리 정보를 포함하고 있으며 _kDartIsolateSnapshotData
대부분의 데이터 정보를 포함하고 있습니다.
일반적으로 FlutterEngine
초기화 프로세스 중에 메소드와 메소드 FlutterJNI
라는 두 가지 주요 엔진 호출이 트리거됩니다 .nativeInit
nativeAttach
요약하다
안에,
setupFlutterEngine()의 논리:
- 캐시를 사용하도록 지정하면 캐시가 FlutterEngine을 가져오고 가져올 수 없으면 예외가 발생합니다.
- Host Host에서 제공하는 FlutterEngine을 구하여 직접 사용
- 그렇지 않으면 FlutterEngine 인스턴스를 만듭니다.
FlutterEngine 생성자의 논리:
- Dart 실행기 만들기
- Flutter Framework용 플랫폼 기능을 제공하는 패키지
- FlutterEngine의 초기화 매개변수 구성
- FlutterJNI 초기화
- FlutterRenderer 및 기타 렌더링 기능 관련 초기화
FlutterView 초기화
FlutterEngine
초기화가 완료된 후 발생하는 초기화는 Listing 4-12에 나와 있습니다 FlutterView
.
// 代码清单4-12 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ensureAlive();
if (host.getRenderMode() == RenderMode.surface) {
// 第1步,判断Host所提供的RenderMode
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView( // 见代码清单4-13
host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);
// 为Host预留一个接口,用于在FlutterSurfaceView创建完成后提供自定义配置入口
host.onFlutterSurfaceViewCreated(flutterSurfaceView);
flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
} else {
...... } // SKIP FlutterTextureView逻辑类似
// 第2步,间接为Host提供首帧渲染/停止渲染的回调
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
flutterSplashView = new FlutterSplashView(host.getContext()); // 第3步,闪屏相关逻辑
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
flutterSplashView.setId(View.generateViewId());
} else {
flutterSplashView.setId(486947586);
} // 开始渲染首帧,见代码清单4-15
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provide
SplashScreen());
// 注意,FlutterEngine和FlutterView建立连接,这是后面能够渲染的基础
flutterView.attachToFlutterEngine(flutterEngine); // 第4步,见代码清单4-17
return flutterSplashView;
}
위의 논리는 대략 4단계로 나눌 수 있습니다.
- 첫 번째 단계
Host
에서 제공되는 판단은RenderMode
일반적으로 로 기본 설정되어 있으므로Surface
인스턴스FlutterSurfaceView
가 초기화되고 매개 변수로 초기화됩니다FlutterView
. 이 프로세스의 핵심 논리는 코드 목록 4-13 및 코드 목록 4-14에 나와 있습니다. - 두 번째 단계 는 첫 번째 프레임을 렌더링하고 렌더링을 중지하기 위한 콜백을 제공하는 데 사용되는 객체를
FlutterView
등록하는 것이며 관련 콜 체인을 자체적으로 분석할 수 있습니다.flutterUiDisplayListener
Host
- Step 3 스플래시 화면의 초기화를 완료하고,
displayFlutterViewWithSplash
스플래시 화면에서 첫 번째 프레임으로 전환하는 메소드를 호출하면 해당 로직은 코드리스트 4-15와 같다. - 4단계에서 메서드를
FlutterView
호출하여attachToFlutterEngine
최종 초기화 작업을 완료하고 관련 로직을 코드 목록 4-17에 나타내었다.
다음으로 위에서 언급한 4단계를 차례로 분석한다. 첫 번째는 Listing 4-13과 같이 FlutterSurfaceView
결국 메서드를 호출하는 의 초기화 입니다.init
// 代码清单4-13 engine/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
private void init() {
if (renderTransparently) {
// 透明
getHolder().setFormat(PixelFormat.TRANSPARENT);
setZOrderOnTop(true);
} // 监听Embedder中Surface的状态变化,在回调中调用Engine对应的处理逻辑
getHolder().addCallback(surfaceCallback);
setAlpha(0.0f); // 保持透明,防止黑屏
}
private final SurfaceHolder.Callback surfaceCallback =
new SurfaceHolder.Callback() {
@Override // Embedder中,在Surface完成创建之后触发
public void surfaceCreated(@NonNull SurfaceHolder holder) {
isSurfaceAvailableForRendering = true;
if (isAttachedToFlutterRenderer) {
connectSurfaceToRenderer(); // 通过本回调通知Engine开始渲染UI,见代码清单4-18
}
}
@Override public void surfaceChanged( ...... ) {
...... }
@Override public void surfaceDestroyed( ...... ) {
...... }
};
위의 논리는 생성이 완료된 후 바인딩을 트리거하는 Surface
데 사용되는 현재 콜백에 콜백을 추가합니다 . 여기서 필드는 바인딩할지 여부를 나타내고 기본값은 이며 설정 타이밍은 다음에 도입됩니다. 코드 목록 4-18. 이 방법도 나중에 자세히 분석할 것입니다.Surface
FlutterRender
isAttachedToFlutterRenderer
RederSurface
FlutterRender
false
true
connectSurfaceToRenderer
위는 FlutterSurfaceView
의 초기화 논리입니다. 그런 다음 초기화 논리를 분석하면 코드 목록 4-14와 같이 FlutterView
핵심 논리도 자체 메서드에 있습니다 .init
// 代码清单4-14 flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java
private void init() {
if (flutterSurfaceView != null) {
// 默认情况下用于一个完整独立的Flutter UI
addView(flutterSurfaceView);
} else if (flutterTextureView != null) {
// Flutter UI作为原生UI的一部分时
addView(flutterTextureView);
} else {
// Flutter UI作为宿主,Platform View作为其一部分
addView(flutterImageView);
}
setFocusable(true);
setFocusableInTouchMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS);
}
}
위는 의 하위 클래스 인 FlutterView
의 초기화 논리이며 , 의 특정 구현 중 하나를 자체 하위 클래스로 선택 하고 포커스 및 자동 채우기와 같은 속성을 설정합니다. 초기화가 완료되면 Listing 4-15와 같이 프리젠테이션 로직이 실행됩니다.FlutterView
FrameLayout
RenderSurface
View
FlutterView
// 代码清单4-15 engine/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
public void displayFlutterViewWithSplash(@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
if (this.flutterView != null) {
// 清理之前的FlutterView
this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
removeView(this.flutterView);
}
if (splashScreenView != null) {
// 清理之前的闪屏
removeView(splashScreenView);
}
this.flutterView = flutterView;
addView(flutterView); // 显示FlutterView
this.splashScreen = splashScreen;
if (splashScreen != null) {
// 存在闪屏资源
if (isSplashScreenNeededNow()) {
// 立即显示闪屏,并通过Listener触发切换
splashScreenView = splashScreen.createSplashView(getContext(),
splashScreenState);
addView(this.splashScreenView); // 加入View Tree
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); // 见代码清单4-16
} else if (isSplashScreenTransitionNeededNow()) {
// 立即进入:从闪屏切换到FlutterView状态
splashScreenView = splashScreen.createSplashView(getContext(),
splashScreenState);
addView(splashScreenView); // 显示闪屏
transitionToFlutter(); // 然后立即开始切换到FlutterView
} else if (!flutterView.isAttachedToFlutterEngine()) {
// 见代码清单4-16
// 若当前FlutterView尚未与FlutterEngine绑定,则监听其绑定状态,完成绑定时触发回调
flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
}
} // 若不存在闪屏资源,则一直显示FlutterView
}
private void transitionToFlutter() {
transitioningIsolateId = flutterView.getAttachedFlutterEngine().
getDartExecutor().getIsolateServiceId();
splashScreen.transitionToFlutter(onTransitionComplete);
}
위의 논리에서 FlutterView
UI가 결국 표시된다는 의미이며 View
, splashScreenView
이는 첫 번째 프레임이 렌더링되기 전에 표시되는 스플래시 화면 UI를 의미하며 이 역시 에서 상속됩니다 FrameLayout
. displayFlutterViewWithSplash
메서드는 먼저 둘 다 정리하고 두 번째는 FlutterView
레이아웃에 추가합니다. 스플래시 화면 리소스가 있는 경우 다음 세 가지 상황으로 나누어 처리합니다.
-
현재 스플래시 화면을 표시해야 함:
splashScreenView
레이아웃에 추가되고flutterUiDisplayListener
첫 번째 프레임이 렌더링된 후 스위치를 트리거하도록 등록됩니다. 특정 로직은 Listing 4-16에 표시되어 있습니다. -
현재 시작 화면에서 첫 번째 프레임으로 전환해야 함:
splashScreenView
레이아웃에 추가되고 첫 번째 프레임 애니메이션으로 즉시 전환됩니다. -
FlutterView
Not yet bound(attached
) toFlutterEngine
: 메서드 호출 시 트리거링을 위한 리스너를FlutterView
등록하기 위해 Listing 4-17에서 이 부분에 대해 자세히 설명합니다.flutterEngineAttachmentListener
FlutterView
attachToFlutterEngine
flutterUiDisplayListener
합계 의 특정 논리는 flutterEngineAttachmentListener
코드 목록 4-16에 나와 있습니다.
// 代码清单4-16 engine/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
@NonNull
private final FlutterView.FlutterEngineAttachmentListener
flutterEngineAttachmentListener = new FlutterView.FlutterEngineAttachmentListener() {
@Override // FlutterView和FlutterEngine绑定成功后触发
public void onFlutterEngineAttachedToFlutterView(@NonNull FlutterEngine engine) {
flutterView.removeFlutterEngineAttachmentListener(this);
displayFlutterViewWithSplash(flutterView, splashScreen); // 见代码清单4-15
}
@Override
public void onFlutterEngineDetachedFromFlutterView() {
}
};
@NonNull
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override // FlutterView开始渲染时触发
public void onFlutterUiDisplayed() {
if (splashScreen != null) {
transitionToFlutter(); // Flutter UI开始渲染时从闪屏开始变换
}
}
@Override public void onFlutterUiNoLongerDisplayed() {
}
};
위의 논리에서, onFlutterUiDisplayed
즉 첫 번째 프레임의 렌더링이 완료되면 transitionToFlutter
위에서 언급한 것처럼 콜백이 스플래시 화면에서 첫 번째 프레임으로 전환 애니메이션을 트리거합니다. onFlutterEngineAttachedToFlutterView
즉, 바인딩이 완료되면 FlutterView
이 콜백에서 로직이 다시 트리거됩니다.FlutterEngine
displayFlutterViewWithSplash
다음 분석 FlutterView
및 FlutterEngine
바인딩 프로세스의 시작점은 코드 목록 4-12의 네 번째 단계, 즉 attachToFlutterEngine
메서드이며 그 논리는 코드 목록 4-17에 나와 있습니다.
// 代码清单4-17 engine/shell/platform/android/io/flutter/embedding/android/FlutterView.java
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
Log.v(TAG, "Attaching to a FlutterEngine: " + flutterEngine);
if (isAttachedToFlutterEngine()) {
if (flutterEngine == this.flutterEngine) {
return; }
detachFromFlutterEngine(); // 解除绑定
}
this.flutterEngine = flutterEngine;
// 获取FlutterEngine对于FlutterEngine绘制能力的封装:FlutterRender
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
isFlutterUiDisplayed = flutterRenderer.isDisplayingFlutterUi();
// FlutterView和FlutterEngine进行绑定的本质是RenderSurface与FlutterRender的attach
renderSurface.attachToRenderer(flutterRenderer); // 见代码清单4-18
flutterRenderer.addIsDisplayingFlutterUiListener(flutterUiDisplayListener); // 对外通知
// SKIP 创建和初始化各种与UI相关的桥接功能类
sendUserSettingsToFlutter(); // 发送与屏幕相关的配置给Engine
localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter(); // 发送窗口大小、设备分辨率等信息
flutterEngine.getPlatformViewsController().attachToView(this);
for (FlutterEngineAttachmentListener listener : flutterEngineAttachmentListeners) {
listener.onFlutterEngineAttachedToFlutterView(flutterEngine); // 通知绑定完成
}
if (isFlutterUiDisplayed) {
// 如果已经在展示Flutter UI,则直接触发回调
flutterUiDisplayListener.onFlutterUiDisplayed();
}
}
코드 목록 4-12에서 볼 수 있듯이 attachToFlutterEngine
메서드는 displayFlutterViewWithSplash
논리 뒤에 있습니다. 따라서 스플래시 화면이 있을 때 일반적으로 목록 4-15의 세 번째 경우가 먼저 트리거된 다음 첫 번째 경우가 트리거됩니다. attachToFlutterEngine
메서드 자체의 논리 분석을 시작하겠습니다 .
위의 로직은 주로 FlutterView
와 의 바인딩으로 FlutterEngine
3단계로 나뉩니다.
- 첫 번째 단계는
FlutterView
각 구성원의 할당입니다. - 두 번째 단계는 코드 목록 4-18과 같이
RenderSurface
및 의 바인딩이며 나중에 자세히 분석할 것입니다.FlutterRender
Platform View
세 번째 단계는 다양한 플랫폼 기능의 초기화를 완료sendUserSettingsToFlutter
하고 패키징을sendViewportMetricsToFlutter
통해 플랫폼 속성과 같은 정보를 서버로FlutterJNI
보낼 것이므로Engine
여기서는 반복하지 않겠습니다.
마지막으로 바인딩이 완료되었다는 알림이 트리거되며, 이는 이전 콘텐츠 및 기타 작업의 스플래시 화면 로직을 트리거하는 데 사용됩니다.
RenderSurface
및 의 바인딩은 아래에서 자세히 분석됩니다 FlutterRender
. 목록 4-18에 표시된 것처럼 이것을 FlutterSurfaceView
예로 들어 보겠습니다.
// 代码清单4-18 engine/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
Log.v(TAG, "Attaching to FlutterRenderer.");
if (this.flutterRenderer != null) {
this.flutterRenderer.stopRenderingToSurface();
this.flutterRenderer.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener);
}
this.flutterRenderer = flutterRenderer;
isAttachedToFlutterRenderer = true; // 开始监听FlutterRender何时渲染出首帧
this.flutterRenderer.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
if (isSurfaceAvailableForRendering) {
// Surface已经可用,一般这里为false
connectSurfaceToRenderer(); // 通常由代码清单4-13的surfaceCallback触发
}
}
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override // Engine渲染首帧时通过FlutterJNI的onFirstFrame方法触发
public void onFlutterUiDisplayed() {
setAlpha(1.0f); // Engine开始完全接管当前Surface的渲染
if (flutterRenderer != null) {
flutterRenderer.removeIsDisplayingFlutterUiListener(this);
}
}
@Override public void onFlutterUiNoLongerDisplayed() {
}
};
private void connectSurfaceToRenderer() {
if (flutterRenderer == null || getHolder() == null) {
throw new IllegalStateException(" ...... ");
} // 告知Engine可以开始渲染
flutterRenderer.startRenderingToSurface(getHolder().getSurface());
}
FlutterView
위의 로직 attachToFlutterEngine
은 메소드와 유사 FlutterSurfaceView
하지만 FlutterView
실제로 렌더링을 담당하는 클래스는 기본 렌더링 기능의 추상 캡슐화 FlutterRender
이므로 FlutterEngine
여기의 로직은 주로 렌더링과 관련됩니다.
Flutter 렌더링에는 두 가지 중요한 타이밍이 있습니다. 하나는 Surface
Embedder 생성이 완료되는 시점(Embedder에서 결정)이고 다른 하나는 Engine의 첫 번째 프레임 데이터가 준비되는 시점( Engine
Embedder에서 결정)입니다. flutterUiDisplayListener
엔진의 첫 번째 프레임 데이터가 준비되는 타이밍을 모니터링하고 이 시점에서 투명도를 0으로 설정하여 1
현재 렌더링을 완전히 인계받을 것임을 나타냅니다 Surface
. 이 방법은 일반적으로 여기에서 준비가 완료되었는지 여부를 isSurfaceAvailableForRendering
판단하는 데 사용됩니다 . 일반적으로 생성 콜백에서 트리거됩니다 . 즉, Listing 4-13에 표시된 로직에서 자체 로직은 결국 Embedder가 준비되었고 첫 번째 프레임을 렌더링할 수 있음을 엔진 측에 알리는 메서드를 호출합니다.Surface
false
connectSurfaceToRenderer
Surface
nativeSurfaceCreated
Surface
위의 로직 대부분은 실제로 콜백을 기반으로 트리거되며 다음과 같은 두 개의 핵심 노드가 있습니다.
-
(1) Embedder가 생성되고
Surface
생성 후 콜백에서 렌더링을 시작하라는 지시를 받습니다Engine
. -
(2) 렌더링 시작을
Engine
기준으로Surface
첫 번째 프레임이 완료되면FlutterJNI
Embedder에 첫 번째 프레임 데이터가 그려졌다고 알립니다Surface
.
Engine
UI의 그리기는 실제로 Framework
드라이버에 의해 구동되므로 위의 논리는 선형적이지 않습니다. 즉, Embedder는 Surface
생성 및 알림만 담당하고 렌더링이 완료되는 시기는 에 따라 달라지며 Engine
렌더링 Engine
은 에 따라 달라 집니다 Framework
. Framework
시작은 실제로 Embedder Driven에 의해 시작됩니다.
요약하다
FlutterActivityAndFragmentDelegate
() 메소드 onCreateView
에서 수행되는 주요 작업 :
FlutterSurfaceView
인스턴스의 인스턴스를 초기화 하고 이를 매개변수로 사용하여 생성FlutterView
(FrameLayout
하위 클래스)flutterUiDisplayListener
콜백을 등록하여Host
첫 번째 프레임 렌더링을 제공하고 렌더링을 중지합니다. 첫 번째 프레임 렌더링 후 콜백은 주로 스플래시 화면에서 첫 번째 프레임으로 전환 애니메이션을 트리거하는 데 사용됩니다.- 스플래시 화면 관련 로직 초기화
attachToFlutterEngine
, 바인딩FlutterView
및 및FlutterEngine
(RenderSurface
및 바인딩 포함FlutterRender
, 다양한 플랫폼 기능,Platform View
초기화)
아래에서 분석을 시작하십시오 Framework
.
프레임워크 시작
지금까지 Embedder가 초기화되고 운영 환경이 준비되어 로직을 실행할 수 있게 되었는데 트리거 FlutterEngine
타이밍 은 Listing 4-19와 같이 메소드의 마지막에 호출될 메소드 에 있습니다.FlutterView
Framework
Framework
FlutterActivity
onStart
Delegate
doInitialFlutterViewRun
// 代码清单4-19 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
private void doInitialFlutterViewRun() {
if (host.getCachedEngineId() != null) {
return; } // 预加载的Engine无法通过本方法启动
if (flutterEngine.getDartExecutor().isExecutingDart()) {
return; } // 已经在运行
String initialRoute = host.getInitialRoute(); // 获取初始路由
if (initialRoute == null) {
// 8.7节将详细介绍Flutter的路由体系
initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
if (initialRoute == null) {
initialRoute = DEFAULT_INITIAL_ROUTE; } // 默认路由
flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
String appBundlePathOverride = host.getAppBundlePath();
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
appBundlePathOverride = FlutterInjector.instance().flutterLoader().
findAppBundlePath();
} // 自定义assets资源地址,默认为flutter_assets
DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint(
appBundlePathOverride, host.getDartEntrypointFunctionName());
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
}
위의 논리에서 해당 검사 작업이 먼저 수행됩니다. appBundlePathOverride
이 필드는 개발자에게 리소스 로딩 경로를 사용자 정의할 수 있는 인터페이스를 제공하는 데 사용되며 기본값은 입니다 flutter_assets
. 위의 논리는 결국 개체를 구성 하고 메서드 를 DartExecutor.DartEntrypoint
호출하여 시작을 완료합니다 .FlutterJNI
nativeRunBundleAndSnapshotFromLibrary
Framework
지금까지 Embedder의 전체 시작 프로세스는 다음과 같습니다.
엔진 엔트리 마무리
Embedder의 시작 프로세스는 그림 4-2에 나와 있습니다.
Embedder는 시작 과정에서 API 콜백 또는 JNI
활성 호출을 통해 여러 곳에서 Engine의 로직을 호출했으며, 그 중 Engine 초기화에 중요한 5가지 로직을 정리하면 다음과 같습니다.
-
JNI_OnLoad
:System.loadLibrary
에서 트리거되면Engine
인터페이스 및Embedder
인터페이스 바인딩 등록이 완료됩니다. -
nativeInit
: 능동적으로 호출되며, 관련 리소스의 준비가 완료된 후 필요한 초기화 파라미터를 서버에FlutterLoader
전달하는 역할을 주로 담당합니다 .Engine
-
nativeAttach
: EmbedderFlutterEngine
개체는 필요한 초기화가 완료된 후 호출되며, 이는 엔진에서 키 클래스의 초기화 논리를 트리거합니다. -
nativeSurfaceCreated
: Java 측에서Surface
객체( ) 준비가 완료된 후 호출되며 그리기와 관련된 로직의 초기화를SurfaceView
트리거합니다 .Engine
-
nativeRunBundleAndSnapshotFromLibrary
: 합계가 초기화된FlutterEngine
후 생명주기에서 능동적으로 호출되어 관련 초기화 작업을 트리거하는 데 사용됩니다.FlutterView
Host
onStart
Framework
엔진 키 클래스 및 시작 프로세스
그림 4-3에서 Engine
하단 계층은 와 상호 작용하기 위한 인터페이스를 PlatformViewAndroidJNI
추상화 하고 상위 계층은 와 상호 작용하기 위한 인터페이스를 추상화합니다 . Flutter에서는 및 를 연결하는 "훌륭한 경로" 덕분에 기반 및 기반 이 서로 협력할 수 있습니다 . 예를 들어, 신호의 등록 및 응답, 항공기의 작동은 모두 이 경로를 통해 완료되고 조정 됩니다 .Embedder
PlatformConfiguration
Framework
Dart VM
Framework
Java VM
Embedder
PlatformViewAndroidJNI
PlatformConfiguration
Vsync
Platform Channel
Framework
Embedder
의 동작 원리를 완전히 이해하기 위해서는 Engine
그림 4-3에 구현된 클래스 구조를 두 가지 관점에서 이해할 필요가 있다.
-
첫 번째 각도는 Embedder에서 시작하여 Framework로 전달하는 것 입니다 . 그림 4-3 에서
Embedder
항목은PlatformViewAndroidJNI
, 에shell_holder
의해 보유되고 필드에 의해 보유 됩니다 . 예 허브, 논리를 필드별로 전달할 수 있고 논리를 필드별로 전달할 수 있으며 논리를 필드별로 전달할 수 있습니다 . 그것은 이름에서 알 수 있듯이 런타임 에 컨트롤러 인 필드에 의해 유지되는 의 추상 캡슐화 입니다. 구체적으로, 그것은 fields 에 의해 유지되고 , fields 에 의해 유지되며 , 후자의 부모 클래스는 fields 에 의해 유지됩니다 . 클래스와 직접적으로 상호 작용하는 클래스 이며 , 위의 경로를 통해 에 로직을 전달할 수 있습니다 .AndroidShellHolder
AndroidShellHolder
shell_
Shell
Shell
Engine
platform_view_
PlatformView
rasterizer_
Rasterizer
engine_
Engine
Engine
Flutter Framework
Flutter Engine
Engine
runtime_controller_
RuntimeController
RuntimeController
Flutter Framework
RuntimeController
vm_
DartVM
root_isolate_
DartIsolate
UIDartState
platform_configuration_
PlatformConfiguration
PlatformConfiguration
Flutter Framework
Embedder
Framework
-
두 번째 각도는 Framework에서 시작하여 Embedder로 전달하는 것 입니다 . 여기에는
Flutter Engine
프록시 패턴(주로 그림 4-3의 클래스Delegate
또는Client
끝 부분)의 독창적인 사용이 포함됩니다. 구체적으로, 클래스가 정확히 상위 클래스 인 필드에 의해 보유되고 따라서 프록시에 의해 보유되는Flutter Framework
논리가 에 전달되며 , 이 프록시는 에 의해 보유되고 이 프록시는 에 의해 보유됩니다 . 허브 이며 fields 가 보유하고 의 하위 클래스는 와 상호 작용하는 클래스 인 fields 가 보유 합니다. 위의 경로를 통해 로직을 에 전달할 수 있습니다 .PlatformConfiguration
client_
PlatformConfigurationClient
RuntimeController
PlatformConfiguration
RuntimeController
RuntimeController
RuntimeDelegate
Engine
Engine
Engine::Delegate
Shell
Shell
Flutter Engine
platform_view_
PlatformView
PlatformView
PlatformViewAndroid
jni_facade_
PlatformViewAndroidJNI
Embedder
Framework
Embedder
사실 위의 내용은 스레드, 스레드, 스레드 및 스레드 를 완료하여 전달할 수 있는 스레드 UI
간의 상호 작용일 뿐입니다.Platform
Flutter Engine
Shell
UI
Platform
I/O
Raster
Android 플랫폼의 경우 AndroidShellHolder
일반적으로 Platform
스레드, UI
스레드, Raster
스레드 및 I/O
스레드의 네 가지 스레드가 생성되고 관리됩니다.
Platform
스레드는Android
메인 스레드이며Engine
이 스레드에서 특별한 작업을 수행하지 않으며 주로 모니터링Embedder
과 같은 상호 작용 및 통신Platform Channel
에 사용됩니다Vsync
.UI
쓰레드는Framework
로직이 위치한 쓰레드이며,UI
관련 작업(트리 3개 업데이트, 애니메이션 등)은 모두UI
쓰레드에서 완료됩니다.Raster
쓰레드는UI
자신이 생성한 데이터를 더 처리하여GPU
화면에 전달하는 역할을 담당하는데, 싱글 쓰레드를 설정하는 이유는 성능 향상을 위해서다.I/O
스레드는 이미지 디코딩 및 리소스 액세스와 같은 무거운 차단 작업을 처리하는 역할을 합니다.
일반적으로 Platform
스레드와 UI
스레드는 개발자와 가장 밀접하게 관련되어 있습니다. Platform Channel
in은 Embedder
메인 스레드( Platform
스레드)를 통해 전송되어야 하며, 그렇지 않으면 실패합니다.
Engine
스레드의 생성 및 관리는 호스트(예 :)에 의해 결정된다는 점에 유의해야 합니다 AndroidShellHolder
. Engine
그 안에 있는 클래스의 경우 Engine
4개의 객체만 보유합니다 .이 4개의 객체가 동일한 스레드에서 오는지 또는 4개의 독립적인 스레드에서 오는지에 TaskRunner
따라 클래스 모르고 있습니다.TaskRunner
Engine
Surface 키 클래스 및 시작 프로세스
Flutter 렌더링 시스템 소개
모바일 렌더링 시스템은 그림 4-5와 같이 7개의 계층으로 나눌 수 있습니다.
그림 4-5에서는 위에서 아래로 7개의 레이어로 나뉩니다.
- 첫 번째 레이어는 애플리케이션 레이어, 즉 사용자가 상호 작용할 수 있도록 개발자가 코딩한 다양한 UI 인터페이스로, 다른 플랫폼에서도 사용자는 거의 동일한 사용 경험을 얻을 수 있습니다.
- 두 번째 계층은 프레임워크 계층으로 Android의 View, Flutter의 Widget 단위와 같이 개발자가 UI를 개발하는 기본 단위입니다.
- 세 번째 계층은 프레임워크 계층의 초석이 되는 고급 그리기 인터페이스(예: Skia)입니다.UI 개발도 고급 그리기 인터페이스를 기반으로 할 수 있지만 자세한 내용은 다루어야 합니다.프레임워크 계층을 고려할 수 있습니다. 고급 드로잉 인터페이스의 시맨틱 캡슐화로.
- 네 번째 계층은 기본 그리기 인터페이스입니다. 여기서 기본 계층은 Skia와 같은 고급 그리기 인터페이스와 관련이 있습니다. 하부 드로잉 인터페이스는 그래픽 분야에 가까운 API를 제공하며 드로잉과 관련된 최소 단위의 명령어와 속성도 제공한다. 기본 그래픽 인터페이스의 경우 가장 큰 문제는 다른 하드웨어의 호환성, 즉 다른 제조업체에서 제공하는 하드웨어 인터페이스가 다를 수 있다는 것입니다.소프트웨어 계층으로서 기본 그래픽 인터페이스를 처리해야 합니다.
- 다섯 번째 계층은 브리징 계층으로 EGL의 정식 명칭은 임베디드 그래픽 라이브러리(Embedded Graphics Library)로 제조사에서 제공하는 API로 하드웨어 인터페이스를 직접 구동할 때 발생할 수 있는 호환성 문제를 해결하기 위해 통합 규격을 따른다.
- 여섯 번째 계층은 GPU로, CPU보다 더 많은 컴퓨팅 유닛과 더 적은 스토리지 유닛을 가지고 있어 동시 컴퓨팅 시나리오에 매우 적합합니다. 일반적으로 CPU를 통해 그리는 것을 소프트 드로잉이라고 하고 GPU를 통해 그리는 것을 하드 드로잉이라고 합니다.하드 드로잉의 성능이 더 좋기 때문에 대부분의 최신 모바일 장치는 하드 드로잉을 사용합니다.
- 일곱 번째 레이어는 렌더링 영역이며 가장 일반적인 것은 물리적 디스플레이이며 오프 스크린 버퍼, 가상 디스플레이(Android)와 같은 일부 가상 렌더링 영역도 있습니다. 응용 프로그램 계층의 UI는 최종적으로 렌더링 영역에 도달하고 UI 표시를 완료하기 전에 실제로 중간 5개 계층에서 처리되어야 합니다.
즉, 프레임워크 레이어는 기본 그리기 인터페이스를 직접 호출할 수도 있고 기본 그리기 인터페이스도 렌더링 영역을 직접 작동할 수 있지만 위 레이어링은 대부분의 UI 프레임워크가 건축을 따르고 추구합니다.
Flutter Engine의 경우 그리기와 관련된 주요 클래스는 그림 4-6에 나와 있습니다.
그림 4-6에서 렌더링을 담당하는 키 클래스 PlatformViewAndroid
이며 Flutter Engine
두 개의 키 개체, 즉 android_context_
필드에 의해 보유되고 필드에 의해 보유 AndroidContext
됩니다 .android_surface_
AndroidSurface
AndroidContext
드로잉과 관련된 컨텍스트를 제공하며, OpenGL
모드(안드로이드 플랫폼의 기본 모드, 이하 동일)의 구체적인 하위 클래스는 입니다 AndroidContextGL
. , 등과 AndroidContextGL
같은 객체는 렌더링을 위한 컨텍스트를 제공합니다.EGLContext
EGLConfig
Flutter Engine
AndroidSurface
렌더링된 출력을 제공하고 프레임 데이터의 소비자입니다. OpenGL
모드의 경우 특정 하위 클래스는 이며 이 클래스는 필드 및 필드를 AndroidSurfaceGL
통해 두 개의 객체를 보유합니다 .onscreen_surface_
offscreen_surface_
AndroidEGLSurface
AndroidEGLSurface
실제 렌더링 논리를 담당할 , 등과 같은 키 인스턴스를 보유 하는 API의 캡슐화 Flutter Engine
입니다 .EGL
EGLSurface
EGLDisplay
EGLContext
또한 는 인스턴스 생성을 위한 필드를 통해 클래스를 보유하는 에서 생성된 것과 동등한 표현 AndroidNativeWindow
입니다 .Embedder
Surface
Engine
AndroidSurfaceGL
native_window_
onscreen_surface_
위의 클래스는 모두 Platform
스레드 에 있으며 Flutter Engine
스레드의 실제 렌더링은 Raster
스레드에서 발생합니다.이러한 이유로 Raster
스레드의 스레드는 필드를 통해 하나를 보유 GPUSurfaceGL
하고 구체적인 구현은 스레드 가 운영 하다delegate_
GPUSurfaceGLDelegate
AndroidSurfaceGL
Raster
AndroidEGLSurface
Flutter Engine
API 시작 프로세스 에서 API는 Surface
여러 번 호출됩니다 .각 코드 조각에 분산되어 있지만 호출 순서는 여전히 그림 4-7에 표시된 프로세스 사양을 엄격하게 따릅니다.EGL
Surface
그림 4-7은 준비 단계부터 UI 최종 화면까지 Flutter Engine에서 경험하는 7가지 주요 API 호출을 보여주고 EGL
있으며 구체적인 기능은 그림에 표시되어 있습니다.
Surface 관련 초기화 프로세스는 다음과 같습니다.
Dart Runtime 키 클래스 및 시작 프로세스
Dart Runtime과 관련된 주요 클래스와 관계는 그림 4-9에 나와 있습니다.
그림 4-9에서 객체를 보유함으로써 실제 객체를 보유하는 Shell
클래스는 이미 우리에게 매우 친숙한 클래스입니다 . 그러나 전체적인 관점에서 볼 때 실제로 유지되는 것은 실제로 .DartVMRef
Dart VM
Dart VM
RuntimeController
먼저 그림 4-9의 오른쪽 절반을 분석하면 생성이 DartVM
생성되기 전에 실행됩니다 DartVMData
. 이 클래스에는 두 개의 키 필드가 포함되어 vm_snapshot_
있으며 이 두 필드는 마스터 에서 시작하는 데 필요한 데이터를 isolate_snapshot_
기록합니다 . 의 필드 와 필드는 각각 현재 시작에 필요한 데이터 시퀀스와 명령어 시퀀스를 보유합니다. 의 기본 표현은 실제로 파일 메모리 맵( , 모드) 또는 동적 링크 라이브러리의 기호 맵( , 모드) 과 같은 의 하위 클래스 입니다.vm-service Isolate
Isolate
Snapshot
DartSnapshot
DartSnapshot
data_
instructions_
DartIsolate
DartSnapshot
Mapping
FileMapping
Debug
SymbolMapping
Release
그런 다음 그림 4-9의 왼쪽 절반을 분석하면 필드 RuntimeController
에 의해 유지되고 개체를 기반으로 초기화 할 수 있으며 에서 상속된 것은 API 인터페이스 의 추상 표현 입니다 . 일반적으로 시작은 해당 ( 파일 또는 파일)을 로드하고 실행하는 것입니다.isolate_snapshot_
DartSnapshot
DartIsolate
DartIsolate
UIDartState
Engine
Dart_Isolate
Dart VM
Isolate
Snapshot
so
Kernel