61 Flutter 混合开发实战(一)

Flutter 混合开发实战(一)

1.简介

该篇主要讲解将Flutter添加到现有的Android开发项目中,通过FlutterActivity、FlutterFragment、FlutterView方式。其中FlutterView方式已被抛弃。注意该篇原理部分分析较少,后面会有专门的源码分析。

2.Flutter Module集成到Android应用

注意:AS中,创建Flutter Module,请在项目根目录上创建,请勿使用File->new->new Module; 第二种方式会导致项目错乱,FlutterActivity、FlutterFragment等失效,无法与Flutter进行通信。

1.配置

android {
  //...
  defaultConfig {
    ndk {
      // Filter for architectures supported by Flutter.
      abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
    }
  }
}

settings.gradle

include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
  settingsDir,
  'flutter_demo/.android/include_flutter.groovy'
))
rootProject.name = "FlutterNative"

include ':flutter_demo'

include ':flutter_demo'

app build.gradle

    implementation project(':flutter')

注意:引入的flutter,而不是其他

2.版本,

minSdkVersion最小为16,低于该版本,Flutter不能使用

JDK一定要是使用1.8以上的版本(包含)

3.FlutterActivity添加Flutter页面

1.xml中添加FlutterActivity
<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/LaunchTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />

theme ——————可以自己选,打开flutter页面时,会有一段时间白屏,当前可以调整白屏的颜色图片

2.使用1
        textView.setOnClickListener(v -> startActivity(
                FlutterActivity.createDefaultIntent(this)
        ));

————————假定Dart 代码入口是调用 main(),Flutter 初始路由是 ‘/’,创建一个新的FlutterEngine

3.使用2
        textView2.setOnClickListener(v -> startActivity(
                FlutterActivity
                        .withNewEngine()
                        .initialRoute("/two")
                        .build(this)
        ));

指定跳转的路由,注意,跳转的路由会将初始路由‘/’也启动, 创建新的FlutterEngine

4.使用3

缓存的 FlutterEngine

Application中

public class MyApplication extends Application {
    public FlutterEngine flutterEngine;
    @Override
    public void onCreate() {
        super.onCreate();
        // Instantiate a FlutterEngine.
        flutterEngine = new FlutterEngine(this);

        flutterEngine.getNavigationChannel().setInitialRoute("/three");//设置需要启动的页面
        // Start executing Dart code to pre-warm the FlutterEngine.
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );

        // Cache the FlutterEngine to be used by FlutterActivity.
        FlutterEngineCache
                .getInstance()
                .put("my_engine_id", flutterEngine);
    }
}

使用

        textView3.setOnClickListener(v -> startActivity(
                FlutterActivity
                        .withCachedEngine("my_engine_id")
                        .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
                        .build(this)
        ));

——————缓存FlutterEngine,一般都会将其生命周期为整个应用,比FlutterActivity和FlutterFragment生命周期高,其中它会首先预热了一个FlutterEngine, 独立于FlutterActivity去执行Dart 代码main(),所以这也是为什么我们后面无法通过指定启动页面,来不及了。

——————因为 FlutterEngine 在预加载的时候并不会执行 Flutter 首页的全部代码,即在界面展示出来之前,Widget 的 build 方法不会执行,所以如果在 AndroidFlutterActivity onCreate 方法中向 Flutter 发送消息,虽然能够收到,但是时机过早

5.设置主题

大部分的全屏 Flutter 交互页面是不透明的。但是,一些应用可能会发布一个类似模态框的 Flutter 页面,例如,一个对话框或者底部工作表。 Flutter 默认支持透明的 FlutterActivity

style中

<style name="MyTheme" parent="@style/MyParentTheme">
  <item name="android:windowIsTranslucent">true</item>
</style>

FlutterActivity

<activity
  android:name="io.flutter.embedding.android.FlutterActivity"
  android:theme="@style/MyTheme"
  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  android:hardwareAccelerated="true"
  android:windowSoftInputMode="adjustResize"
  />
启动透明的 FlutterActivity
// Using a new FlutterEngine.
startActivity(
  FlutterActivity
    .withNewEngine()
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

// Using a cached FlutterEngine.
startActivity(
  FlutterActivity
    .withCachedEngine("my_engine_id")
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
    .build(context)
);

4.FlutterFragment

1.介绍

FlutterFragment 允许开发者在 Fragment 中控制以下 Flutter 的开发细节:

  • Flutter 初始路由
  • 将要执行的 Dart 入口
  • 非透明或者透明的背景
  • FlutterFragment 是否能控制它外层的 Activity
  • 使用新的还是缓存的 FlutterEngine

————————简单说,就如同Activity中Fragment

package com.uih.flutternative;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;

import android.content.Intent;
import android.os.Bundle;

import io.flutter.embedding.android.FlutterFragment;
import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.android.RenderMode;
import io.flutter.embedding.android.TransparencyMode;

public class SecondActivity extends AppCompatActivity {
    private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";
    private FlutterFragment flutterFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        // Get a reference to the Activity's FragmentManager to add a new
        // FlutterFragment, or find an existing one.
        FragmentManager fragmentManager = getSupportFragmentManager();

        // Attempt to find an existing FlutterFragment,
        // in case this is not the first time that onCreate() was run.
        flutterFragment = (FlutterFragment) fragmentManager
                .findFragmentByTag(TAG_FLUTTER_FRAGMENT);

        // Create and attach a FlutterFragment if one does not exist.
        if (flutterFragment == null) {
//            flutterFragment = FlutterFragment.createDefault();
            flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
                    .renderMode(RenderMode.texture) //采用TextureView模式加载
                    .transparencyMode(TransparencyMode.transparent) //透明背景色
                    .build();
            fragmentManager
                    .beginTransaction()
                    .add(
                            R.id.fragment_container,
                            flutterFragment,
                            TAG_FLUTTER_FRAGMENT)
                    .commit();
        }
    }

//    @Override
//    public void onPostResume() {
//        super.onPostResume();
//        flutterFragment.onPostResume();
//    }
//
//    @Override
//    protected void onNewIntent(@NonNull Intent intent) {
//        super.onNewIntent(intent);
//        flutterFragment.onNewIntent(intent);
//    }
//
//    @Override
//    public void onBackPressed() {
//        flutterFragment.onBackPressed();
//    }
//
//    @Override
//    public void onRequestPermissionsResult(
//            int requestCode,
//            @NonNull String[] permissions,
//            @NonNull int[] grantResults
//    ) {
//        flutterFragment.onRequestPermissionsResult(
//                requestCode,
//                permissions,
//                grantResults
//        );
//    }
//
//    @Override
//    public void onUserLeaveHint() {
//        flutterFragment.onUserLeaveHint();
//    }
//
//    @Override
//    public void onTrimMemory(int level) {
//        super.onTrimMemory(level);
//        flutterFragment.onTrimMemory(level);
//    }
}

上面的代码会以 main() 为 Dart 入口函数, / 为初始路由,并使用新的 FlutterEngine

2.使用预热FlutterEngine

FlutterFragment 会创建它自己的 FlutterEngine 实例,同时也需要不少的启动时间.

与FlutterActivity一致,原理也一致

            flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
                    .renderMode(RenderMode.texture) //采用TextureView模式加载
                    .transparencyMode(TransparencyMode.transparent) //透明背景色
                    .build();
3.控制 FlutterFragment 的渲染模式

FlutterFragment 可以选择使用 SurfaceView 或者 TextureView 来渲染其内容。默认配置的 SurfaceView 在性能上明显好于 TextureView。然而,SurfaceView 无法插入到 Android 的 View 层级之中。

// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build();

// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .transparencyMode(FlutterView.TransparencyMode.transparent)
    .build();
4.是否控制外部Activity

Fragment 通常只是整个 UI 的一部分,没有必要控制Activity的系统状态,比如状态栏等。以免Flutter 控制 Activity 的系统 UI,可以使用 FlutterFragmentBuilder 中的 shouldAttachEngineToActivity() 方法

// Using a new FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
    .shouldAttachEngineToActivity(false)
    .build();

// Using a cached FlutterEngine.
FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .shouldAttachEngineToActivity(false)
    .build();

5.FlutterView

FlutterView,已经开始不推荐使用,每次加载时都比较慢,存在较大的性能问题,加载速度慢,至于为什么会出现这种情况,我们在下一篇文章具体去分析

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ThreeActivity">

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="0dp"
        android:layout_height="280dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">
        <io.flutter.embedding.android.FlutterView
            android:id="@+id/flutterView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>


    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="0dp"
        android:layout_height="280dp"
        android:layout_marginBottom="2dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher_background" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.uih.flutternative;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import io.flutter.embedding.android.FlutterView;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
public class ThreeActivity extends AppCompatActivity {
    private LinearLayout linearLayout1;
    private LinearLayout linearLayout2;
    private FlutterView flutterView;
    private FlutterEngine flutterEngine;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three);
        linearLayout1 = findViewById(R.id.linearLayout);
        linearLayout2 = findViewById(R.id.linearLayout2);
        flutterView = findViewById(R.id.flutterView);
        initFlutterView();
    }

    public void initFlutterView() {
        flutterEngine = new FlutterEngine(this);
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );
        flutterView.attachToFlutterEngine(flutterEngine);
        flutterEngine.getNavigationChannel().setInitialRoute("/");//设置需要启动的页面
    }


    @Override
    protected void onResume() {
        super.onResume();
        flutterEngine.getLifecycleChannel().appIsResumed();
    }

    @Override
    protected void onPause() {
        super.onPause();
        flutterEngine.getLifecycleChannel().appIsInactive();
    }

    @Override
    protected void onStop() {
        super.onStop();
        flutterEngine.getLifecycleChannel().appIsPaused();
    }
}

————————Flutter2.0,需要在每个生命周期中,通知Flutter,FlutterEngine作为连接桥路

6.分析当前存在的问题

1.缓存FlutterEngine解答

​ FlutterEngine 在预加载的时候并不会执行 Flutter 首页的全部代码,即在界面展示出来之前,Widget 的 build 方法不会执行,所以如果在 AndroidFlutterActivity onCreate 方法中向 Flutter 发送消息,虽然能够收到,但是时机过早。

2.缓存FlutterEngine,发现只有第一次指定的路由有效,第二次为’/'路由

3.如何处理缓存FlutterEngine跳转不同的Flutter页面?

​ 思路1: 多个缓存的FlutterEngine

​ 缺陷:占用内存过高,不可取, 不过,在Flutter2.0官方中说明,每个FlutterEngine只占用180kb的内存,不像以前Android19M,IOS 13m,减少了约 99% 后,

​ 思路2:每次启动时,都创建一个新的FlutterEngine

​ 缺陷:白屏卡顿,每次启动新的FlutterEngine耗时

​ 思路3:根据传递缓存FlutterEngine传递不同的值,然后让Flutter自己跳转不同的Flutter页面

​ 优点:可以实现一个FlutterEngine跳转不同的Flutter页面, 不会有卡顿,性能高

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值