上一小节呢,我们把一个android传统工程改造成了flutter混编工程,在flutter工程中想要调用android原生的方法或者功能,那肯定是离不开plugin工程的,下面,我们就首先来讲解一下我们本节课的第一部分内容,plugin工程。
那什么是plugin工程呢,我们创建一个来看一看即可,下面是我创建的一个网络plugin工程,用来实现flutter工程通过此plugin完成网络请求,工程如下:
通过图中,我们可以看到,与我们之前创建的flutter app的目录结构及组成完全一样,所以plugin工程本质上就是一个flutter工程,那我们创建好的plugin工程中的各部分是如何与flutter工程配合使用的呢,下面我们就一部分一部分来分析这个问题。
首先,我们来看一下android这部分,很显然,这是一个android的库工程,他的核心要实现的就是我们要对flutter提供的功能,下面简单带大家看一下,我基于okhttp实现的这个网络请求库。
大家可以看到,就是个纯的android库工程,IOS中则是同样实现网络请求的IOS Library.最后一部分lib中,则是我们要对flutter暴露的接口。代码如下:
import 'dart:async';
import 'package:flutter/services.dart';
class HttpPlugin {
static const MethodChannel _channel = const MethodChannel('http_plugin');
static Future<String> post(payload) async {
final String result = await _channel.invokeMethod('post', payload);
return result;
}
static Future<String> get(payload) async {
final String result = await _channel.invokeMethod('get', payload);
return result;
}
}
大家来看代码,也很简单,就是对外提供了一个get方法和一个post方法,到此这三个核心部分分别的作用,我们就分析完毕了,下面我们来看一下,他们是如何被使用的,我们来看一下这个plugin中的android library.这个子工程是如何被flutter使用的呢,其实,这个plugin工程中的andorid library被添加到了flutter工程中的Flutter module中,通过我们之前分析过的include_flutter我们就可以知道,我们再来看一下这块代码:
// Generated file. Do not edit.
def scriptFile = getClass().protectionDomain.codeSource.location.path
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, 'android/Flutter')
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
//为android工程引入所有的plugin android工程,以供Flutter module去依赖
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
gradle.getGradle().projectsLoaded { g ->
g.rootProject.afterEvaluate { p ->
p.subprojects { sp ->
if (sp.name != 'flutter') {
sp.evaluationDependsOn(':flutter')
}
}
}
}
重点看最后这一段即可,将所有子工程的android工程下的library都作为Flutter工程的依赖。所以到这里,我们就可以知道,Flutter这个工程中就会依赖所有的plugin中的android module.这样,我们就可以知道,所有plugin中的android library会被打到我们的apk中,iOS部分的原理是一样的,而最后一部lib,则会被根工程下的lib工程所依赖,在我们的pubspec.ymal文件中。综上所述,可以发现,我们的一个plugin工程,会被分隔成几部分去单独依赖。下面我们通过一个图来更加直观的说明:
可以看到,我们plugin工程中的每部分都被拆分开了,分别依赖到了根工程下的android工程的Flutter子工程和主工程的lib工程,iOS的处理逻辑与android的完全一样。这是plugin工程的一个特别之处,这样,我们从工程上知道plugin代码被依赖到了哪里去,下面,我们再从代码的角度来看一下,plugin中的代码是如何被调用的。下面,先不看一下我们的例子中是如何调用的。
//第一步,应用层调用,因为主工程中的flutter已经依赖了http_plugin中的lib,所以可以使用HttpPlugin这个类,
Future<HomeData> getHomeData() async {
//模拟请求参数
var demoData = {
"url": "http://api.imooc.com/get_home_data",
};
final String result = await HttpPlugin.post(demoData);
return HomeData.fromJson(json.decode(result));
}
//第二步,看一下HttpPlugin的实现,可见核心是一个MethodChannel,通过MethodChannel的invokeMethod方法完成对原生方法的调用
class HttpPlugin {
static const MethodChannel _channel = const MethodChannel('http_plugin');
static Future<String> post(payload) async {
final String result = await _channel.invokeMethod('post', payload);
return result;
}
static Future<String> get(payload) async {
final String result = await _channel.invokeMethod('get', payload);
return result;
}
}
//第三步,看一下原生代码的实现
/** HttpPlugin */
public class HttpPlugin implements MethodCallHandler {
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "http_plugin");
channel.setMethodCallHandler(new HttpPlugin());
}
@Override public void onMethodCall(MethodCall call, final Result result) {
String method = call.method;
String url = call.argument(HttpConstant.PARAM_URL);
Map params = null;
if (call.hasArgument(HttpConstant.PARAM_PARAMS)) {
params = call.argument(HttpConstant.PARAM_PARAMS);
}
switch (method) {
case HttpConstant.METHOD_POST:
handleFlutterPostRequest(url, params, result);
break;
case HttpConstant.METHOD_GET:
handleFlutterGetRequest(url, params, result);
break;
default:
result.notImplemented();
}
}
从代码中,我们可以看到,最核心的一处就是这个MethodChannel,因为正是通过这个MethodChannel这个管道,让我们完成了从flutter代码到java代码的调用。而这个MehodChannel的原理又是怎么样的,了解过后台mq的同学可以很容易的理解,就是建立一个通信管道,然后flutter发出要调用的方法和参数,管道将这些参数传到java层,然后调用Java层的代码同时返回调用结果。
好了,到这里我们就从代码和工程两个角度解析了flutter如何通过plugin工程完成native代码调用。工程角度让大家明白,各个子工程的代码最终去了哪里,在代码角度则说明具体是如何完成调用。
···························
欢迎关注课程:
企业级Android应用架构设计与开发(新课限时优惠ing~)