Android 項目接入Flutter

一、官方方案

創建Android項目

如果你已經有Android項目,可以直接使用。這里我們先創建一個空的android項目來模擬已有的項目,取名叫TestFlutter。

創建Flutter模塊

在Android工程目錄同級目錄下執行命令

flutter create -t module flutter_module

上面的命令會創建一個flutter的項目模塊,在flutter文件夾中有一個.android的隱藏文件夾,里面包裹了一個安卓庫的工程模塊。

可以嘗試用Gradle編譯這個庫,但這不是必須的步驟:

$ cd .android/
$ ./gradlew flutter:assembleDebug

編譯后會在.android/Flutter/build/outputs/aar/路徑下產生flutter-debug.aar的文件。

將Flutter模塊作為依賴添加到主項目

打開你的Android工程的setting.gradle文件,添加如下代碼:

include ':app'                                   
setBinding(new Binding([gradle: this]))                                 
evaluate(new File(                                                      
  settingsDir.parentFile,                                               
  'flutter_module/.android/include_flutter.groovy'                          
))                                                                      

這幾行代碼的意思就是將你剛才創建的那個module作為android模塊引入到Android工程中。

點擊同步完成后,到你app目錄的build.gradle文件把依賴加上:

dependencies {
  implementation project(':flutter')
}

再次同步完成就已經將Flutter添加到了你的項目了。接下來就可以開始混合開發了。

遇到問題總結

1、比如,我們不在Android工程的同級目錄去flutter create -t module my_flutter會怎么樣,我嘗試了,只需要對路徑加上你工程目錄名即可,這么寫

setBinding(new Binding([gradle: this]))                                
evaluate(new File(                                                      
        settingsDir.parentFile,                                           
        '你工程目錄名/flutter_module/.android/include_flutter.groovy'                        
))

2、release 混淆問題

-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }

二、閑魚方案

通過Android studio 新建Flutter Application項目

在命令行輸入命令flutter build apk
會編譯生成apk文件,位于build/app/outputs/apk/release/文件夾下。

這個apk里的產物實際上是在Android的app/build.gradle構建代碼里引入了Flutter的構建代碼。

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

通過閱讀flutter構建源碼我們發現在構建apk文件的時候,會將需要的文件構建到apk中。

1.assets文件夾

assets文件夾下面有flutter_assets文件夾、flutter_shared文件夾、isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr文件。

flutter_assets里是flutter工程產生的assets文件
flutter_shared里是封裝在flutter.jar里面的處理字符編碼的ICU庫
isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr為特定平臺的數據和指令

2.lib文件夾

lib文件夾下是特定平臺(arm或者x86)的so文件。

flutter在Android平臺下會默認生成arm-v7架構的的so庫,flutter.gradle源碼中會根據target-platform屬性判斷平臺動態生成對應的so,官方注釋目前flutter只支持在debug模式下生成x86的so。

提取aar

上面通過編譯命令得到了apk,那想要打包成aar,理論上只要把app/build.gradle中的apply plugin: 'com.android.application'修改為apply plugin: 'com.android.library',同時刪除applicationId "com.shanbay.flutterapp"再次執行flutter build apk命令,便可以得到app-release.aar文件。

或者進入android文件目錄下,執行命令 ./gradlew assembleRelease 也可以編譯得到app-release.aar

集成到現有項目

我們將得到的aar文件集成到現有的Android工程中使用,但是打開flutter頁面卻閃退了,同時flutter報出了error,錯誤是說aar里面缺少icudtl.dat文件。

解壓縮aar查看文件結構,可以發現其中的問題。

image.png

在aar文件夾下的assets里面缺少了flutter_shared文件夾,icudtl.dat文件正是在該文件夾里面,也就是說flutter.gradle在編譯流程中并沒有將icudtl.dat文件打進aar包里面,這一點從flutter庫的issue里面得到了證實,我們的辦法是將apk里面得到的flutter_shared文件夾手動copy到flutter工程中,再次編譯aar,這樣就可以得到有icudtl.dat的aar文件。再次集成到Android項目中便可以成功運行,不會產生錯誤。

總結

這個方案需要兩個步驟,第一步是先編譯成apk取得icudtl.dat文件放入到工程中,第二步修改apply plugin: 'com.android.library'再次編譯取得aar。

如果Flutter項目引用了path_provider、shared_preferences 需要將這兩個jar包導入aar中不然會提示找不到這兩個jar包的PathProviderPlugin、SharedPreferencesPlugin

image.png

在app的build.gradle 里面添加代碼

implementation fileTree(dir: 'libs', include: ['*.jar'])

為了方便打aar包和flutter項目運行,我們可以設置一個變量來控制

在android目錄下的gradle.properties里面添加BUILD_MODE=aar,src目錄下新建一個文件夾里面放置AndroidManifest.xml

image.png

在app的build.gradle中添加以下代碼

image.png
image.png
image.png
image.png

之后需要打aar包的時候,只需要在gradle.properties中修改BUILD_MODE為aar就可以用來打aar包,修改為flutter就是正常的flutter編譯模式

build.gradle 相關代碼

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}
def BUILD_MODE = localProperties.getProperty('BUILD_MODE')
def isRunAsFlutter = "flutter".equals(BUILD_MODE)

if (isRunAsFlutter) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 28

    lintOptions {
        disable 'InvalidPackage'
    }

    defaultConfig {
        if (isRunAsFlutter) {
            applicationId "com.test.flutter"
        }
        minSdkVersion 16
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            signingConfig signingConfigs.debug
        }
    }

    sourceSets {
        main {
            def srcFile = isRunAsFlutter ? 'src/main/AndroidManifest.xml' : 'src/maven/AndroidManifest.xml'
            manifest.srcFile srcFile
            java {
                srcDir 'src/main/java'

            }
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }

    configurations.all {
        resolutionStrategy {
            cacheChangingModulesFor 0, 'seconds'
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    if (!isRunAsFlutter) {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    }
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support:design:28.0.0'
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:support-v13:28.0.0'
    implementation 'com.android.support:support-annotations:28.0.0'
}

參考:

Flutter在混合項目中的構建和集成
Flutter混合開發和動態更新的探索歷程 Android版

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容