【Flutter 混合開(kāi)發(fā)】添加 Flutter 到 Android Activity

Flutter 混合開(kāi)發(fā)系列 包含如下:

  • 嵌入原生View-Android
  • 嵌入原生View-iOS
  • 與原生通信-MethodChannel
  • 與原生通信-BasicMessageChannel
  • 與原生通信-EventChannel
  • 添加 Flutter 到 Android Activity
  • 添加 Flutter 到 Android Fragment
  • 添加 Flutter 到 iOS

每個(gè)工作日分享一篇脊僚,歡迎關(guān)注瓢捉、點(diǎn)贊及轉(zhuǎn)發(fā)箍邮。

創(chuàng)建 Flutter Module

Flutter可以以源代碼或AAR的方法嵌入到Android原生項(xiàng)目悦施,集成流程可以使用 Android Studio 完成,也可以手動(dòng)完成削解。強(qiáng)烈建議使用 Android Studio竖伯。

首先創(chuàng)建一個(gè) Android 項(xiàng)目茫舶,創(chuàng)建一個(gè)空的 Activity:

Android 項(xiàng)目創(chuàng)建成功后,使用Android Studio 添加Flutter模塊恤筛,在Android原生項(xiàng)目中點(diǎn)擊“File > New > New Module...”官还,創(chuàng)建 Flutter Module

注意:Android Studio 的版本3.5及以上,F(xiàn)lutter IntelliJ plugin版本42及以上毒坛。

在彈出的選擇Module類型的對(duì)話框中選中Flutter Module,然后點(diǎn)擊Next望伦,

設(shè)置Flutter module的Project name、Flutter SDK等煎殷,點(diǎn)擊Next:

設(shè)置Flutter module的包名屡谐,點(diǎn)擊Finish:

編譯完成后將在當(dāng)前App目錄下生成Flutter模塊的代碼,目錄結(jié)構(gòu)如下:

啟動(dòng)頁(yè)加載 Flutter

將 Flutter 頁(yè)面加載到 MainActivity(默認(rèn)啟動(dòng)頁(yè)) 中蝌数,修改 MainActivity :

package com.flutter.androidflutter

import io.flutter.embedding.android.FlutterActivity

class MainActivity : FlutterActivity()

你沒(méi)有看錯(cuò)愕掏,只需讓 MainActivity 繼承 FlutterActivity 即可。

注意:FlutterActivity的包名是io.flutter.embedding.android.FlutterActivity

跳轉(zhuǎn)到 Flutter 頁(yè)面

MainActivity(默認(rèn)啟動(dòng)頁(yè))添加一個(gè)按鈕顶伞,點(diǎn)擊后跳轉(zhuǎn)到新的頁(yè)面饵撑,此頁(yè)面加載 Flutter 剑梳,MainActivity代碼如下:

package com.flutter.androidflutter

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            startActivity(Intent(this,SecondFlutterActivity::class.java))
        }
    }
}

SecondFlutterActivity 代碼如下:

package com.flutter.androidflutter

import io.flutter.embedding.android.FlutterActivity

class SecondFlutterActivity:FlutterActivity() 

AndroidManifest.xml 中注冊(cè)此 Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.flutter.androidflutter">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <activity android:name=".SecondFlutterActivity"/>
    </application>

</manifest>
flutter_android_1

SecondFlutterActivity 只是繼承了 FlutterActivity,這種情況下滑潘,也可以直接使用 FlutterActivity

startActivity(Intent(this, FlutterActivity::class.java))

或者:

startActivity(FlutterActivity.createDefaultIntent(this))

AndroidManifest.xml 中注冊(cè) FlutterActivity:

<activity android:name="io.flutter.embedding.android.FlutterActivity"/>

效果與上面是一樣的垢乙。

FlutterActivity 會(huì)加載 Flutter Module 中 lib/main.dart 中 main 方法,如果有多個(gè)Flutter頁(yè)面语卤,如何指定跳轉(zhuǎn)追逮,比如現(xiàn)在有 OnePage Flutter 頁(yè)面,OnePage 代碼如下:

class OnePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text('這是 One 頁(yè)面'),
      ),
    );
  }
}

FlutterActivity 指定加載頁(yè)面需要使用命名路由粹舵,MyApp 修改如下:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      routes: {
        'one_page':(context){
          return OnePage();
        },
        'two_page':(context){
          return TwoPage();
        }
      },
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

MainActivity 頁(yè)面點(diǎn)擊到 Flutter 頁(yè)面钮孵,加載 OnePage 頁(yè)面:

class MainActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            startActivity(
                FlutterActivity
                    .withNewEngine()
                    .initialRoute("one_page")
                    .build(this)
            )
        }
    }
}

引擎緩存

加載 FlutterActivity 頁(yè)面時(shí)明顯看到一段時(shí)間的黑屏,這段時(shí)間主要是啟動(dòng) Flutter 引擎(FlutterEngine)眼滤,F(xiàn)lutter 引擎啟動(dòng)的時(shí)間在不同手機(jī)上不同巴席,性能越好的手機(jī)越短。同時(shí)每一個(gè) FlutterActivity 頁(yè)面都會(huì)啟動(dòng)一個(gè)引擎诅需,所以強(qiáng)烈建議不要在一個(gè)項(xiàng)目中創(chuàng)建多個(gè) FlutterActivity(或者啟動(dòng)多個(gè) FlutterActivity 實(shí)例)漾唉,否則內(nèi)存會(huì)越來(lái)越大,下面是每隔3秒創(chuàng)建一個(gè) FlutterActivity 實(shí)例內(nèi)存變化圖:

為了減少 FlutterActivity 頁(yè)面的延遲時(shí)間和多個(gè) FlutterActivity 實(shí)例內(nèi)存一直增長(zhǎng)問(wèn)題堰塌,我們可以使用 Flutter 引擎(FlutterEngine)緩存壮锻,在啟動(dòng) FlutterActivity 前先啟動(dòng) Flutter 引擎堪伍,然后使用緩存的引擎加載頁(yè)面,通常將其放在 Application 中:

class MyApplication : Application() {
    lateinit var flutterEngine: FlutterEngine

    override fun onCreate() {
        super.onCreate()
        flutterEngine = FlutterEngine(this)
        flutterEngine.dartExecutor.executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
        )
        FlutterEngineCache
            .getInstance()
            .put("engine_id", flutterEngine)
    }

}

使用緩存的引擎:

startActivity(
    FlutterActivity
        .withCachedEngine("engine_id")
        .build(this)
)

在同一臺(tái)手機(jī)上效果非常明顯,黑屏?xí)r間大大減少掰伸,不過(guò)還是有一個(gè)短暫的黑屏托嚣。

這里要注意汞窗,使用緩存引擎時(shí)趾撵,其生命周期不在是 FlutterActivity(或者 FlutterFragment)的生命周期,而是整個(gè) App 的生命周期(在Application 中的創(chuàng)建和銷毀)施籍。當(dāng)然也可以提前銷毀:

flutterEngine.destroy()

另外項(xiàng)目的 debug 和 release 版本對(duì)性能的影響非常大居扒,如果要測(cè)試其性能一定在要 release 下測(cè)試

上面使用新的引擎可以指定 FlutterActivity(或者 FlutterFragment)配置初始路由丑慎,但使用緩存引擎時(shí)無(wú)法在 FlutterActivity(或者 FlutterFragment)配置初始路由喜喂,因?yàn)榫彺嬉嬉呀?jīng)啟動(dòng)并運(yùn)行,不過(guò)可以在啟動(dòng)緩存引擎時(shí)指定其初始路由:

flutterEngine = FlutterEngine(this)

flutterEngine.navigationChannel.setInitialRoute("one_page")

flutterEngine.dartExecutor.executeDartEntrypoint(
    DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
    .getInstance()
    .put("engine_id", flutterEngine)

如果使用緩存引擎在FlutterActivity(或 FlutterFragment)指定不同路由竿裂,如何處理玉吁?這時(shí)需要?jiǎng)?chuàng)建一個(gè) method channel,flutter 接收具體消息從而切換不同的路由腻异。

交流

老孟Flutter博客(330個(gè)控件用法+實(shí)戰(zhàn)入門(mén)系列文章):http://laomengit.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末进副,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子悔常,更是在濱河造成了極大的恐慌影斑,老刑警劉巖给赞,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異矫户,居然都是意外死亡片迅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)皆辽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柑蛇,“玉大人,你說(shuō)我怎么就攤上這事驱闷〕芴ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵遗嗽,是天一觀的道長(zhǎng)粘我。 經(jīng)常有香客問(wèn)我鼓蜒,道長(zhǎng)痹换,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任都弹,我火速辦了婚禮娇豫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畅厢。我一直安慰自己冯痢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布框杜。 她就那樣靜靜地躺著浦楣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咪辱。 梳的紋絲不亂的頭發(fā)上振劳,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音油狂,去河邊找鬼历恐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛专筷,可吹牛的內(nèi)容都是我干的弱贼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼磷蛹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吮旅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起味咳,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庇勃,失蹤者是張志新(化名)和其女友劉穎氛什,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體匪凉,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枪眉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了再层。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贸铜。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖聂受,靈堂內(nèi)的尸體忽然破棺而出蒿秦,到底是詐尸還是另有隱情,我是刑警寧澤蛋济,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布棍鳖,位于F島的核電站,受9級(jí)特大地震影響碗旅,放射性物質(zhì)發(fā)生泄漏渡处。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一祟辟、第九天 我趴在偏房一處隱蔽的房頂上張望医瘫。 院中可真熱鬧,春花似錦旧困、人聲如沸醇份。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)僚纷。三九已至,卻和暖如春拗盒,著一層夾襖步出監(jiān)牢的瞬間怖竭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工锣咒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侵状,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓毅整,卻偏偏與公主長(zhǎng)得像趣兄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悼嫉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359