ticktakclockの日記

技術ポエムを綴ったりします。GitHub idも同じです (@ticktakclock)

Flutter Androidのソースコードを読む(FlutterApplication編)

こんにちは、tkyです。

今日はFlutter Androidソースコードを読んで、調べた軌跡を残したいと思います。

本記事は確定的は情報はなく、tkyが右往左往しながら調べたことと感じたことをまとめたもので、仕組みを解説するような内容ではないことを予めここに明記致します。

また、リアルタイムで勉強しながら記事を書いているので誤りがある場合、随時訂正していきます。

(というか正直難しい・・・いろいろ読み進めてみて、dartdart VMのことを知らないと理解できないのではないかと感じました。)

今回読んでいくのはこれ

flutter.jar

Android Studioにはjarのclassファイルに定義ジャンプすることができるので⌘ + クリック(winの場合は ctrl + クリック)で読めますが、 ここで読めるのはあくまでjavaで書かれた領域だけなのでネイティブコード(C/C++の領域のことを指します)も見ることを考慮して flutter/engineもcloneしておきます。

github.com

目次

  • まずはAndroidManifest.xmlから
  • そもそもflutter.jarはどこにあるのか?
  • FlutterApplication
  • FlutterMain
    • startInitialization
      • VM snapshotとは
      • instrって何
      • isolateって何
      • app.soとは
      • app.flxって何

FlutterActivityについては別途調べて見ようと思います。

まずはAndroidManifest.xmlから

とにもかくにもこれから見るのが最初の足がかりとしては良いかと思います。 どうやらFlutterApplicationMainActivityによって構成されていることがわかりました。 MainActivityFlutterActivityを継承しているようですね。

AndroidManifest.xml抜粋

    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="kasikari_memo"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

MainActivity.kt抜粋

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
  }
}

ここで新しいワードが3つ出てきています。上から順番に調べていきますが、本記事ではFlutterApplicationだけフォーカスします。 - FlutterApplication - FlutterActivity - GeneratedPluginRegistrant

このクラス郡がflutter.jar内に格納されている、ということですね。

そもそもflutter.jarはどこにあるのか?

調べたところ、こちらにあるようでした。android-armの部分はアーキテクチャによってパスが変わりそうです。

Androidの場合:
/Users/${username}/flutter/bin/cache/artifacts/engine/android-arm

iOSの場合:
/Users/t.takenaka/flutter/bin/cache/artifacts/engine/ios

試しにJD-GUIツール(jarファイル内の.classファイルの可視化ツール)で見るとたしかに、ちゃんとFlutterActivityなどの存在が確認できました。 f:id:ticktakclock:20190105225406p:plain

FlutterApplication

onCreateの処理はこの様になっていました。何かの初期化処理をしているようです。

    @CallSuper
    public void onCreate() {
        super.onCreate();
        FlutterMain.startInitialization(this);
    }

FlutterMain

ここから一気に難しくなります。startInitialization()はstaticメソッドですね。

startInitialization()

このメソッドではざっくり4つの処理をしていました。3種類の初期化メソッドと、libflutter.soファイルのロード処理のようです。

            initConfig(applicationContext);
            initAot(applicationContext);
            initResources(applicationContext);
            System.loadLibrary("flutter");

この辺のinit処理で1つ1つコードを見ていくと、理解を超えそうでしたので、

initConfig()内に記述されていた、要所要所の単語を調べていく形で理解していこうと思います。

  • vm_snapshot_data
  • vm_snapshot_instr
  • isolate_snapshot_data
  • isolate_snapshot_instr
  • app.so
  • app.flx

libflutter.soとは

flutter engineの以下のネイティブコードのライブラリですね。BUILD.gnmakefileになります。 FlutterJNI.javaJava側の定義、 platform_view_android_jni.hC++側の定義です。

$ ls shell/platform/android/io/flutter/embedding/engine/
FlutterEngine.java      FlutterJNI.java         dart                    renderer

$ls shell/platform/android/
AndroidManifest.xml                     android_surface.cc                      io
BUILD.gn                                android_surface.h                       library_loader.cc
android_context_gl.cc                   android_surface_gl.cc                   platform_message_response_android.cc
android_context_gl.h                    android_surface_gl.h                    platform_message_response_android.h
android_environment_gl.cc               android_surface_software.cc             platform_view_android.cc
android_environment_gl.h                android_surface_software.h              platform_view_android.h
android_external_texture_gl.cc          android_surface_vulkan.cc               platform_view_android_jni.cc
android_external_texture_gl.h           android_surface_vulkan.h                platform_view_android_jni.h
android_native_window.cc                apk_asset_provider.cc                   vsync_waiter_android.cc
android_native_window.h                 apk_asset_provider.h                    vsync_waiter_android.h
android_shell_holder.cc                 flutter_main.cc
android_shell_holder.h                  flutter_main.h

VM snapshotとは

wikiにちゃんと書いてあるんですね・・・すごい。VMDart VMのことですね。

アプリの起動を高速化するため仕組みのようです。

dartコードを構文解析して得られたバイナリデータのようです。dartコードをロードしたオブジェクトが書き込まれている感じでしょうか。 そのスナップショットを実行すると短い起動時間で処理を再現できるというものみたいです。

僕らが書いたmain.dartはapkビルド時にsnapshotになって、snapshotをDart VMに食わせているのかと推測していますが、間違っていたらスミマセン。。 github.com

以下のドキュメントも参考にしました。 https://www.cresc.co.jp/tech/java/Google_Dart/DartLanguageGuide.pdf

instrって何

instructions(命令?)のことらしいですが、スナップショット自体と処理を別々に管理しているのでしょうか。(ヒープスナップショットと処理スナップショットとか?)

isolateって何

このことであってますかね。Flutter (dart)は基本的にはシングルスレッドですが、 isolateというライブラリを使用することで並列処理が実現できるようになるようです。

その並列の単位をisolate(アイソレート)と呼称し、dart vm上では必ず1アイソレート立ち上がる、と認識しています。 api.dartlang.org

app.soとは

libflutter.soと同じような感じで、libapp.soがapkの中にあるのかなと思いきや存在しない・・・

f:id:ticktakclock:20190106174512p:plain

調べても全然出てこないので、そもそもこのapp.soが使われていない気もしてきました。 一応AOTビルドコマンドが存在します。 github.com

早速試してみましょう。

$ flutter build aot
Building AOT snapshot in release mode (android-arm-release)... 14.4s
Built to build/aot/.
$ ls build/aot/
app.dill            kernel_compile.d
frontend_server.d       kernel_compile.d.fingerprint
gen_snapshot.d          snapshot.d.fingerprint
isolate_snapshot_data       vm_snapshot_data
isolate_snapshot_instr      vm_snapshot_instr

app.soないじゃん 😇 コード検索してもそんなに出てこないし、もう使用されていないのか、根本的になにか勘違いしているのか 🤔

app.flxって何

Flutter SDK側を調べるとそれっぽいものが見つかりました。 ios側のビルドで使用するものっぽいです(flxはFiLe eXtensionの略称であっているのか?)

github.com

そもそもapp.flx自体がapkの中に入っていないですね。(app.soのときのapkのキャプチャ参照)

また、flutter buildでビルドできるコマンドを見てみるとflx Deprecatedと非推奨になっているので昔の名残なのかもしれませんね。

$ flutter build
Flutter build commands.

Usage: flutter build <subcommand> [arguments]
-h, --help    Print this usage information.

Available subcommands:
  aot      Build an ahead-of-time compiled snapshot of your app's Dart code.
  apk      Build an Android APK file from your app.
  bundle   Build the Flutter assets directory from your app.
  flx      Deprecated
  ios      Build an iOS application bundle (Mac OS X host only).

Run "flutter help" to see global options.

おわりに

新年の調べもごと『Flutterってどうやって動いているのか?』を知りたくてFlutter Android側のコードを眺めてみました。 FlutterApplicationではlibflutter.soを使用するための準備とFlutterVMを起動する前の設定値やファイルの準備を行っているようでした。 調べてもそんなに出てこないし、まだまだわからないことが多くてツラミ感じていますが、次はMainActivity側のコードを読んで行こうと思います。