原文地址: Flutter学习(9)——Flutter插件实现(Flutter调用Android原生) | Stars-One的杂货小窝

步骤说明

1.打开android文件夹

flutter中有个ios和android的文件夹,分别对应的Android和Ios的原生代码

Flutter学习(9)——Flutter插件实现(Flutter调用Android原生-LMLPHP

我们想要实现FLutter调用原生代码,在里面写原生代码即可

在android文件夹中,新建有个类,Android可以选择Java或者是Kotlin代码编写即可

android目录结构其实就是常见的Android项目目录

Flutter学习(9)——Flutter插件实现(Flutter调用Android原生-LMLPHP

然后使用Android Studio打开,右键菜单,选择flutter -> Open Android module in Android Studio

Flutter学习(9)——Flutter插件实现(Flutter调用Android原生-LMLPHP

之后可以看到已经像Android开发一样打开了一个项目(当然,这里你也可以自己使用Android Studio去选择那个android文件夹,将其当做项目打开即可)

2.新建Activity

此Activity需要继承FlutterActivity,并重写configureFlutterEngine方法,在此方法中进行插件的初始化

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {

        //插件实例的注册...

        //这个是必写,别删除!!
        GeneratedPluginRegistrant.registerWith(flutterEngine);
    }
}

那么这里需要插件的实例,插件的实例怎么来呢?其实就是自己写个类,然后实现Flutter提供的FlutterPlugin接口

3.原生代码编写

新建一个类,实现FlutterPlugin接口,创建一个MethodChannel对象,利用此对象的setMethodCallHandler方法设置方法处理回调,里面通过判断方法名来调我们原生写的方法

public class MyTestPlugin implements FlutterPlugin {
    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
        //可以利用binding对象获取到Android中需要的Context对象
        //Context applicationContext = binding.getApplicationContext();

        //设置channel名称,之后flutter中也要一样
        MethodChannel channel = new MethodChannel(binding.getFlutterEngine().getDartExecutor(), "test-plugin");

        //把当前的MethodCallHandler设置
        channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
                String method = call.method;
                if (method.equals("getText")) {

                    //调用原生的方法,这里为了方便,我就把方法写在当前类了
                    String str = getText();

                    //将结果返回给flutter
                    result.success(str);

                    //这里也有error的方法,可以看情况使用
                    //result.error("code", "message", "detail");
                } else {
                    //Flutter传过来id方法名没有找到,就调此方法
                    result.notImplemented();
                }
            }
        });
    }

    private String getText() {
        return "hello world";
    }
    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {

    }
}

如果你想要一个Application的context上下文对象,可以在onAttachedToEngine()方法中使用binding的getApplicationContext()方法获取,如下代码

Context applicationContext = binding.getApplicationContext();

如果是想要获取当前Activity的context对象,可以让当前类实现ActivityAware接口,不过略显繁琐,一般用Application的context对象应该可以满足大部分要求了,看情况选择吧

private Context context;
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
    context = binding.getActivity();
}

@Override
public void onDetachedFromActivityForConfigChanges() {

}

@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {

}

@Override
public void onDetachedFromActivity() {
context = null;
}

4.Activity中注册插件

之前在第二步中的Activity中,补上注册的代码

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        //插件实例的注册...
        flutterEngine.getPlugins().add(new MyTestPlugin());

        GeneratedPluginRegistrant.registerWith(flutterEngine);
    }
}

5.flutter中插件初始化和封装

在flutter中创建一个文件,文件名和class名任意,只是用来声明和初始化上述的Java类

class Md5Plugin{
    //注意,这里的名称需要和Android原生中定义的一样
    static const MethodChannel _channel = MethodChannel("apk_md5");

    static Future<String> getMd5() async{
        //传递一个方法名,即调用Android的原生方法
        return await _channel.invokeMethod("getMd5");
    }

}

还记得之前写的方法名的判断吗?这里就是传一个方法名,之后就会触发回调,之后即可得到返回结果

6.flutter页面中使用插件

之后在对应的page文件对应代码处中调用即可

Md5Plugin.getMd5().then(value=>{
    //相关操作
});

如果想使用同步代码,可以这样写

var result = Md5Plugin.getMd5()

传参补充

上述的例子中,并没有涉及到传参,这里再补充讲解下我自己的研究使用

这里只讲Flutter如何给Android原生传参

FLutter中调用方法(即上述的第五步操作):

class Md5Plugin{
    //注意,这里的名称需要和Android原生中定义的一样
    static const MethodChannel _channel = MethodChannel("apk_md5");

    static Future<String> getMd5() async{
        //传字符串给Android
        var param = "hello";

        //传递一个方法名,即调用Android的原生方法
        //注意这里的第二个参数
        return await _channel.invokeMethod("getMd5",param);
    }
}

Android中的接收(上述的第三步):

在判断方法名之后,即可通过对应的方法获取数据(需要类型转换)

public class MyTestPlugin implements FlutterPlugin {
    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
        ...

        //把当前的MethodCallHandler设置
        channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
                String method = call.method;
                if (method.equals("getText")) {

                    //注意这里的获取数据(强转)
                    String packageName = (String)call.arguments;

                    省略...
                } else {
                    //Flutter传过来id方法名没有找到,就调此方法
                    result.notImplemented();
                }
            }
        });
    }
   ...
}

上述的代码只是传单个数据,如果是要穿多个数据要怎么办呢?

由于invokeMethod()方法里只支持传单个数据,所以我们需要传map或是json格式的数据给到Android原生

Flutter发送数据:

var param = {"myKey":"hello"}

//传递一个方法名,即调用Android的原生方法
//注意这里的第二个参数
return await _channel.invokeMethod("getMd5",param);

Android接收数据:


String packageName = call.argument("myKey");

这里有点要注意,call中有个arguments属性和arguments()方法,如下图

Flutter学习(9)——Flutter插件实现(Flutter调用Android原生-LMLPHP

flutter中传过来的数据是map或json的,就得用arguments()来获取参数据;否则就是使用arguments属性

当然,如果传过来的数据是map或json类型,call提供了一个方便快捷的方法,我们可以直接使用argument(key)来直接获取key对应的数值(注意这里也需要类型强转,注意类型需要对应)

最后这里给出Flutter与Java的对应的类型表:

代码参考

参考

12-25 21:52