美文网首页
Flutter 混合(1)

Flutter 混合(1)

作者: Android_冯星 | 来源:发表于2019-07-19 17:22 被阅读0次

    https://github.com/fengxing1234/Flutter

    常用命令

    • 创建module
      flutter create -t module my_flutter

    创建项目

    使用Android Studio 在 flutter目录中创建5个项目:

    1. Native 项目
      Android 原生项目

    2. flutter_app项目
      标准的Flutter App工程,包含标准的Dart层与Native平台层

    3. flutter_module项目
      Flutter组件工程,仅包含Dart层实现,Native平台层子工程为通过Flutter自动生成的隐藏工程

    4. flutter_package项目
      Flutter纯Dart插件工程,仅包含Dart层的实现,往往定义一些公共Widget

    5. flutter_plugin项目
      Flutter平台插件工程,包含Dart层与Native平台层的实现

    native集成flutter_module

    官网方式集成

    https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps

    官方建议使用flutter_module形式集成native,这里做个实验,看看flutter_app形式能否成功。

    native项目中:

    • build.gradle
    compileOptions {
      sourceCompatibility 1.8
      targetCompatibility 1.8
    }
    

    不添加会报错的。

    • dependencies
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
        implementation project(':flutter')
    }
    
    • setting gradle
    setBinding(new Binding([gradle: this]))                                 // new
    evaluate(new File(                                                      // new
      settingsDir.parentFile,                                               // new
      'my_flutter/.android/include_flutter.groovy'                          // new
    )) 
    

    同步代码
    mainActivity 中添加代码

    setContentView(R.layout.activity_main);
            container = (FrameLayout) findViewById(R.id.flutter_container);
            findViewById(R.id.btn_flutter).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    View flutterView = Flutter.createView(
                            MainActivity.this,
                            getLifecycle(),
                            "route1"
                    );
    //                FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
    //                layout.leftMargin = 100;
    //                layout.topMargin = 200;
    //                addContentView(flutterView, layout);
                    container.addView(flutterView);
                }
            });
    

    运行,查看结果,完毕。

    aar 方式集成

    flutter_module集成

    首先我们注释掉一些代码

    • setting 中
    include ':app'
    //setBinding(new Binding([gradle: this]))                                 // new
    //evaluate(new File(                                                      // new
    //        settingsDir.parentFile,                                               // new
    //        'flutter_module/.android/include_flutter.groovy'                          // new
    //))
    
    • dependencies 中
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
    //    implementation project(':flutter')
    }
    
    
    • mainActivity
    //                View flutterView = Flutter.createView(
    //                        MainActivity.this,
    //                        getLifecycle(),
    //                        "route1"
    //                );
    ////                FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
    ////                layout.leftMargin = 100;
    ////                layout.topMargin = 200;
    ////                addContentView(flutterView, layout);
    //                container.addView(flutterView);
    

    同步代码 运行。

    首先打出aar包

    在flutter_module目录下,执行:
    flutter build apk
    会在flutter_module/.android/Flutter/build/outputs/aar/生成aar文件
    如果代码没有改变,在此使用命令不会打出新的aar。

    使用
    ./gradlew assembleDebug
    ./gradlew assembleRelease
    这两个命令也可以打出aar包。

    使用aar集成方式,适用flutter_app和flutter_module开发。

    • Native中:

    把这个aar放在libs目录

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

    自动依赖aar

    • java代码

    在开启Flutter前

    FlutterMain.startInitialization(this);
    

    可以放在Application中,这里我就放在了MainActivity 中了

    • MainActivity
    package com.picc.anative;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.FrameLayout;
    
    import io.flutter.view.FlutterMain;
    
    
    public class MainActivity extends AppCompatActivity {
    
        private FrameLayout container;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            FlutterMain.startInitialization(this);
            container = (FrameLayout) findViewById(R.id.flutter_container);
            findViewById(R.id.btn_flutter).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startActivity(new Intent(MainActivity.this, MyFlutterActivity.class));
    //                View flutterView = Flutter.createView(
    //                        MainActivity.this,
    //                        getLifecycle(),
    //                        "route1"
    //                );
    ////                FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
    ////                layout.leftMargin = 100;
    ////                layout.topMargin = 200;
    ////                addContentView(flutterView, layout);
    //                container.addView(flutterView);
                }
            });
        }
    }
    
    
    • MyFlutterActivity
    package com.picc.anative;
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    
    import io.flutter.app.FlutterActivity;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    
    public class MyFlutterActivity extends FlutterActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            GeneratedPluginRegistrant.registerWith(this);
        }
    }
    
    

    这里注意:FlutterActivity 导报时候注意 别倒错了。

    现在同步下代码,然后运行,完事。

    目前这样做会有问题,如果flutter-module集成了native插件,会报错。

    稍等在写一个native插件模拟报错。

    flutter-app 集成进native

    纯flutter-app项目,需要做一些修改才能打aar包。

    • flutter-app/android/app/build.gradle
      修改如下
    def isLib = true
    
    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'
    }
    
    if(isLib) {
        apply plugin: 'com.android.library'
    } else  {
        apply plugin: 'com.android.application'
    }
    apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
    
    android {
        compileSdkVersion 28
    
        lintOptions {
            disable 'InvalidPackage'
        }
    
        defaultConfig {
            // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    
            if(!isLib) {
                applicationId "com.zhyen.flutter_app"
            }
            minSdkVersion 16
            targetSdkVersion 28
            versionCode flutterVersionCode.toInteger()
            versionName flutterVersionName
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    
            if(isLib) {
                ndk {
                    //设置支持的SO库架构
                    abiFilters 'armeabi', 'armeabi-v7a', 'x86'
                }
            }
        }
    
        buildTypes {
            release {
                // TODO: Add your own signing config for the release build.
                // Signing with the debug keys for now, so `flutter run --release` works.
                signingConfig signingConfigs.debug
            }
        }
    
        sourceSets {
            main {
                if (isLib) {
                    manifest.srcFile 'src/main/module/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                }
            }
        }
    }
    
    flutter {
        source '../..'
    }
    
    dependencies {
        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'
    }
    
    

    如果想要打包aar文件 就使用apply plugin: 'com.android.library'
    打包aar,application是打apk包的。

    打包aar,不需要 applicationId

    为了避免AndroidManifest文件冲突,在不同模式下,选择不同文件。

    sourceSets {
            main {
                if (isLib) {
                    manifest.srcFile 'src/main/module/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                }
            }
        }
    

    创建`module/AndroidManifest.xml,上git上找去。

    现在开始打包

    cd flutter_app
    cd android
    ./gradlew assembleDebug
    

    flutter_app/build/app/outputs/aar/目录在会生辰aar文件。

    现在包有了,和上面module集成方式就一样了。
    我这里直接替换aar包,就可以运行了。
    小试了一下, 因为我打的包是debug,在native进入flutter页面时,会出现黑屏,改成release就好了。

    现在 纯flutter 和 module 模式 都可以使用 aar方式集成了
    当然还有很多坑没踩,现在就来踩坑完。
    编写flutter-plugin 让现在的集成方式报错。

    flutter_plugin

    创建插件时,自动生成一个native方法,我们还是在创建一个方法把,就做个toast吧。

    嗯。。。 不写了 。费事。就用自动带的方法吧,如果调用成功,会显示手机版本,如果失败,Unknown。

    flutter_app依赖flutter-plugin

    首先更改开发模式

    def isLib = false
    
    • 依赖插件
      这里使用本地依赖
      pubspec.yaml文件
    flutter_plugin:
        path: ../flutter_plugin/
    
    • 修改代码
      按照plugin-示例代码使用

    运行 ,可以 ,完毕。

    修改模式

    def isLib = true
    

    打包aar。

    • native
      替换aar
      运行,报错。
    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/zhyen/flutter_plugin/FlutterPlugin;
            at io.flutter.plugins.GeneratedPluginRegistrant.registerWith(GeneratedPluginRegistrant.java:14)
            at com.picc.anative.MyFlutterActivity.onCreate(MyFlutterActivity.java:14)
            at android.app.Activity.performCreate(Activity.java:7436)
            at android.app.Activity.performCreate(Activity.java:7426)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3279)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3484)
            at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86)
            at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
            at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2123)
            at android.os.Handler.dispatchMessage(Handler.java:109)
            at android.os.Looper.loop(Looper.java:207)
            at android.app.ActivityThread.main(ActivityThread.java:7470)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
    

    找不到插件,看来打包aar,只能打包源码,不能打包依赖。

    Flutter 带有原生代码的插件,在插件安装后,也是会以本地 Module Project 的形式引入 。

    在插件安装之后,所有带原生代码的插件,都会以路径和插件名的key=value 形式 存在 .flutter-plugins 文件中。

    而在 android 工程的 settings.gradle 里,会通过读取该文件将 .flutter-plugins 文件中的项目一个个 include 到主工程里。

    include ':app'
    
    def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
    
    def plugins = new Properties()
    def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
    if (pluginsFile.exists()) {
        pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
    }
    
    plugins.each { name, path ->
        def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
        include ":$name"
        project(":$name").projectDir = pluginDirectory
    }
    
    

    之后就是主工程里的 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 脚本的引入了,这个脚本一般在于 flutterSDK/packages/flutter_tools/gradle/目录下,其中最关键的部分同样是 读取.flutter-plugins 文件中的项目,然后一个一个再implementation 到主工程里完成依赖。

    自此所有原生代码的 Flutter 插件,都被作为本地 Module Project 的形式引入主工程了 ,最后脚本会自动生成一个 GeneratedPluginRegistrant.java 文件,实现原生代码的引用注册, 而这个过程对你完全是无感的。

    解决办法 一 使用 fat-aar

    -修改flutter-app端代码

    • android/gradle文件
      添加 fat-aar
    dependencies {
            classpath 'com.android.tools.build:gradle:3.2.1'
            classpath 'com.kezong:fat-aar:1.1.10'
        }
    
    • android/app/gradle文件
    if(isLib) {
        apply plugin: 'com.android.library'
    } else  {
        apply plugin: 'com.android.application'
    }
    if(isLib) {
        apply plugin: 'com.kezong.fat-aar'
    }
    apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
    
     ///为库的方式才添加本地仓库依赖,这个本地仓库目前是从 include 那里读取的。
        if(isLib) {
            def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
            def plugins = new Properties()
            def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
            if (pluginsFile.exists()) {
                pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
            }
            plugins.each { name, _ ->
                println name
                embed project(path: ":$name", configuration: 'default')
            }
        }
    

    打aar包。可以使用了,完毕。

    方案二

    方案二我们使用 flutter_module来做,一人一个,谁也不偏向。

    flutter_module 依赖和app一样的。
    lib代码一样的。
    修改一下标题区分module还是app。

    编译,生成aar,替换aar,运行,报错。

    还是一样的错误,我们来解决一下。

    • flutter-plugin 项目
      生成aar包

    或者直接在flutter-module中找到plugin的aar


    image.png

    找到aar后,直接把aar文件放在libs目录下。

    运行,完毕,可用。

    相关文章

      网友评论

          本文标题:Flutter 混合(1)

          本文链接:https://www.haomeiwen.com/subject/fgljlctx.html