然后一路 "Next"下去。完成后的工程结构如下:
整个工程包含4个主目录,android和ios目录下是对应Native代码。lib目录下是插件的Flutter端代码。example目录下是个完整的Flutter App。这个App示范怎么使用你开发的Flutter插件。在本例中,example在手机上跑起来就是上面那个播放器的样子。
插件Native端
照例我们先来看看Native端怎么做,在android目录下,IDE会为你生成一个XXXPlugin.java的文件。打开打开以后可以看到下面这样的示例代码:
/** FlutterMusicPlugin /
public class FlutterMusicPlugin implements MethodCallHandler {
/* Plugin registration. */
public static void registerWith(Registrar registrar) {
final FlutterMusicPlugin plugin = new FlutterMusicPlugin();
final MethodChannel channel = new MethodChannel(registrar.messenger(), “flutter_music_plugin”);
channel.setMethodCallHandler(plugin);
}
@Override
public void onMethodCall(MethodCall call, Result result) {
// TODO implement method call handler
}
}
里面有一个实现了MethodCallHandler
的类FlutterPlugin
和一个静态函数registerWith
。在这个静态函数里,new了一个MethodChannel
,然后把FlutterPlugin的实例设置给了这个MehodChannel。换句话说,你的插件里的那些个MethodChannel
,EventChannel
都是通过这个函数注册到Host App的。这样Flutter端在调用的时候才能找到对应的channel。接下来我们要做的就是重写onMethodCall
这个函数,把之前定义好的媒体播放的API在这里做路由:
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {
case “pause”:
// 暂停
mMediaPlayer.pause();
break;
case “start”:
// 开始播放
mMediaPlayer.start();
break;
case “open”:
//TODO 打开本地音频文件
break;
case “getDuration”:
// 获取音频时长
if (mMediaPlayer != null) {
result.success(mMediaPlayer.getDuration());
} else {
result.error(“ERROR”, “no valid media player”, null);
}
break;
default:
result.notImplemented();
break;
}
}
具体本地MediaPlayer的操作就不细说了,大家可以去看源码。MethodChannel就添加完了。此外我们还需要上报播放器的状态和播放时的进度,这就需要在registerWith
里再注册两个EventChannel了
public static void registerWith(Registrar registrar) {
…
// 上报播放器的状态的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中我会用startActivityForResult
和onActivityResult
来获取音频文件,可是我们现在开发的是一个插件,不是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;
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/0d8594963f03fa00df896784c166acf0.jpeg)
学习路线+知识梳理
花了很长时间,就为了整理这张详细的知识路线脑图。当然由于时间有限、能力也都有限,毕竟嵌入式全体系实在太庞大了,包括我那做嵌入式的同学,也不可能什么都懂,有些东西可能没覆盖到,不足之处,还希望小伙伴们一起交流补充,一起完善进步。
这次就分享到这里吧,下篇见。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
2024/03/13/H4lCoPEF.jpg" />
学习路线+知识梳理
花了很长时间,就为了整理这张详细的知识路线脑图。当然由于时间有限、能力也都有限,毕竟嵌入式全体系实在太庞大了,包括我那做嵌入式的同学,也不可能什么都懂,有些东西可能没覆盖到,不足之处,还希望小伙伴们一起交流补充,一起完善进步。
这次就分享到这里吧,下篇见。