请选择 进入手机版 | 继续访问电脑版

石家庄老站长

点击联系客服
客服QQ:509006671 客服微信:mengfeiseo
 找回密码
 立即注册
查看: 10|回复: 0

Android和Flutter混合开发

[复制链接]

1

主题

1

帖子

-7

积分

限制会员

积分
-7
发表于 2021-6-10 07:03:38 | 显示全部楼层 |阅读模式



最近项目中需要在Android原生的应用中添加一些功能,时间紧,任务重,考虑再三,只有Android与Flutter混合才能按时完成。如上图所示,在Android页面中有些按钮需要在Android中跳转,而一些按钮则需跳转至Flutter页面,本文简单梳理一下混合开发流程。

1. 创建flutter  module





在Android项目中,单击New,然后单击New  Module。然后,在弹出面板中选择Flutter  module,输入Flutter  module的Project  name,选择Flutter  SDK所在的路径,选择Flutter  module的文件位置,输入Flutter  Module的说明,然后输入Next





填写上诉基本信息后,单击“下一步”,然后在弹出面板中输入Flutter  module的软件包名称,如下图所示





输入Package  name并单击Finish后,将正式创建Flutter  module。创建的flutter  module和新创建的flutter项目在内容上几乎没有差异。

2. 关联Flutter  Module

通常,当用户创建Flutter  module时,在Android项目中创建的flutter  module会自动连接。是否可以在Android项目的settings.gradle中查看关联。
ext_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MjAyMDU0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述" />
若开发中是导入flutter module,或想直接使用其他的flutter项目,可手动在settings.gradle中添加

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

其中’…/flutter_module/.android/include_flutter.groovy’是flutter module中include_flutter.groovy的文件路径,由于flutter module所在的位置不一定在Android项目中,只要这个路径写对,flutter module在电脑中任意位置都可以。
同时还需要在使用flutter module的Android module下的build.gradle中dependencies添加

implementation project(path: ':flutter')

3. Android中跳转Flutter页面
  • FlutterActivity直接跳转
    在跳转之前需要先在AndroidManifest.xml注册FlutterActivity。

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

    在需要跳转的时候,使用FlutterActivity.withNewEngine()进行跳转:

       startActivity(FlutterActivity.withNewEngine()
                  .initialRoute("params")
                  .build(xxxxActivity.this));

    其中initialRoute是Android跳转到flutter需要的参数,非必需。
    在flutter接收参数如下

       ...
      class _MyHomePageState extends StateMyHomePage> {
      String route = window.defaultRouteName;
      ...
      }

    window.defaultRouteName就是获取Android传递过来的参数,当Android端需要跳转多个flutter页面,通常这个用于路由分发,若需要的信息比较多的时候可以传递json字符串。注:window.defaultRouteName的导包为’dart:ui’,而不是’dart:html’。

  • FlutterActivity间接跳转
    所谓的间接跳转其实就是通过继承FlutterActivity来实现的

    public class Hybrid extends FlutterActivity {
        public final static String PARAMS = "params";
        private String params;
       
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            params = getIntent().getStringExtra(PARAMS);
        }
        @NonNull
        @Override
        public String getInitialRoute() {
            return params == null ? super.getInitialRoute() : params;
        }
        public static void toFlutter(Context context, String params) {
            Intent intent = new Intent(context, Hybrid.class);
            intent.putExtra(PARAMS, params);
            context.startActivity(intent);
        }
       
    }

    在AndroidManifest.xm注册Hybrid后就可以通过

    Hybrid.toFlutter(xxxxxActivity.this,"params");

    进行跳转。

  • 通过FlutterEngine和BasicMessageChannel进行跳转
    为什么会有这种方式,是因为使用前两种方式跳转时会有短暂的空白,使用起来感觉非常不流畅,严重影响用户体验。这种跳转方式和上面的两种不同,边跳边发,适用于多种场景。这里也不一定非要使用BasicMessageChannel,也可使用MethodChannel或EventChannel,只是混合开发通常涉及到两端频繁通信,个人更加倾向使用BasicMessageChannel,不分主客,使用和通信更方便。

    [ol]
  • FlutterEngine和BasicMessageChannel的初始化和使用[/ol]
    public class HyBridRouteAndMessageHandleCenter{
        private static FlutterEngine flutterEngine;
        private static BasicMessageChannel basicMessageChannel;
        private static String channelName = "com.hybrid.basic.message.channel";
        public static final String ENGINE_ID = "default_engine_id";
        private static Context mContext;
        private static BasicMessageChannel.Reply mReplay;
        public static void init(Context context) {
            mContext = context;
            if (flutterEngine == null) {
                flutterEngine = new FlutterEngine(context);
                flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
                FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine);
            }
            if (basicMessageChannel == null) {
                if (flutterEngine != null) {
                    basicMessageChannel = new BasicMessageChannelObject>(flutterEngine.getDartExecutor(), channelName, StandardMessageCodec.INSTANCE);
                    basicMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
                        @Override
                        public void onMessage(@Nullable @org.jetbrains.annotations.Nullable Object message, @NonNull @NotNull BasicMessageChannel.Reply reply) {
                            mReplay = reply;
                            // 接收消息并处理
                            handleMessage(message, reply);
                        }
                    });
                }
            }
        }
        // 处理消息
        private static void handleMessage(Object message, BasicMessageChannel.Reply reply) {
                    ...
        }
        public static void toFlutter(Context context, Object params) {
            sendMessage(params);
            context.startActivity(FlutterActivity.withCachedEngine(ENGINE_ID).build(context));
        }
        public static void sendMessage(Object object) {
            if (basicMessageChannel != null) {
                basicMessageChannel.send(object, new BasicMessageChannel.Reply() {
                    @Override
                    public void reply(@Nullable @org.jetbrains.annotations.Nullable Object reply) {
                        // 发送回调
                        ...
                    }
                });
            }
        }
        public static void destroyEngine() {
            if (flutterEngine != null) {
                flutterEngine.destroy();
            }
        }
    }

  • 使用FlutterActivity.withCachedEngine(ENGINE_ID).build(context)使因为从缓存中取更快,更省,而且只需要创建一个flutterEngine即可。Android端其实是可以创建多个flutterEngine,只要ENGINE_ID不同,就不是同一个flutterEngine,但是在iOS中只能有一个flutterEngine,使用多个会无效闪退,为了节省资源,避免过多损耗,全局使用一个flutterEngine比较好。
  • 关于Flutter向Android发送消息,比如flutter想拍照,拍完照后的图片路径需要传给flutter,照片的路径发送可以使用BasicMessageChannel.Reply回复,也可以使用sendMessage主动再发一次消息。个人认为接收消息并回复消息属于一次通信,所以倾向于使用BasicMessageChannel.Reply。若在Android端有多个页面需要向flutter回复结果,建议使用sendMessage。
  • Flutter收发消息[/ol]
        static const BasicMessageChannel messageChannel = const BasicMessageChannel(
          "com.hybrid.basic.message.channel", StandardMessageCodec());
       
             static Futuredynamic>  sendMessage(Object message) async{
                dynamic result = await messageChannel.send(message);
                        print("**********$result");
                        return result;
             }
             ...
             messageChannel.setMessageHandler((message) async {
             ...
            }
            ...

  • sendMessage即flutter向Android端发送消息。比如拍照,拍完照后Android端通过BasicMessageChannel.Reply将图片路径reply.reply(imageUrl)给flutter,而flutter通过dynamic result = await messageChannel.send(message)接收回复的消息,其中result即为Android端返回imageUrl。
  • messageChannel.setMessageHandler是Android主动向flutter发消息,flutter端的消息监听,通过解析message,可以知道Android需要flutter做什么,比如要跳转到什么页面,需要增删改什么数据等等。
    4. 效果


    5. 总结
    1. 若熟练使用Flutter,混合开发可节省至少30%时间。
    2. 直接使用FlutterActivity进行跳转,跳转过程中会出现空白间隙,建议使用FlutterEngineCache,效果更好。
    3. 创建的Flutter Module不一定非要在Android项目中,可以在任意位置,关联正确就好。
    注 :Android Studio 4.2.1,Android SDK 30 ,Flutter 2.2.0 , Dart 2.13.0。
  • 回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|无图版|手机版|小黑屋|石家庄@IT精英团

    GMT+8, 2021-6-25 09:41 , Processed in 2.814981 second(s), 27 queries .

    Powered by Discuz! X3.4

    © 2001-2021 Comsenz Inc.

    快速回复 返回顶部 返回列表