Flutter插件(Plugin)开发 - Android视角,okhttp读取信息kotlin


// 上报播放器的状态的EventChannel
EventChannel status_channel = new EventChannel(registrar.messenger(), “flutter_music_plugin.event.status”);
status_channel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
// 把eventSink存起来
plugin.setStateSink(eventSink);
}

@Override
public void onCancel(Object o) {

}
});
//上报播放进度的EventChannel
EventChannel position_channel = new EventChannel(registrar.messenger(), “flutter_music_plugin.event.position”);
position_channel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
// 把eventSink存起来
plugin.setPositionSink(eventSink);
}

@Override
public void onCancel(Object o) {

}
});
}

注册完以后我们就拿到了两个EventSink,当需要的时候就可以用需要的EventSink给Flutter App上报事件了。

Native这边还有一环是打开本地音频文件的操作,这里我偷个懒,用发送Intent的方式来让用户在第三方app中选择音频文件。如果是在Activity中我会用startActivityForResultonActivityResult来获取音频文件,可是我们现在开发的是一个插件,不是Activity怎么办?

回想一下我们用来注册插件的静态函数registerWith,入参的类型是Registrar。看看它里面都有啥?

public interface Registrar {
//返回 Host app的Activity
Activity activity();
//返回 Application Context.
Context context();
//返回 活动Context
Context activeContext();
//返回 BinaryMessenger 主要用来注册Platform channels
BinaryMessenger messenger();
//返回 TextureRegistry,从里面可以拿到SurfaceTexture
TextureRegistry textures();
//返回 当前Host app创建的FlutterView
FlutterView view();
//返回Asset对应的文件路径
String lookupKeyForAsset(String var1);
//返回Asset对应的文件路径
String lookupKeyForAsset(String var1, String var2);
//插件对外发布的一个"值"
PluginRegistry.Registrar publish(Object var1);
//注册权限相关的回调
PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener var1);
//注册ActivityResult回调
PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener var1);
//注册NewIntent回调
PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener var1);
//注册UserLeaveHint回调
PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener var1);
//注册View销毁回调
PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener var1);
}

。。。简直就是个宝库啊。里面的中文注释我是照官方英文文档翻译的,有些方法的用途也不太明确,有待大家的发掘。本例中目前只需要两个方法,调用activity()就拿到Host App的Activity。addActivityResultListener设置处理返回结果的回调。代码如下:

// 实现 PluginRegistry.ActivityResultListener
public class FlutterMusicPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {

private Activity mActivity;
// 加个构造函数,入参是Activity
private FlutterMusicPlugin(Activity activity) {
// 存起来
mActivity = activity;
}

public static void registerWith(Registrar registrar) {
//传入Activity
final FlutterMusicPlugin plugin = new FlutterMusicPlugin(registrar.activity());

// 注册ActivityResult回调
registrar.addActivityResultListener(plugin);
}

@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {

case “open”:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(“audio/*”);
mActivity.startActivityForResult(intent, REQUEST_CODE_OPEN);
break;

}
}

@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_OPEN && resultCode == RESULT_OK) {
Uri uri = data.getData();
if (uri != null) {
// 拿到音频文件uri,开始播放。
play(uri);
} else {
mStateSink.error(“ERROR”, “invalid media file”, null);
}
return true;
}
return false;
}
}

我们改造一下FlutterMusicPlugin, 增加以Activity为入参的构造函数,在静态函数registerWith里实例化的时候传入Host app的Activity。同时注册自身来处理onActivityResult回调。 在onMethodCall方法内"open"下启动第三方选择音频文件的页面。当用户选好了某首歌返回的时候,插件这边就会拿到音频文件uri,并开始播放。

至此,Native端的逻辑就完成了,我们再来看看插件的Flutter端怎么做。

插件Flutter端

IDE在lib目录下会帮你自动生成flutter_music_plugin.dart文件,这个就是插件的Flutter代码所在了,内容比较简单,就是对我们定义好的Platform channels的包装。直接上代码:

typedef void EventHandler(Object event);

class FlutterMusicPlugin {
static const MethodChannel _channel = const MethodChannel(‘flutter_music_plugin’);
static const EventChannel _status_channel = const EventChannel(‘flutter_music_plugin.event.status’);
static const EventChannel _position_channel = const EventChannel(‘flutter_music_plugin.event.position’);

static Future open() async {
await _channel.invokeMethod(‘open’);
}

static Future pause() async {
await _channel.invokeMethod(‘pause’);
}

static Future start() async {
await _channel.invokeMethod(‘start’);
}

static Future getDuration() async {
int duration = await _channel.invokeMethod(‘getDuration’);
return Duration(milliseconds: duration);
}

static listenStatus(EventHandler onEvent, EventHandler onError) {
_status_channel.receiveBroadcastStream().listen(onEvent, onError: onError);
}

static listenPosition(EventHandler onEvent, EventHandler onError) {
_position_channel.receiveBroadcastStream().listen(onEvent, onError: onError);
}
}

插件Example App

除了自身的逻辑之外,一个插件还要有示例应用来演示其API怎么使用,同时,示例应用也是我们开发,调试,验证插件的必备工具。本例中的示例可参考example目录下的main.dart文件。使用插件API的主要逻辑都在State中。简要代码如下

@override
void initState() {
super.initState();
// 在这里注册EventChannles,参数传入响应的回调
FlutterMusicPlugin.listenStatus(_onPlayerStatus, _onPlayerStatusError);
FlutterMusicPlugin.listenPosition(_onPosition, _onPlayerStatusError);
}

// 根据播放状态调用pause或start
void _playPause() {
switch (_status) {
case “started”:
FlutterMusicPlugin.pause();
break;
case “paused”:
case “completed”:
FlutterMusicPlugin.start();
break;
}
}
// 打开媒体文件
void _open() {
FlutterMusicPlugin.open();
}
// MediaPlayer出错事件处理
void _onPlayerStatusError(Object event) {
print(event);
}
// MediaPlayer状态改变事件处理
void _onPlayerStatus(Object event) {
setState(() {
_status = event;
});
if (_status == “started”) {
_getDuration();
}
}
// 获取音频时长
void _getDuration() async {
Duration duration = await FlutterMusicPlugin.getDuration();
setState(() {
_duration = duration;
});
}
// 播放进度事件处理
void _onPosition(Object event) {
Duration position = Duration(milliseconds: event);
setState(() {
_position = position;
});
}

发布

当你的插件开发测试完成以后,你就可以把你的插件发布出去了。 发布之前,先检查pubspec.yaml, README.mdCHANGELOG.md这几个文件的内容是否完整正确。然后运行下面这个命令检查插件是否可以发布。

$ flutter packages pub publish --dry-run

如果有问题存在的话,会在终端输出相关信息,你需要据此做出修改直到返回成功。具体遇到的问题可以参考官方文档

最后去掉--dry-run以后再运行以上命令。

最后:学习总结——Android框架体系架构知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。

下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)
Flutter插件(Plugin)开发 - Android视角,okhttp读取信息kotlin_第1张图片

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!

小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)
[外链图片转存中…(img-Ln5EiKqv-1643530781277)]

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

你可能感兴趣的:(程序员,架构,移动开发,android)