Flutter混合开发 BasicMessageChannel与原生android通信(4.3)

上一篇中,给大家介绍了android原生给flutter发送初始化数据,但是呢flutter不能返回数据给android原生,本篇给大家介绍的是BasicMessageChannel,可以完成android与flutter之间的相互通信!!

混合通讯的三种方式

  • BasicMessageChannel:双向通信,有返回值,持续通信

  • MethodChannel:双向通信,有返回值,一次性通信

  • EventChannel: 单向通信,无返回值 ,持续通信,收到消息后无法回复此次消息

先来看看今天要完成的效果吧:

Flutter混合开发 BasicMessageChannel与原生android通信(4.3)
还是咋们的老套路,先分析一下功能:

分析:

Android端:

  • 一个按钮,携带数据点击跳转到Flutter页面,
  • 一个EditText文本框输入文本内容
  • 完成Flutter数据的传递

Flutter端

  • 一个TextField()组件可以完成数据的返回并Toast
  • 一个RaisedButton()按钮,点击可以完成Toast的展示并且数据返回成功
  • 底部文本展示Android返回的数据

第一步;创建BasicMessageChannel()

(别急着复制,下边我会写成工具类!很好用)

 private final BasicMessageChannel<String> messageChannel;
 
messageChannel = new BasicMessageChannel(messenger, "BasicMessageChannelPlugin", StringCodec.INSTANCE);

BasicMessageChannel参数介绍:

  • BinaryMessenger messenger - 消息信使,是消息的发送与接收的工具;
  • String name - Channel的名字,也是其唯一标识符;
  • MessageCodec<T> codec - 消息的编解码器,他有如下表格类型
MessageCodec<T> codec类型 介绍
BinaryCodec 最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已
JSONMessageCodec 用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典,在Android端则使用了其自定义的JSONUtil与StringCodec作为序列化工具
StringCodec 用于字符串与二进制数据之间的编解码,其编码格式为UTF-8
StandardMessageCodec 是BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典,其工作原理;

第二步:设置消息处理器,处理来自Dart的消息

 //设置消息处理器,处理来自Dart的消息
        messageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler<String>() {
            @Override
            public void onMessage(@Nullable String message, @NonNull BasicMessageChannel.Reply<String> reply) {
            //可以通过reply进行回复
			 reply.reply("BasicMessageChannel收到:" + message);
            }
        });

onMessage参数介绍:

  • 参数一,String message,就是收到的参数
  • 参数二:BasicMessageChannel.Reply<String> reply,当收到Flutter消息时,可以通过 reply.reply()立即返回一个数据

第三步,发送消息

 messageChannel.send(message, callback);

参数介绍:

  • 参数一:要发送的消息内容
  • 参数二:来自Dart的反馈(我觉得并没有什么用… )

将BasicMessageChannel封装成工具类:

import android.app.Activity;
import android.widget.Toast;

import io.flutter.Log;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StringCodec;

/**
      szj 2020/11/26
     CSDN博客:https://blog.csdn.net/weixin_44819566/
     微信公众号:码上变有钱
 */
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String> {
    private final Activity activity;
    private final BasicMessageChannel<String> messageChannel;

    public static BasicMessageChannelPlugin registerWith(BinaryMessenger messenger, Activity activity) {
        return new BasicMessageChannelPlugin(messenger, activity);
    }

    private BasicMessageChannelPlugin(BinaryMessenger messenger, Activity activity) {
        this.activity = activity;
        this.messageChannel = new BasicMessageChannel(messenger, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
        //设置消息处理器,处理来自Dart的消息
        messageChannel.setMessageHandler(this);

    }

    @Override//处理Dart发来的消息
    public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {
        reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
        if (activity instanceof IShowMessage) {
            ((IShowMessage) activity).onShowMessage(s);
            Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
        }


    }

    /**
     * 向Dart发送消息,并接受Dart的反馈
     *
     * @param message  要给Dart发送的消息内容
     * @param callback 来自Dart的反馈
     */
   public void send(String message, BasicMessageChannel.Reply<String> callback) {
        messageChannel.send(message, callback);
    }

}

代码非常简单,有不懂的同学记得评论区留言哦~

第四步:写一个接口,用来定义获取和发送的数据:

public interface IShowMessage {
    /**
     *
     * @param message Flutter -> Android
     */
    void onShowMessage(String message);

    /**
     *
     * @param message Android --> flutter
     */
    void sendMessage(String message);
}

第五步:创建点击事件,跳转页面并传值

 EditText edit = findViewById(R.id.edit);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FlutterAppActivity.start(BasicMsgChannelActivity.this,edit.getText().toString(),1);
            }
        });

这块代码有迷糊的同学请看上一章:Flutter混合开发 传递初始化数据给Android(4.2)

java代码(FlutterAppActivity类);

public class FlutterAppActivity extends FlutterActivity  implements IShowMessage{
    private BasicMessageChannelPlugin basicMessageChannelPlugin;

    public final static String INIT_PARAMS = "initParams";
    /**
     * 0 给Flutter传递初始化数据
     * 1 使用BasicMsgChannel传递数据
     */
    private static int mtype ;

    public static void start(Context context, String initParams, int type) {
        mtype = type;
        Intent intent = new Intent(context, FlutterAppActivity.class);
        intent.putExtra(INIT_PARAMS, initParams);
        context.startActivity(intent);
    }

    String mInitParam;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("szjonCreate",mtype+"");
        if (mtype == 0) {
           // 给Flutter传递初始化数据
            mInitParam = getIntent().getStringExtra(INIT_PARAMS);

        }else if(mtype == 1){
            //使用BasicMsgChannel传递数据
            basicMessageChannelPlugin = BasicMessageChannelPlugin.registerWith(getFlutterEngine().getDartExecutor(), this);
        }

    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mtype == 1) {
            String initParam = getIntent().getStringExtra(INIT_PARAMS);
            this.sendMessage(initParam);
        }

    }

    /**
     * 传递初始化参数给Flutter
     * @return
     */
    @NonNull
    @Override
    public String getInitialRoute() {
        return mInitParam == null ? super.getInitialRoute() : mInitParam;
    }

//         使用在MyApplication预先初始化好的Flutter引擎以提升Flutter页面打开速度,
//         注意:在这种模式下会导致getInitialRoute 不被调用所以无法设置初始化参数
//        @Override
//        public String getCachedEngineId() {
//            return App.ENG_INED;
//        }


    @Override
    public void onShowMessage(String message) {
        Log.i("szjonShowMessage",message);
    }

    @Override
    public void sendMessage(String message) {
        Log.i("szjsendMessage",message);
        if (basicMessageChannelPlugin == null) {
            return;
        }
        basicMessageChannelPlugin.send(getIntent().getStringExtra(INIT_PARAMS),this::onShowMessage);
    }
}

注意:
sendMessage()传递消息的方法一定要卸载onStart()方法里面

 @Override
    protected void onStart() {
        super.onStart();
        if (mtype == 1) {
            String initParam = getIntent().getStringExtra(INIT_PARAMS);
            this.sendMessage(initParam);
        }
    }

代码中的mtype是用来区分和上一章:Flutter混合开发 传递初始化数据给Android(4.2)的变量!

走到这里Android端的初始化就完成了,在简单的分析一下:

  • 在BasicMsgChannelActivity页面有一个按钮一个文本框,点击按钮调用了跳转FlutterAppActivity页面的方法**,FlutterAppActivity就是Flutter页面,因为FlutterAppActivity继承自FlutterActivity.然后在FlutterAppActivity的onCreate方法中使用工具类BasicMessageChannelPlugin**,这个工具类的作用是初始化BasicMessageChannel().在onStart()方法中调用send方法,给dart传值.

在来看看Flutter的代码吧:

第一步:初始化BasicMessageChannel()

//初始化BasicMessageChannel()
BasicMessageChannel<String> _basicMessageChannel =
      BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());
  • 参数一:对应的是android代码中的BasicMessageChannel()这个参数,必须完全以一直Flutter混合开发 BasicMessageChannel与原生android通信(4.3)
    -参数二:编码模式,Android端和Flutter端的编码模式也呀一值

第二步:使用BasicMessageChannel接受来自Native的消息,并向Native回复


  String showMessage;

  @override
  void initState() {
    //使用BasicMessageChannel接受来自Native的消息,并向Native回复
    _basicMessageChannel
        .setMessageHandler((String message) => Future<String>(() {
              setState(() {
                showMessage = 'BasicMessageChannel:' + message;
              });
              return "BasicMessageChannel收到android的消息:" + message;
            }));
    super.initState();
  }
 RaisedButton(
      onPressed: () {
      _basicMessageChannel.send("我是Flutter的数据!!!");
      },
      child: Text("发送消息给native"),
      ),
     TextField(
        onChanged: _onTextChange,
        decoration: InputDecoration(
        hintText: "请输入给原生发送的消息",
          ),
    ),
    Text("BasicMessageChannel接收android原生数据为: ${showMessage}"),

建议使用异步操作发送消息;

  void _onTextChange(value) async {
    String response;
    /**
     * 在android对应的是 reply.reply()
     */
    response = await _basicMessageChannel.send(value);
    setState(() {
      showMessage =  response ;
    });
  }

走到这里就完成了,大家肯定看的有点迷糊,我讲的不太好,还要拿文字表达出来,表达的不是很完美,还是在总结一下吧:

Android给Flutter发消息使用

BasicMessageChannel messageChannel = BasicMessageChannel(参数一,参数二,参数三);
messageChannel.send(message, callback);

Android获取Flutter发送的消息使用:

 @Override//处理Dart发来的消息
  messageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler<String>() {
            @Override
            public void onMessage(@Nullable String message, @NonNull BasicMessageChannel.Reply<String> reply) {
             reply.reply("我是android的代码reply:" + s);//可以通过reply进行回复
                Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
            }
        });
  • 这里的message就是Flutter给Android发送的消息,我用Toast显示出来了
  • 这里的reply.reply()是获取到Flutter数据之后回复Flutter的数据

Android端需要注意的就是send()方法一定要在onStart()方法里面,其实这么说也不完全对,应该是send()方法一定要在初始化BasicMessageChannel()方法后面,如果是在他前面send()会null

Flutter获取Android发送的数据使用:

//初始化BasicMessageChannel
 BasicMessageChannel<String> _basicMessageChannel =
      BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());

_basicMessageChannel
        .setMessageHandler((String message) => Future<String>(() {
              setState(() {
                //Android --> Flutter
                showMessage = 'BasicMessageChannel:' + message;
              });
              return "BasicMessageChannel收到android的消息:" + message;
            }));

这里的message就是Android给Flutter发送的数据

如果获取Flutter发送给Android数据后,Android返回的数据:

String response = _basicMessageChannel.send(value);

发送数据后,send()的返回值就是Android返回的数据

在来看看效果吧:
Flutter混合开发 BasicMessageChannel与原生android通信(4.3)

你以为走到这里就完了吗?

肯定没有呀,难道你就不想知道跳转的时候为什么有黑屏???

如何解决呢?对于我这种杠精来说,尽量要做到完美~

其实上一章:Flutter混合开发 传递初始化数据给Android(4.2)已经说过了,只需要在FlutterAppActivity中吧

  @Override
        public String getCachedEngineId() {
            return App.ENG_INED;
        }

这段代码写上就好,getCachedEngineId()会导致getInitialRoute() 不被调用所以无法设置初始化参数,但是BasicMessageChannel()他可管不着~

来康康最终效果吧:
Flutter混合开发 BasicMessageChannel与原生android通信(4.3)
这个通信我搞了好几天,出了好多错,最终终于完成了这个,在接下来的几篇中我会介绍其他两个通信方式~

完整代码

上一章:Flutter混合开发 传递初始化数据给Android(4.2)

原创不易,您的点赞就是对我最大的支持,留下您的点赞吧~

上一篇:TypeError: Cannot read property ‘__ob__’ of undefined


下一篇:K8S etcd参数优化