原生通信系列

1. Flutter 调用 Android
2. Android 通知 Flutter
3. Flutter 调用 iOS
4. iOS 通知 Flutter

项目地址


在 flutter 开发中一定会有需要和原生通信的情况

第一篇介绍的是 flutter 调用 Android

这篇是给刚刚入门或者刚刚接触原生调用的朋友们使用的,老鸟们或者英文强的朋友还是直接看官方文档比较好

创建项目

一般来说建议使用一个 plugin 作为一个单独的项目来将业务逻辑独立出去

我这里使用命令行创建,事实上一律建议使用命令行创建,因为信息更加可见一些

flutter create --template plugin  battle_power
cd battle_power

前面都是固定格式,最后面那个是插件名,根据你自己的需要来修改

创建好的截图如下 图片

和开发 package 的时候不同,开发 plugin 需要打开 example/android 目录

图片

使用快捷的方式,或自己通过 Android Studio 的 open 打开项目

图片 等待完成

图片 接着就可以开始开发了,

默认生成了一个 java 文件,可以称之为插件的主文件

流程图

图片

乱画的..随便看看就好

android 端

package com.example.battlepower;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;

/** BattlePowerPlugin */
public class BattlePowerPlugin implements MethodCallHandler {
  /** Plugin registration. */
  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "battle_power"); //这里对应dart端的 MethodChannel
    channel.setMethodCallHandler(new BattlePowerPlugin());
  }

  @Override
  public void onMethodCall(MethodCall call, Result result) {
    if (call.method.equals("getPlatformVersion")) { // 对应dart端 invoceMethod
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }
}

当然如果你对于 kotlin 比较喜欢也可以使用 kotlin 进行插件开发

如果你不会 Java/Kotlin ,甚至不会原生开发,只能说你进入了一个全新的领域,希望你一切都好吧…,建议你先系统的学习下 android 再来

dart 端

import 'dart:async';

import 'package:flutter/services.dart';

class BattlePower {
  static const MethodChannel _channel =
      const MethodChannel('battle_power');

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

dart 端和 java 端是一一对应的

交互

调用

由 dart 端使用_channel.invokeMethod调用,这个方法的参数是方法名和一个可选参数

返回值是一个Future<dynamic> 因为 java 端的调用是不可预知的,所以是异步的

换句话说,无论结果是对是错你在 java 端都需要有返回值,否则 dart 端会挂起

查看 java 端文档

图片

可选参数

可选参数是 dart 中的一个语法特点,方法的签名中通过[]包裹的为匿名可选参数,通过{}包裹的为命名可选参数

而 channel 中的这个方法的完整实现如下

结果返回

查看文档

图片

不同于传统的同步方法调用,java 端需要使用result.success(object)来返回一个结果给 dart

使用result.notImplemented();来表示未定义的方法,dart 端会接收到一个异常

返回一个错误result.error(String code,String msg,Throwable throwable)

调用过程分析

  Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
    assert(method != null);
    final dynamic result = await BinaryMessages.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null)
      throw MissingPluginException('No implementation found for method $method on channel $name');
    return codec.decodeEnvelope(result);
  }

method 是必填参数, 这个和 java 端的监听一一对应

第二个是一个可选一个参数,这个参数可传递任意参数,但只能有一个,然后会通过内部的 codec 进行编码后传递给 native 端,这个 codec 对象是创建MethodChannel时创建的 图片 有一个默认类型为StandardMethodCodec,这个类型后面会解析,先略过


  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "battle_power");
    channel.setMethodCallHandler(new BattlePowerPlugin());
  }

在 java 端使用这类代码来注册插件


  @Override
  public void onMethodCall(MethodCall call, Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }

这里的代码是用来监听回调的,当 dart 调用invokeMethod这里会被触发

比如getPlatformVersion是一个没有传参数的方法, 直接返回了 Android os 对应的版本号

这样 dart 在 java 端result.success调用后就会 返回版本号

StandardMethodCodec 解析

这个类也是在两端都有的一个类, 通常来说就是作为发送端负责编码, 作为接受端负责解码

作为一个内置类,其中做了一些”自动”的转码过程,将原生类型映射为 dart 类型 或者反过来映射

查看文档

图片

这里代表了基础的映射关系 也就是说 你在 invokeMethod 时,参数会被映射为右边的原生类型 如果这里没有的类型就是不支持的类型,建议不要使用,否则会抛出无法解码的异常

当然有正向映射,也会有对应的反向映射,也就是说, 你在 java 中作为结果返回的类型在 dart 端也会被映射为 dart 对应类型

当然这个编解码器也可以自定义,基于二进制扩展即可,不过这个不属于基础的应用篇,暂时先不做

自定义

前面是自动生成的方法和签名

现在自己添加一个带参数的方法

dart

  static Future<int> requestNativeAdd(int x, int y) async {
    int result = await _channel.invokeMethod('add', {"x": x, "y": y});
    return result;
  }

java

    @Override
    public void onMethodCall(MethodCall call, Result result) {
        switch (call.method) {
            case "getPlatformVersion":
                result.success("Android " + android.os.Build.VERSION.RELEASE);
                break;
            case "add":
                Integer x = call.argument("x");
                Integer y = call.argument("y");
                if (x != null && y != null) {
                    result.success(x + y);
                } else {
                    result.error("1", "不能为空", null);
                }
                break;
            default:
                result.notImplemented();
                break;
        }
    }

方法很简单,传入一个 x 一个 y,然后 java 端加完返回回来

这样一个简单的自定义就完成了


你以为就这样结束了?

请看第二篇 android 通知 flutter