Flutter動(dòng)態(tài)化-Android(二)

上一篇文章我們已經(jīng)了解如何修改flutter engine代碼實(shí)現(xiàn)動(dòng)態(tài)化效果柑贞,這一篇文章主要是講解,flutter build aar的過程,然后修改對(duì)應(yīng)的gradle編譯腳本,目的是打出適合混合工程使用的aar包些阅。

一、flutter build aar打包結(jié)果

這一小節(jié)斑唬,我們先來看下正常使用flutter build aar打包出的aar的結(jié)構(gòu)是怎么樣的

1.1市埋、前置條件

Android混合工程,先創(chuàng)建一個(gè)新的Android項(xiàng)目赖钞,然后新建flutter module腰素,也可以使用flutter create -t module --org com.example flutter_module創(chuàng)建。

cd flutter_module雪营,然后執(zhí)行flutter pub get弓千,會(huì)自動(dòng)創(chuàng)建.android文件

1.2、執(zhí)行flutter build aar

image

我們這里只分析release版本的aar献起,將flutter_release-1.0.aar的后綴修改成jar洋访,然后用反編譯工具JD-GUI打開flutter_release-1.0.jar,結(jié)果如下:

image

可以看到打包的代碼中沒有flutter.jar的代碼谴餐,也沒有libflutter.so姻政,那么這兩個(gè)文件放在哪兒了呢?

如何將這兩個(gè)文件打包到aar中呢岂嗓?帶著問題我們來看下執(zhí)行flutter build aar過程中都執(zhí)行了哪些腳本汁展。

這里先提一個(gè)解決方案,那就是fat-aar來打包aar厌殉,具體使用請大家自行百度

二食绿、flutter build aar的過程解析

關(guān)于flutter命令的執(zhí)行流程,里面的東西還是挺多的公罕,我會(huì)單獨(dú)拿一篇文章來寫器紧,這一小節(jié),主要介紹build過程中涉及到的兩個(gè)gradle文件:flutter.gradle楼眷、aar_init_script.gradle

2.1铲汪、flutter.gradle

:Flutter的build.gradle中引用了apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

// 引用plugin
apply plugin: FlutterPlugin
// 緊接著是plugin的實(shí)現(xiàn)
class FlutterPlugin implements Plugin<Project> {
  void apply(Project project) {
    // 創(chuàng)建擴(kuò)展字段source、target
    project.extensions.create("flutter", FlutterExtension)
    // 其他任務(wù)完成后執(zhí)行addFlutterTasks(設(shè)置flutter相關(guān)初始化項(xiàng))
    project.afterEvaluate this.&addFlutterTasks
    
    // abi相關(guān)配置
    if (shouldSplitPerAbi())
    罐柳。掌腰。。
    张吉。辅斟。。
    
    // 獲取engine的版本芦拿,方便從maven上下載
    engineVersion = useLocalEngine() ? "+" : "1.0.0-" + Paths.get(flutterRoot.absolutePath, "bin", "internal", "engine.version").toFile().text.trim()
    士飒。。蔗崎。
    // 使用本地engine時(shí)的配置獲取酵幕,需要在gradle.properties中配置
    if (useLocalEngine()) {
      // This is required to pass the local engine to flutter build aot.
      String engineOutPath = project.property('local-engine-out')
      File engineOut = project.file(engineOutPath)
      if (!engineOut.isDirectory()) {
        throw new GradleException('local-engine-out must point to a local engine build')
      }
      localEngine = engineOut.name
      localEngineSrcPath = engineOut.parentFile.parent
    }
    // 每種type類型添加dependencies
    project.android.buildTypes.each this.&addFlutterDependencies
    project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies
  }
}

核心的就是這個(gè)apply方法,主要是配置dependencies和其他一些相關(guān)配置缓苛,下面我們來看看addFlutterDependencies方法做了哪些事情:

/**
* Adds the dependencies required by the Flutter project.
* This includes:
*    1. The embedding
*    2. libflutter.so
*/
// 只看這個(gè)注釋就能知道芳撒,這個(gè)方法是用于添加flutter.jar和libflutter.so的
void addFlutterDependencies(buildType) {
  String flutterBuildMode = buildModeFor(buildType)
  // 只有在使用本地engine時(shí)supportsBuildMode會(huì)判斷gradle.properites中的local-engine-build-mode是否和當(dāng)前flutterBuildMode相同
  if (!supportsBuildMode(flutterBuildMode)) {
    return
  }
  // 設(shè)置maven源路徑
  String repository = useLocalEngine() ? project.property('local-engine-repo') : MAVEN_REPO
  project.rootProject.allprojects {
    repositories {
      maven {
        url repository
      }
    }
  }
  // Add the embedding dependency.
  // 添加flutter.jar引用
  addApiDependencies(project, buildType.name,   "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion")

  。未桥。笔刹。
  platforms.each { platform ->
    String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_")
    // 添加libflutter.so依賴
    addApiDependencies(project, buildType.name,
                       "io.flutter:${arch}_$flutterBuildMode:$engineVersion")
  }
}

2.2、aar_init_script.gradle

這個(gè)腳本是會(huì)有flutter build aar觸發(fā)的冬耿,文件路徑$flutterRoot/packages/flutter_tools/gradle/aar_init_script.gradle

projectsEvaluated {
    assert rootProject.hasProperty("is-plugin")
    if (rootProject.property("is-plugin").toBoolean()) {
        assert rootProject.hasProperty("output-dir")
        // In plugin projects, the root project is the plugin.
        configureProject(rootProject, rootProject.property("output-dir"))
        return
    }
    // 針對(duì)混合工程時(shí)的 `:flutter` module
    Project moduleProject = rootProject.subprojects.find { it.name == "flutter" }
    assert moduleProject != null
    assert moduleProject.hasProperty("output-dir")
    configureProject(moduleProject, moduleProject.property("output-dir"))

    // 獲取所有的plugin子工程
    Set<Project> modulePlugins = rootProject.subprojects.findAll {
        it.name != "flutter" && it.name != "app"
    }
    modulePlugins.each { pluginProject ->
        configureProject(pluginProject, moduleProject.property("output-dir"))
        moduleProject.android.libraryVariants.all { variant ->
            String variantName = variant.name.capitalize()
              // 插件的`assembleAar$variantName`任務(wù)執(zhí)行完后再執(zhí)行moduleProject的任務(wù)
            moduleProject.tasks.findByPath("assembleAar$variantName")
                .dependsOn(pluginProject.tasks.findByPath("assembleAar$variantName"))
        }
    }
}

上面是入口方法舌菜,從上面的腳本里能看出有一個(gè)核心的方法configureProject,下面我們來看看這個(gè)方法的作用:

void configureProject(Project project, String outputDir) {
    // 保證是Android工程
    if (!project.hasProperty("android")) {
        throw new GradleException("Android property not found.")
    }
    // 保證是library而不是application
    if (!project.android.hasProperty("libraryVariants")) {
        throw new GradleException("Can't generate AAR on a non Android library project.");
    }
        // 使用maven插件
    project.apply plugin: "maven"

    project.android.libraryVariants.all { variant ->
        // 在上傳maven任務(wù)完成后再執(zhí)行這個(gè)任務(wù)
        addAarTask(project, variant)
    }
   
    // 上傳的maven路徑亦镶,此處是maven的本地庫的一個(gè)用法
    project.version = project.version.replace("-SNAPSHOT", "")
    project.uploadArchives {
        repositories {
            mavenDeployer {
                repository(url: "file://${outputDir}/outputs/repo")
            }
        }
    }
    if (!project.property("is-plugin").toBoolean()) {
        return
    }
    // build aar目前暫時(shí)不支持使用本地engine
    if (project.hasProperty('localEngineOut')) {
        throw new GradleException(
            "Local engine isn't supported when building the plugins as AAR. " +
            "See: https://github.com/flutter/flutter/issues/40866")
    }

        // 添加倉庫以及依賴
    project.repositories {
        maven {
            url "http://download.flutter.io"
        }
    }
    String engineVersion = Paths.get(getFlutterRoot(project), "bin", "internal", "engine.version").toFile().text.trim()
    project.dependencies {
                // 添加flutter.jar依賴
        compileOnly ("io.flutter:flutter_embedding_release:1.0.0-$engineVersion") {
            // 我們只需要暴漏出io.flutter.plugin.*
            // 不需要暴漏依賴項(xiàng)日月,transitive的默認(rèn)值是true,gradle會(huì)自動(dòng)添加子依賴缤骨,此處表示不添加子依賴
            transitive = false
        }
    }
}

這個(gè)方法的主要作用就是添加完依賴項(xiàng)后執(zhí)行上傳maven任務(wù)爱咬,下面來看看上傳的方法addAarTask

void addAarTask(Project project, variant) {
    String variantName = variant.name.capitalize()
    String taskName = "assembleAar$variantName"
    project.tasks.create(name: taskName) {
                //檢查是否配置`uploadArchives`
        if (!project.gradle.startParameter.taskNames.contains(taskName)) {
            return
        }
        // 以下是上傳maven配置
        project.uploadArchives.repositories.mavenDeployer {
            pom {
                artifactId = "${project.name}_${variant.name.toLowerCase()}"
            }
        }
        overrideDefaultPublishConfig(project, variant)
        // Generate the Maven artifacts.
        finalizedBy "uploadArchives"
    }
}

上面兩個(gè)核心的打包aar的腳本我們已經(jīng)介紹完了,下一小節(jié)我們看如何修改這個(gè)腳本

三绊起、如何修改gradle打包aar腳本

我們修改打包腳本的主要原因是:

  1. 打包aar時(shí)能使用本地engine
  2. 將修改后的本地engine打包到aar中精拟,也就是engine產(chǎn)物flutter.jarlibflutter.so文件打包進(jìn)aar

3.1、修改flutter.gradle中的addFlutterDependencies

void addFlutterDependencies(buildType) {
  String flutterBuildMode = buildModeFor(buildType)
  if (!supportsBuildMode(flutterBuildMode)) {
    return
  }
  // add local engine dependencies by panmin [start]
  // 當(dāng)使用本地engine時(shí)添加flutter.jar和libflutter.so文件依賴
  if(useLocalEngine()){
    String engineOutPath = project.property('local-engine-out')
    File engineOut = project.file(engineOutPath)
    if (!engineOut.isDirectory()) {
      throw new GradleException('local-engine-out must point to a local engine build')
    }
    // 本地engine的flutter.jar文件路徑
    File flutterJar = Paths.get(engineOut.absolutePath, "flutter.jar").toFile()
    if (!flutterJar.isFile()) {
      throw new GradleException('Local engine build does not contain flutter.jar')
    }
    // 本地engine的libflutter.so文件路徑
    File flutterSo = Paths.get(engineOut.absolutePath, "flutter_embedding_$flutterBuildMode").toFile()
    project.dependencies {
      // 添加自動(dòng)生成的`GeneratedPluginRegistrant.java`文件需要的@Keep和@NonNull需要的依賴庫
      implementation 'androidx.annotation:annotation:1.1.0'
      // add flutter jar & libflutter so
      if (project.getConfigurations().findByName("api")) {
        "${flutterBuildMode}Api" project.files(flutterJar)
        "${flutterBuildMode}Api" project.files(flutterSo)
      } else {
        "${flutterBuildMode}Compile" project.files(flutterJar)
        "${flutterBuildMode}Compile" project.files(flutterSo)
      }
    }
    return;
  }
  // add local engine dependencies by panmin [end]
  虱歪。蜂绎。。
}

3.2实蔽、修改aar_init_script.gradle

void configureProject(Project project, String outputDir) {
    if (!project.hasProperty("android")) {
        throw new GradleException("Android property not found.")
    }
    if (!project.android.hasProperty("libraryVariants")) {
        throw new GradleException("Can't generate AAR on a non Android library project.");
    }
    project.apply plugin: "maven"
        // add local engine dependencies by panmin [start]
    if (project.hasProperty('local-engine-build-mode')){
        String flutterBuildMode = project.property('local-engine-build-mode').toLowerCase()
        project.android.libraryVariants.all { variant ->
            String variantName = variant.name.capitalize().toLowerCase()
            // 判斷當(dāng)前gradle.properties配置的打包的模式
            if (variantName == flutterBuildMode) {
                addAarTask(project, variant)
            }
        }
    } else {
        project.android.libraryVariants.all { variant ->
            addAarTask(project, variant)
        }
    }
    // add local engine dependencies by panmin [end]
    project.version = project.version.replace("-SNAPSHOT", "")
        荡碾。。局装。坛吁。
}

四、如何使用修改后的gradle腳本

4.1铐尚、配置.android項(xiàng)目的gradle.properties

# 以打包armeabi-v7a架構(gòu)為例
local-engine-repo=engine/src/out/android_release # 這個(gè)自己通過gclient sync下載flutter engine的路徑
local-engine-out=engine/src/out/android_release # arm64平臺(tái)對(duì)應(yīng)使用android_release_arm64
local-engine-build-mode=release

未修改腳本前使用flutter build aar時(shí)會(huì)報(bào)錯(cuò):

Could not determine the dependencies of task ':flutter:compileReleaseAidl'.
    > Could not resolve all task dependencies for configuration ':flutter:releaseCompileClasspath'.
    > Could not find any matches for io.flutter:flutter_embedding_release:+ as no versions of io.flutter:flutter_embedding_release are available.
        Required by:
         project :flutter
  > Could not find any matches for io.flutter:armeabi_v7a_release:+ as no versions of io.flutter:armeabi_v7a_release are available.
    Required by:
        project :flutter
  > Could not find any matches for io.flutter:arm64_v8a_release:+ as no versions of io.flutter:arm64_v8a_release are available.
    Required by:
        project :flutter
  > Could not find any matches for io.flutter:x86_64_release:+ as no versions of io.flutter:x86_64_release are available.
    Required by:
            project :flutter

可能是ninja -C out/android_release時(shí)生成的flutter_embedding_release.jar不能通過maven引用拨脉,大家有什么好的解釋,歡迎留言區(qū)討論宣增。

4.2玫膀、打包aar命令

修改腳本后,因?yàn)?code>gradle.properties中配置了local-engine-build-mode=release并且local-engine-outandroid_release爹脾,也就是armeabi-v7a版本的帖旨,所以這里執(zhí)行打release包的命令為:

# 對(duì)應(yīng)arm平臺(tái)的release模式
flutter build aar --target-platform=android-arm --no-debug --no-profile --verbose
# 對(duì)應(yīng)arm64平臺(tái)的release模式
flutter build aar --target-platform=android-arm64 --no-debug --no-profile --verbose

打包結(jié)果為:


image

這個(gè)是打包后的結(jié)果箕昭,flutter.jarlibflutter.so已經(jīng)打包進(jìn)去了,這個(gè)aar產(chǎn)物直接交給Android原生工程使用的話就能直接使用了解阅。

五落竹、總結(jié)

這篇主要將了如何使用已經(jīng)編譯后flutter engine產(chǎn)物flutter.jarlibflutter.so,依賴修改了flutter.gradleaar_init_script.gradle的打包腳本货抄,實(shí)現(xiàn)了產(chǎn)物嵌入述召,方便Android混合工程開發(fā)。但是還是有些不方便的地方就是大家都需要去下載蟹地、修改积暖、編譯flutter engine,下一篇文章我會(huì)講下如何把編譯產(chǎn)物上傳maven庫怪与,然后再次修改打包腳本夺刑,方便大家無腦使用,歡迎大家關(guān)注和點(diǎn)贊琼梆,也歡迎大家在留言區(qū)溝通交流性誉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茎杂,隨后出現(xiàn)的幾起案子错览,更是在濱河造成了極大的恐慌,老刑警劉巖煌往,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倾哺,死亡現(xiàn)場離奇詭異,居然都是意外死亡刽脖,警方通過查閱死者的電腦和手機(jī)羞海,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曲管,“玉大人却邓,你說我怎么就攤上這事≡核” “怎么了腊徙?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長檬某。 經(jīng)常有香客問我撬腾,道長,這世上最難降的妖魔是什么恢恼? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任民傻,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘漓踢。我一直安慰自己牵署,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布喧半。 她就那樣靜靜地躺著碟刺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薯酝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天爽柒,我揣著相機(jī)與錄音吴菠,去河邊找鬼。 笑死浩村,一個(gè)胖子當(dāng)著我的面吹牛做葵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播心墅,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼酿矢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怎燥?” 一聲冷哼從身側(cè)響起瘫筐,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铐姚,沒想到半個(gè)月后策肝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隐绵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年之众,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片依许。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棺禾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出峭跳,到底是詐尸還是另有隱情膘婶,我是刑警寧澤表锻,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布瓣蛀,位于F島的核電站,受9級(jí)特大地震影響鉴象,放射性物質(zhì)發(fā)生泄漏滞欠。R本人自食惡果不足惜古胆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逸绎,春花似錦惹恃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颊乘,卻和暖如春参淹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乏悄。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工浙值, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人檩小。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓开呐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親规求。 傳聞我的和親對(duì)象是個(gè)殘疾皇子筐付,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容