Flutter 源码剖析(一)

前言

作技术,只有弄懂了原理,才能遇事不慌,手中无码,心中有码。这篇文章主要研究Flutter 在安卓平台上的启动流程源码。前端

启动流程

入口Activity

当咱们建立一个Flutter app工程时,打开android目录下的源码,会发现有一个MainActivity继承自FlutterActivity,整个MainActivity很是简单,只在onCreate下加了一行代码GeneratedPluginRegistrant.registerWith(this),那么FlutterActivity又是何方神圣呢?FlutterActivity的源码在Flutter SDK的jar包中,想要研究Flutter源码,第一件事就是须要下载一套SDK源码,咱们能够在GitHub上下载 engine源码java

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 省略部分源码,删除注释后代码以下android

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处理。c++

engine\src\flutter\shell\platform\android\io\flutter\app\FlutterActivityDelegate.javagit

@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?github

一开始咱们就知道ViewFactoryFlutterActivity实现的,这里createFlutterView方法实现也在FlutterActivity里,但这个方法始终返回空,再看createFlutterNativeView方法,仍然是返回空shell

@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++层初始化这些信息

建立 splash view

接下来,还有一个地方值得一说,在建立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"> <!-- Show a splash screen on the activity. Automatically removed when Flutter draws its first frame --> <item name="android:windowBackground">@drawable/launch_background</item> </style>
复制代码

看到,windowBackground实际上是指定为launch_background,在drawable文件夹下找到它

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item> <bitmap android:gravity="center" android:src="@mipmap/launch_image" /> </item> -->
</layer-list>
复制代码

这里默认背景确实被设置成了白色,而且还友好的给出了一个设置图片的示例,将注释的代码打开,就能够设置本身的splash view

Application启动

咱们知道安卓app启动时,首先调用的是ApplicationonCreate,如今来看一看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 引擎的,主要作了几件事,

  • 初始化配置
  • 获取是否预编译模式
  • 初始化并提取资源文件
  • 加载libflutter.so

Java层与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
复制代码

运行Dart代码

经过反编译Flutter生成的apk,咱们很清楚的知道,Dart代码编译后的产物,包括相关的资源文件,实际上都是打包到安卓的assets目录下的,那么Dart代码是在哪加载执行的呢?

FlutterActivityDelegateonCreate方法的最后有以下代码

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 );
复制代码

调用过程以下,点击大图

在这里插入图片描述

总结

  • FlutterActivityDelegateonCreate流程图
    在这里插入图片描述
    对象调用关系图,点击大图
    在这里插入图片描述

FlutterMain.ensureInitializationComplete方法调用

在这里插入图片描述

FlutterNativeView

欢迎关注个人公众号:编程之路从0到1

编程之路从0到1
相关文章
相关标签/搜索