项目背景
本人前段时间负责公司一个Android SDK开发工作,并制定相关对接文档,开发完成之后,与一些合作方对接。
但是某些公司的项目是用Flutter编写,需要编写对应SDK的flutter插件提供给对方,啥?flutter?不会呀!
哈哈哈,但是作为无所畏惧的程序员来说,能认怂么,当然是不能(硬着头皮上)
最终一周内,将对应的Flutter插件开发完成并交付,以下是我学习开发Android SDK Flutter插件的总结
总结
万事都是开头难,但是在flutter大环境已经很成熟的情况下,完全不用慌。
第一步是弄明白flutter如何调用android原生代码
推荐一篇博客,还有很多好的博客可以自行搜索:https://developer.aliyun.com/article/697792
还有一个我认为写的比较好的开源flutter插件,可以拿来借鉴:https://github.com/OpenFlutter/Pangolin
安装环境
在 Windows 操作系统上安装和配置 Flutter 开发环境
创建插件项目
环境搭建完毕之后,打开Android studio ->File->new->new Flutter Project ->选择Flutter Plugin
创建成功之后,打开项目,看到这样的目录结构,我们更多关心的是写Android插件地方
编写插件
我们打开android目录下的FlutterPlugin文件,这是项目自己为我们生成的插件Demo,在这个窗口下,我们不难发现很多android原生代码会爆红,虽然不影响编译,
但是看着很难受,我们选择右上角Open for Editing in Android Studio,选择在另一个窗口打开就没事了。
我们看下它为我们自动生成的插件代码,这里是Kotlin语法,它实现了FlutterPlugin,MethodCallHandler
public class FlutterPlugin: FlutterPlugin, MethodCallHandler {
...
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_plugin")
channel.setMethodCallHandler(this);
}
...
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "flutter_plugin")
channel.setMethodCallHandler(FlutterPlugin())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
其中,MethodCallHandler这个接口实现的onMethodCall(MethodCall call, MethodChannel.Result result)
- 用于接受消息,
这里接收的消息就是来自于Flutter项目,call是消息内容,它有两个成员变量String类型的call.method
表示调用的方法名,
Object 类型的call.arguments
表示调用方法所传递的入参。
开始编码,在android的目录下的build.gradle加入我们自己Android项目的SDK依赖:
implementation 'com.jayxu.android:***Sdk:1.0.1'
在自动生成的插件代码的基础上,进行我们项目的开发,在插件里,我还需要拿到当前的activity对象,用于原生Activity之间的跳转,
所以我还实现了ActivityAware,具体使用,请看代码(Kotlin写法)。
public class PedesxpluginPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
...
private lateinit var channel: MethodChannel
private lateinit var applicationContext: Context
private lateinit var activity: Activity
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
onAttachedToEngine(flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
}
fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
this.applicationContext = applicationContext
channel = MethodChannel(messenger, "pedesxplugin")
channel.setMethodCallHandler(this)
}
fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger, activity: Activity) {
this.applicationContext = applicationContext
channel = MethodChannel(messenger, "pedesxplugin")
channel.setMethodCallHandler(this)
this.activity = activity
}
...
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val instance = PedesxpluginPlugin()
instance.onAttachedToEngine(registrar.context(), registrar.messenger(), registrar.activity())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else if (call.method == "registerPedesx") {
val appId: String? = call.argument("appId")
val shelf_id: String? = call.argument("shelf_id")
val csj_appId: String? = call.argument("csj_appId")
val csj_video_id: String? = call.argument("csj_video_id")
//初始化SDK
PedesxUtil.init(applicationContext, appId, shelf_id, csj_appId, csj_video_id)
} else if (call.method == "registerPedesxUser") {
val uid: String? = call.argument("uid")
val oaid: String? = call.argument("oaid")
//初始化SDK的User信息
PedesxUtil.initUser(applicationContext, uid, oaid)
} else if (call.method == "startPedesxVideoActivity") {
//跳转我们自己SDK的界面1
ActivityUtils.startActivity(activity, Demo1Activity::class.java)
} else if (call.method == "startPedesxWelfareActivity") {
//跳转我们自己SDK的界面2
ActivityUtils.startActivity(activity, Demo2Activity::class.java)
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onDetachedFromActivity() {
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
this.activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
}
}
到此Android 插件部分就全部写好了,是不是很简单。
如何使用插件
插件写好了,那么Flutter项目如何使用我们的插件呢,我们的工作还在继续
回到flutter插件项目,我们在lib目录下,新建一个dart文件,以下为代码示例,没有写全,基本的写法都差不多,这个文件主要是将Android插件部分的代码,再次封装给我们的example项目使用。
...
MethodChannel _channel = MethodChannel('pedesxplugin')
..setMethodCallHandler(_methodHandler);
StreamController<BasePedesxResponse> _pedesxResponseEventHandlerController =
new StreamController.broadcast();
Stream<BasePedesxResponse> get pedesxResponseEventHandler =>
_pedesxResponseEventHandlerController.stream;
Future _methodHandler(MethodCall methodCall) {
var response =
BasePedesxResponse.create(methodCall.method, methodCall.arguments);
_pedesxResponseEventHandlerController.add(response);
return Future.value();
}
Future<bool> initPedesxSdk({
@required String appId, //SDK APPId
@required String shelf_id, //注释1
@required String csj_appId, //注释2
@required String csj_video_id, //注释3
}) async {
return await _channel.invokeMethod("registerPedesx", {
"appId": appId,
"shelf_id": shelf_id,
"csj_appId": csj_appId,
"csj_video_id": csj_video_id,
});
}
Future<bool> initPedesxSdkUser({
@required String uid,
@required String oaid,
}) async {
return await _channel
.invokeMethod("registerPedesxUser", {"uid": uid, "oaid": oaid});
}
Future<bool> startPedesxVideoActivity() async {
return await _channel.invokeMethod("startPedesxVideoActivity");
}
Future<bool> startPedesxWelfareActivity() async {
return await _channel.invokeMethod("startPedesxWelfareActivity");
}
...
这里我们将Android插件的部分代码与flutter调用插件的部分代码进行比较,有没有很清晰
android 插件代码:
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
...
if (call.method == "registerPedesx") {
val appId: String? = call.argument("appId")
val shelf_id: String? = call.argument("shelf_id")
val csj_appId: String? = call.argument("csj_appId")
val csj_video_id: String? = call.argument("csj_video_id")
//初始化SDK
PedesxUtil.init(applicationContext, appId, shelf_id, csj_appId, csj_video_id)
}
...
}
-----------------------这是一条分界线---------------------------
flutter代码:
Future<bool> initPedesxSdk({
@required String appId, //SDK APPId
@required String shelf_id, //货架ID
@required String csj_appId, //穿山甲APPID
@required String csj_video_id, //穿山甲激励视频ID
}) async {
return await _channel.invokeMethod("registerPedesx", {
"appId": appId,
"shelf_id": shelf_id,
"csj_appId": csj_appId,
"csj_video_id": csj_video_id,
});
}
最终example项目调用插件的部分dart代码:
_initPedesxSdk() async{
await Pedesxplugin.initPedesxSdk(
appId: "***",
shelf_id: "***********",
csj_appId: "*****",
csj_video_id: "*******",
);
}
因为部分代码,涉及项目隐私,不能全部贴出,有问题童鞋的可以去看看https://github.com/OpenFlutter/Pangolin这个项目,强烈推荐。