Flutter 노트 | Flutter 핵심 원칙(6) Embedder 시작 프로세스(Android)

여기에 이미지 설명 삽입
EmbedderFlutter가 네이티브 플랫폼에 연결하기 위한 핵심 요소로 전체 Flutter 아키텍처의 하단에 위치하여 Engine생성, 관리, 소멸을 담당하고 EngineUI를 그리기 위한 인터페이스도 제공합니다. 최하층? 이 기사에서는 자세히 분석합니다.

Embedder 키 클래스 분석

Embedder의 시작 프로세스를 공식적으로 분석하기 전에 특정 프로세스를 분석할 때 Embedder의 논리와 목적을 이해할 수 있도록 Embedder의 주요 클래스와 그 관계를 명확히 해야 합니다. Embedder 레이어의 키 클래스 구조는 그림 4-1에 나와 있습니다.

그림 4-1 Embedder 레이어의 키 클래스 구조

Embedder에서 FlutterActivityFlutterFragment개발자들이 가장 많이 접하는 클래스 예를 들어 Counter기본적으로 생성되는 App에서는 에서 MainActivity상속됨 ​​이 FlutterActivity두 클래스의 부모 클래스 Activity이며 Fragment안드로이드에서 원스크린 UI를 구현하는데 사용되는 공통 단위 따라서 이 두 클래스의 책임은 Flutter Engine이 그린 UI를 표시하는 것입니다. 그렇다면 이 두 클래스 아래에 숨겨진 논리는 무엇입니까?

FlutterActivity와 의 대부분의 논리와 책임은 FlutterFragment동일하므로 동일한 논리 처리를 위한 객체(이하 참조)를 공동으로 보유하고 , 자체 기능 추상화를 위한 인터페이스를 공동으로 구현 FlutterActivityAndFragmentDelegate합니다 .DelegateHost

Delegate두 가지 주요 클래스를 보유하고 있습니다. FlutterEngine전자 는 Embedder에서 Flutter Engine ( )의 호출 및 관리를 FlutterView담당 하고 후자는 Flutter Engine에서 UI 데이터의 화면 표시 를 담당합니다 . Engine 의 기능도 사용하게 되므로 보류도 합니다 .libflutter.soFlutterViewFlutterEngine

또한 특정 구현 FlutterView도 개최됩니다 . 이름에서 알 수 있듯이 Flutter UI를 렌더링하기 위한 인터페이스이며 세 가지 구현이 있습니다.RenderSurfaceRenderSurface

  • FlutterSurfaceViewAndroid 기반 SurfaceView구현은 최고의 성능을 갖지만 Android의 View Hierarchy에는 없으며 일반적으로 전체 페이지 Flutter UI 표시에 사용되며 기본적으로 선호됩니다.
  • FlutterTextureViewAndroid TextureView구현을 기반으로 성능은 전자보다 좋지 않지만 사용자 경험은 Android의 일반 사용자 경험에 더 가깝습니다 View. 이는 Flutter가 기본 UI에 포함된 일부 시나리오 에 더 적합합니다.
  • FlutterImageView일반적으로 플랫폼 보기가 존재하는 시나리오에서 사용됩니다.

RenderSurfaceFlutterRender엔진의 관련 그리기 기능은 개체를 통해 호출되며 FlutterRender엔진의 그리기 기능은 Embedder에서 사용하기 편리한 추상화 및 캡슐화됩니다. 또한 엔진에서 Dart Runtime 관련 로직이 호출 FlutterEngine됩니다 . DartExecutor이 두 클래스에서 RenderSurfaceUI 관련 업무와 DartExecutor로직 관련 업무를 담당하지만 결국 Engine의 특정 Native 메서드를 호출하게 되므로 FlutterJNI대부분의 Embedder(Java 코드)와 Engine(C++ 코드) 상호 호출 인터페이스.

다음 개념은 다음 콘텐츠에 여러 번 나타납니다. Surface、SurfaceTexture、SurfaceView、TextureView. 혼동을 피하기 위해 여기에 통일된 설명이 제공됩니다.

  • Surface렌더링 버퍼의 핸들(Handle)을 나타내는 비교적 추상적인 개념으로, 일반적으로 SurfaceTexture、MediaRecorder렌더링 데이터의 소비자가 생성하고(예를 들어) 렌더링 데이터의 생산자에게 매개변수로 전달됩니다(예를 들어 OpenGL ES) .
  • SurfaceTextureSurface텍스처 와 결합 됩니다 OpenGL ES. 즉, OpenGL ES그리기 명령에 의해 생성된 텍스처에는 SurfaceTexture일반적인 렌더링 출력인 출력이 필요합니다. 여기에는 렌더링 데이터의 생산자와 소비자 연결을 담당하는 SurfaceTexture내부 인스턴스가 포함되어 있습니다 . BufferQueue정상적인 상황에서 BufferQueue등은 생산자로 사용되고 OpenGL ES다른 사람은 TextureView소비자로 사용됩니다.
  • TextureView이 클래스는 View와 를 결합하여 텍스처 데이터를 소비하고 재정의된 메서드의 형태로 화면에 표시 SurfaceTexture할 수 있습니다 .SurfaceTextureViewdraw
  • SurfaceView이전에 Android API에 등장하여 TextureView사용중인 일반적인 것과 일치 View하지만 하위 계층에서 자체 독립성을 가지며 이것의 렌더링을 별도의 스레드Surface 에서 수행 할 수 있다는 장점이 있습니다 . 컨텍스트 환경을 렌더링할 때 자신만의 환경을 가질 수 있습니다. 이는 이벤트에 대한 기본 스레드의 응답에 영향을 주지 않기 때문에 게임 및 비디오와 같은 일부 성능 관련 응용 프로그램에 매우 유용합니다. 그러나 이것이 기본값이 아니기 때문에 단점도 있습니다. 속성에 의해 표시가 제어되지 않으므로 변환, 확대/축소 등을 할 수 없으며 다른 위치에 배치할 수 없으며 일부 기능이 포함되어 있습니다. 쓸 수 없다.SurfaceSurfaceView TreeViewViewGroupView

일반적으로 TextureView유연하지만 성능은 낮고 SurfaceView약간의 제한이 있지만 성능은 더 효율적입니다. SurfaceViewFlutter 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에 나와 있습니다.ApplicationInfoSystemServicegetApplicationContextApplicationContextActivityContextSystemServiceVsyncWaiterTask

// 代码清单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에서 자세히 분석됩니다.JITassetsKernelassetszip

요약하면 FlutterApplication에서 onCreate수행되는 주요 작업은 세 가지입니다.

  1. 후속 엔진이 시스템에 등록할 수 있도록 개체를 FlutterJNI제공 합니다 . 신호 모니터링은 Flutter 렌더링 파이프라인의 프레임 드로잉 프로세스를 구동합니다.AsyncWaitForVsyncDelegateVsync
  2. 로드 libflutter.so, 트리거JNI_OnLoad
  3. 초기화 하여 리소스 ResourceExtractor추출Asset

FlutterEngine 초기화

Flutter Embedder 의 클래스를 참조 FlutterEngine하고 Flutter Engine은 Flutter EmbedderFlutter Framework 에 상대적인 해당 로직을 참조합니다 .Javalibflutter.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를 반환 합니다 . 이 부분은 나중에 자세히 분석하겠습니다.FlutterActivityAndFragmentDelegateonCreateViewViewActivity

먼저 onAttachListing 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메소드를 통해 인터페이스를 구현한 모든 플러그인에 알립니다 . 마지막으로 메소드를 통해 콜백을 제공하며 일반적으로 개발자는 등록 과 같은 콜백에 관련된 초기화 작업을 완료해야 합니다 .FlutterEngineActivityAttachconfigureFlutterEngineFlutterActivityFlutterEnginePlatform 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위의 논리는 초기화를 완료하기 위해 세 가지 경우로 나뉩니다 .

  • 첫 번째 경우 제공된 캐시에 대해 해당 캐시 idHost검색하고 그렇지 않은 경우 다음 논리를 계속하는 대신 예외가 발생합니다.idFlutterEngine
  • 두 번째 경우에는 인스턴스를 직접 Host제공 하면 FlutterEngine직접 사용할 수 있습니다.
  • 세 번째 경우 위의 두 가지 조건이 충족되지 않으면 새 FlutterEngine인스턴스가 생성됩니다.

FlutterEngine처음 두 경우의 구체적인 인스턴스를 제어할 수 있으므로 ( 또는 를 Host구현하여 ) 해당 필드는 입니다 .getCachedEngineIdprovideFlutterEngineisFlutterEngineFromHosttrue

참고: 획득에 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메서드에 의해 트리거되며 이에 대해서는 나중에 자세히 분석할 것입니다.
  • FlutterRenderer5단계는 초기화 및 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비즈니스 코드의 탑재다. 모드 에서는 Debug3개의 필드가 로드되며 해당 값과 기능은 다음과 같습니다.

  • 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나타냅니다 .isolateFramework

Release모드 에서는 로드만 되며 AOT_SHARED_LIBRARY_NAME기본값은 and 의 libapp.so합과 같지만 전자는 모드의 vm_snapshot_data산물 이고 후자의 두 가지는 모드의 파일 직렬화 산물 입니다 . 형식 파일이므로 코드 목록 4-11과 같이 관련 도구를 통해 내용을 볼 수 있습니다.isolate_snapshot_dataReleaseAOTDebugKernellibapp.soelf

목록 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라는 두 가지 주요 엔진 호출이 트리거됩니다 .nativeInitnativeAttach

요약하다

여기에 이미지 설명 삽입
안에,

setupFlutterEngine()의 논리:

  1. 캐시를 사용하도록 지정하면 캐시가 FlutterEngine을 가져오고 가져올 수 없으면 예외가 발생합니다.
  2. Host Host에서 제공하는 FlutterEngine을 구하여 직접 사용
  3. 그렇지 않으면 FlutterEngine 인스턴스를 만듭니다.

FlutterEngine 생성자의 논리:

  1. Dart 실행기 만들기
  2. Flutter Framework용 플랫폼 기능을 제공하는 패키지
  3. FlutterEngine의 초기화 매개변수 구성
  4. FlutterJNI 초기화
  5. 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등록하는 것이며 관련 콜 체인을 자체적으로 분석할 수 있습니다.flutterUiDisplayListenerHost
  • 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. 이 방법도 나중에 자세히 분석할 것입니다.SurfaceFlutterRenderisAttachedToFlutterRendererRederSurfaceFlutterRenderfalsetrueconnectSurfaceToRenderer

위는 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와 같이 프리젠테이션 로직이 실행됩니다.FlutterViewFrameLayoutRenderSurfaceViewFlutterView

// 代码清单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);
}

위의 논리에서 FlutterViewUI가 결국 표시된다는 의미이며 View, splashScreenView이는 첫 번째 프레임이 렌더링되기 전에 표시되는 스플래시 화면 UI를 의미하며 이 역시 에서 상속됩니다 FrameLayout. displayFlutterViewWithSplash메서드는 먼저 둘 다 정리하고 두 번째는 FlutterView레이아웃에 추가합니다. 스플래시 화면 리소스가 있는 경우 다음 세 가지 상황으로 나누어 처리합니다.

  • 현재 스플래시 화면을 표시해야 함: splashScreenView레이아웃에 추가되고 flutterUiDisplayListener첫 번째 프레임이 렌더링된 후 스위치를 트리거하도록 등록됩니다. 특정 로직은 Listing 4-16에 표시되어 있습니다.

  • 현재 시작 화면에서 첫 번째 프레임으로 전환해야 함: splashScreenView레이아웃에 추가되고 첫 번째 프레임 애니메이션으로 즉시 전환됩니다.

  • FlutterViewNot yet bound( attached) to FlutterEngine: 메서드 호출 시 트리거링을 위한 리스너를 FlutterView등록하기 위해 Listing 4-17에서 이 부분에 대해 자세히 설명합니다.flutterEngineAttachmentListenerFlutterViewattachToFlutterEngine

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콜백에서 로직이 다시 트리거됩니다.FlutterEnginedisplayFlutterViewWithSplash

다음 분석 FlutterViewFlutterEngine바인딩 프로세스의 시작점은 코드 목록 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와 의 바인딩으로 FlutterEngine3단계로 나뉩니다.

  • 첫 번째 단계는 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 렌더링에는 두 가지 중요한 타이밍이 있습니다. 하나는 SurfaceEmbedder 생성이 완료되는 시점(Embedder에서 결정)이고 다른 하나는 Engine의 첫 번째 프레임 데이터가 준비되는 시점( EngineEmbedder에서 결정)입니다. flutterUiDisplayListener엔진의 첫 번째 프레임 데이터가 준비되는 타이밍을 모니터링하고 이 시점에서 투명도를 0으로 설정하여 1현재 렌더링을 완전히 인계받을 것임을 나타냅니다 Surface. 이 방법은 일반적으로 여기에서 준비가 완료되었는지 여부를 isSurfaceAvailableForRendering판단하는 데 사용됩니다 . 일반적으로 생성 콜백에서 트리거됩니다 . 즉, Listing 4-13에 표시된 로직에서 자체 로직은 결국 Embedder가 준비되었고 첫 번째 프레임을 렌더링할 수 있음을 엔진 측에 알리는 메서드를 호출합니다.SurfacefalseconnectSurfaceToRendererSurfacenativeSurfaceCreatedSurface

위의 로직 대부분은 실제로 콜백을 기반으로 트리거되며 다음과 같은 두 개의 핵심 노드가 있습니다.

  • (1) Embedder가 생성되고 Surface생성 후 콜백에서 렌더링을 시작하라는 지시를 받습니다 Engine.

  • (2) 렌더링 시작을 Engine기준으로 Surface첫 번째 프레임이 완료되면 FlutterJNIEmbedder에 첫 번째 프레임 데이터가 그려졌다고 알립니다 Surface.

EngineUI의 그리기는 실제로 Framework드라이버에 의해 구동되므로 위의 논리는 선형적이지 않습니다. 즉, Embedder는 Surface생성 및 알림만 담당하고 렌더링이 완료되는 시기는 에 따라 달라지며 Engine렌더링 Engine은 에 따라 달라 집니다 Framework. Framework시작은 실제로 Embedder Driven에 의해 시작됩니다.

요약하다

FlutterActivityAndFragmentDelegate() 메소드 onCreateView에서 수행되는 주요 작업 :

  1. FlutterSurfaceView인스턴스의 인스턴스를 초기화 하고 이를 매개변수로 사용하여 생성 FlutterView( FrameLayout하위 클래스)
  2. flutterUiDisplayListener콜백을 등록하여 Host첫 번째 프레임 렌더링을 제공하고 렌더링을 중지합니다. 첫 번째 프레임 렌더링 후 콜백은 주로 스플래시 화면에서 첫 번째 프레임으로 전환 애니메이션을 트리거하는 데 사용됩니다.
  3. 스플래시 화면 관련 로직 초기화
  4. attachToFlutterEngine, 바인딩 FlutterView및 및 FlutterEngine( RenderSurface및 바인딩 포함 FlutterRender, 다양한 플랫폼 기능, Platform View초기화)

아래에서 분석을 시작하십시오 Framework.

프레임워크 시작

지금까지 Embedder가 초기화되고 운영 환경이 준비되어 로직을 실행할 수 있게 되었는데 트리거 FlutterEngine타이밍 Listing 4-19와 같이 메소드의 마지막에 호출될 메소드 에 있습니다.FlutterViewFrameworkFrameworkFlutterActivityonStartDelegatedoInitialFlutterViewRun

// 代码清单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호출하여 시작을 완료합니다 .FlutterJNInativeRunBundleAndSnapshotFromLibraryFramework

지금까지 Embedder의 전체 시작 프로세스는 다음과 같습니다.

여기에 이미지 설명 삽입

엔진 엔트리 마무리

Embedder의 시작 프로세스는 그림 4-2에 나와 있습니다.

여기에 이미지 설명 삽입

Embedder는 시작 과정에서 API 콜백 또는 JNI활성 호출을 통해 여러 곳에서 Engine의 로직을 호출했으며, 그 중 Engine 초기화에 중요한 5가지 로직을 정리하면 다음과 같습니다.

  • JNI_OnLoad: System.loadLibrary에서 트리거되면 Engine인터페이스 및 Embedder인터페이스 바인딩 등록이 완료됩니다.

  • nativeInit: 능동적으로 호출되며, 관련 리소스의 준비가 완료된 후 필요한 초기화 파라미터를 서버에 FlutterLoader전달하는 역할을 주로 담당합니다 .Engine

  • nativeAttach: Embedder FlutterEngine개체는 필요한 초기화가 완료된 후 호출되며, 이는 엔진에서 키 클래스의 초기화 논리를 트리거합니다.

  • nativeSurfaceCreated: Java 측에서 Surface객체( ) 준비가 완료된 후 호출되며 그리기와 관련된 로직의 초기화를 SurfaceView트리거합니다 .Engine

  • nativeRunBundleAndSnapshotFromLibrary: 합계가 초기화된 FlutterEngine생명주기에서 능동적으로 호출되어 관련 초기화 작업을 트리거하는 데 사용됩니다.FlutterViewHostonStartFramework

엔진 키 클래스 및 시작 프로세스

여기에 이미지 설명 삽입

그림 4-3에서 Engine하단 계층은 와 상호 작용하기 위한 인터페이스를 PlatformViewAndroidJNI추상화 하고 상위 계층은 와 상호 작용하기 위한 인터페이스를 추상화합니다 . Flutter에서는 및 를 연결하는 "훌륭한 경로" 덕분에 기반 기반 서로 협력할 수 있습니다 . 예를 들어, 신호의 등록 및 응답, 항공기의 작동은 모두 이 경로를 통해 완료되고 조정 됩니다 .EmbedderPlatformConfigurationFrameworkDart VMFrameworkJava VMEmbedderPlatformViewAndroidJNIPlatformConfigurationVsyncPlatform ChannelFrameworkEmbedder

의 동작 원리를 완전히 이해하기 위해서는 Engine그림 4-3에 구현된 클래스 구조를 두 가지 관점에서 이해할 필요가 있다.

  • 첫 번째 각도는 Embedder에서 시작하여 Framework로 전달하는 것 입니다 . 그림 4-3 에서 Embedder항목은 PlatformViewAndroidJNI, 에 shell_holder의해 보유되고 필드에 의해 보유 됩니다 . 예 허브, 논리를 필드별로 전달할 수 있고 논리를 필드별로 전달할 수 있으며 논리를 필드별로 전달할 수 있습니다 . 그것은 이름에서 알 수 있듯이 런타임 에 컨트롤러 필드에 의해 유지되는 의 추상 캡슐화 입니다. 구체적으로, 그것은 fields 에 의해 유지되고 , fields 에 의해 유지되며 , 후자의 부모 클래스는 fields 에 의해 유지됩니다 . 클래스와 직접적으로 상호 작용하는 클래스 이며 , 위의 경로를 통해 에 로직을 전달할 수 있습니다 .AndroidShellHolderAndroidShellHoldershell_ShellShellEngineplatform_view_PlatformViewrasterizer_Rasterizerengine_EngineEngineFlutter FrameworkFlutter EngineEngineruntime_controller_RuntimeControllerRuntimeControllerFlutter FrameworkRuntimeControllervm_DartVMroot_isolate_DartIsolateUIDartStateplatform_configuration_PlatformConfigurationPlatformConfigurationFlutter FrameworkEmbedderFramework

  • 두 번째 각도는 Framework에서 시작하여 Embedder로 전달하는 것 입니다 . 여기에는 Flutter Engine프록시 패턴(주로 그림 4-3의 클래스 Delegate또는 Client끝 부분)의 독창적인 사용이 포함됩니다. 구체적으로, 클래스가 정확히 상위 클래스 필드에 의해 보유되고 따라서 프록시에 의해 보유되는 Flutter Framework논리가 에 전달되며 , 이 프록시는 에 의해 보유되고 프록시는 에 의해 보유됩니다 . 허브 이며 fields 가 보유하고 의 하위 클래스는 상호 작용하는 클래스 fields 가 보유 합니다. 위의 경로를 통해 로직을 에 전달할 수 있습니다 .PlatformConfigurationclient_PlatformConfigurationClientRuntimeControllerPlatformConfigurationRuntimeControllerRuntimeControllerRuntimeDelegateEngineEngineEngine::DelegateShellShellFlutter Engineplatform_view_PlatformViewPlatformViewPlatformViewAndroidjni_facade_PlatformViewAndroidJNIEmbedderFrameworkEmbedder

사실 위의 내용은 스레드, 스레드, 스레드 및 스레드 를 완료하여 전달할 수 있는 스레드 UI간의 상호 작용일 뿐입니다.PlatformFlutter EngineShellUIPlatformI/ORaster

여기에 이미지 설명 삽입

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 Channelin은 Embedder메인 스레드( Platform스레드)를 통해 전송되어야 하며, 그렇지 않으면 실패합니다.

Engine스레드의 생성 및 관리는 호스트(예 :)에 의해 결정된다는 점에 유의해야 합니다 AndroidShellHolder. Engine그 안에 있는 클래스의 경우 Engine4개의 객체만 보유합니다 .이 4개의 객체가 동일한 스레드에서 오는지 또는 4개의 독립적인 스레드에서 오는지에 TaskRunner따라 클래스 모르고 있습니다.TaskRunnerEngine

Surface 키 클래스 및 시작 프로세스

Flutter 렌더링 시스템 소개

모바일 렌더링 시스템은 그림 4-5와 같이 7개의 계층으로 나눌 수 있습니다.
그림 4-5 모바일 드로잉 시스템

그림 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 표면 키 클래스

그림 4-6에서 렌더링을 담당하는 키 클래스 PlatformViewAndroid이며 Flutter Engine두 개의 키 개체, 즉 android_context_필드에 의해 보유되고 필드에 의해 보유 AndroidContext됩니다 .android_surface_AndroidSurface

AndroidContext드로잉과 관련된 컨텍스트를 제공하며, OpenGL모드(안드로이드 플랫폼의 기본 모드, 이하 동일)의 구체적인 하위 클래스는 입니다 AndroidContextGL. , 등과 AndroidContextGL같은 객체는 렌더링을 위한 컨텍스트를 제공합니다.EGLContextEGLConfigFlutter Engine

AndroidSurface렌더링된 출력을 제공하고 프레임 데이터의 소비자입니다. OpenGL모드의 경우 특정 하위 클래스는 이며 이 클래스는 필드 및 필드를 AndroidSurfaceGL통해 두 개의 객체를 보유합니다 .onscreen_surface_offscreen_surface_AndroidEGLSurface

AndroidEGLSurface실제 렌더링 논리를 담당할 , 등과 같은 키 인스턴스를 보유 하는 API의 캡슐화 Flutter Engine입니다 .EGLEGLSurfaceEGLDisplayEGLContext

또한 는 인스턴스 생성을 위한 필드를 통해 클래스를 보유하는 에서 생성된 것과 동등한 표현 AndroidNativeWindow입니다 .EmbedderSurfaceEngineAndroidSurfaceGLnative_window_onscreen_surface_

위의 클래스는 모두 Platform스레드 에 있으며 Flutter Engine스레드의 실제 렌더링은 Raster스레드에서 발생합니다.이러한 이유로 Raster스레드의 스레드는 필드를 통해 하나를 보유 GPUSurfaceGL하고 구체적인 구현은 스레드 가 운영 하다delegate_GPUSurfaceGLDelegateAndroidSurfaceGLRasterAndroidEGLSurface

Flutter EngineAPI 시작 프로세스 에서 API는 Surface여러 번 호출됩니다 .각 코드 조각에 분산되어 있지만 호출 순서는 여전히 그림 4-7에 표시된 프로세스 사양을 엄격하게 따릅니다.EGL

그림 4-7 EGL 상태 전환

Surface그림 4-7은 준비 단계부터 UI 최종 화면까지 Flutter Engine에서 경험하는 7가지 주요 API 호출을 보여주고 EGL있으며 구체적인 기능은 그림에 표시되어 있습니다.

Surface 관련 초기화 프로세스는 다음과 같습니다.

여기에 이미지 설명 삽입

Dart Runtime 키 클래스 및 시작 프로세스

Dart Runtime과 관련된 주요 클래스와 관계는 그림 4-9에 나와 있습니다.

여기에 이미지 설명 삽입

그림 4-9에서 객체를 보유함으로써 실제 객체를 보유하는 Shell클래스는 이미 우리에게 매우 친숙한 클래스입니다 . 그러나 전체적인 관점에서 볼 때 실제로 유지되는 것은 실제로 .DartVMRefDart VMDart VMRuntimeController

먼저 그림 4-9의 오른쪽 절반을 분석하면 생성이 DartVM생성되기 전에 실행됩니다 DartVMData. 이 클래스에는 두 개의 키 필드가 포함되어 vm_snapshot_있으며 이 두 필드는 마스터 에서 시작하는 데 필요한 데이터를 isolate_snapshot_기록합니다 . 의 필드 필드는 각각 현재 시작에 필요한 데이터 시퀀스와 명령어 시퀀스를 보유합니다. 의 기본 표현은 실제로 파일 메모리 맵( , 모드) 또는 동적 링크 라이브러리의 기호 맵( , 모드) 과 같은 의 하위 클래스 입니다.vm-service IsolateIsolateSnapshotDartSnapshotDartSnapshotdata_instructions_DartIsolateDartSnapshotMappingFileMappingDebugSymbolMappingRelease

그런 다음 그림 4-9의 왼쪽 절반을 분석하면 필드 RuntimeController에 의해 유지되고 개체를 기반으로 초기화 할 수 있으며 에서 상속된 것은 API 인터페이스 의 추상 표현 입니다 . 일반적으로 시작은 해당 ( 파일 또는 파일)을 로드하고 실행하는 것입니다.isolate_snapshot_DartSnapshotDartIsolateDartIsolateUIDartStateEngineDart_IsolateDart VMIsolateSnapshotsoKernel

여기에 이미지 설명 삽입


참조: "Flutter 커널 소스 코드 분석"

추천

출처blog.csdn.net/lyabc123456/article/details/130959425