最近一个项目采用Flutter来开发,作为一个OC原生程序猿,也是心里没底,摸石头过河。结合各方文档趟了一把坑,把实际项目中遇到的坑记录一下。
附上极光的sdk地址:https://github.com/jpush/jpush-flutter-plugin
集成方法上面也有,该配证书配证书,该导依赖导依赖,就不赘述了。说说遇到的坑吧。
补充一句,我在写文章的时候 JPush 是 0.3.0.
一、接收推送
未购买付费服务:由于系统机制不一样,当APP被杀死情况下,Android收不到,但是Android启动APP之后,能收到延迟的推送。Android退出不杀进程,依然能收到推送。IOS任何时候均能收到推送。
大概原理就是,Android是通过极光的通信接受推送,需要APP进程存在才能收到推送。IOS是通过apns(和原生一样)。
购买付费服务(未作真实测试,仅供参考):Android能通过 小米、华为等自家平台完成推送。
二、点击推送
重点内容来了,想要实现效果是,点击推送消息跳转到一个指定的页面。
jpush.applyPushAuthority(
new NotificationSettingsIOS(sound: true, alert: true, badge: true));
try {
jpush.addEventHandler(
// 接收通知回调方法。
onReceiveNotification: (Map<String, dynamic> message) async {
},
// 点击通知回调方法。
onOpenNotification: (Map<String, dynamic> message) async {
},
// 接收自定义消息回调方法。
onReceiveMessage: (Map<String, dynamic> message) async {
}, );
} on PlatformException {
debugPrint('Failed to get platform version.');
}
);
两种情况:
1 APP活跃,收到消息,点击正常执行。校验登录情况,是否允许push到指定页面。
void jump() {
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => NotificationsPage())
);
}
2 APP被杀死,IOS能收到,点击消息不执行。Android由于收不到消息,不存在这个情况。如果是付费服务,收到推送消息,理论上能执行,没有实际测试过。
现在问题就只有一个,IOS通过消息启动APP,怎么能执行跳转。最后查阅了文档,看到了一个方法。
///
/// iOS Only
/// 点击推送启动应用的时候原生会将该 notification 缓存起来,该方法用于获取缓存 notification
/// 注意:notification 可能是 remoteNotification 和 localNotification,两种推送字段不一样。
/// 如果不是通过点击推送启动应用,比如点击应用 icon 直接启动应用,notification 会返回 @{}。
/// @param {Function} callback = (Object) => {}
///
Future<Map<dynamic, dynamic>> getLaunchAppNotification() async {
print(flutter_log + "getLaunchAppNotification:");
final Map<dynamic, dynamic> result = await _channel.invokeMethod('getLaunchAppNotification');
return result;
}
于是就有了一个思路。
APP启动的时候创建一个单例,单例需要缓存主视图的context,以便于push时候使用,比如 登录后的home页面的context,点击消息时,由home页面push到Tag页面。同时需要做个标记,记录点击事件,是否已经被执行了。而点击通知是需要将标记状态做个更改。如果是IOS消息启动,直接执行push。
class TaskNotifcation {
int read = 0;
BuildContext _context;
/// 实现单例
/// .........
///
// 点击时使用
void checkNotifcation() {
if (read == null || read == 0) {
read = 1;
} else {
read++;
}
if (Platform.isIOS) {
jpush.setBadge(0);
}
jump();
}
// 主视图加载完成时候,赋值获取context
void runTask(BuildContext context) async {
if (_context == null) {
_context = context;
if (Platform.isIOS) {
final map = await jpush.getLaunchAppNotification();
if (map != null && map.isNotEmpty) {
read = 1;
}
}
jump();
}
}
void jump(){
// TODO
}
}
jump也要做相应的验证,同时把状态复位。延迟是为了动画执行,避免一闪而过(其实没什么用)。
void jump() {
if (_context != null && read > 0) {
read = 0;
Future.delayed(Duration(milliseconds: 300), () {
Navigator.push(_context,
new MaterialPageRoute(builder: (context) => NotificationsPage()));
});
}
}
main.dart 修改点击事件。TaskNotifcation 一定要是单例,单例的写法不用我教吧。( 这个延迟有用吗??)
onOpenNotification: (Map<String, dynamic> message) async { Future.delayed(Duration(seconds: 1), () {
TaskNotifcation.instance.checkNotifcation();
});
},
在home.dart 里,页面加载完成是赋值context
@override
Widget build(BuildContext context) {
TaskNotifcation.instance.runTask(context); //传入context,并校验启动来源
return Scaffold(
/// widget 视图
);
}
到这里推送点击基本就OK了,点击事件都交由TaskNotifcation去管理了,可以在里面加入别的业务逻辑,或更多判断。但由于他是单例,存下来的context不会被释放,所以还要在home.dart里加上:
@override void dispose() {
// TODO: implement dispose
super.dispose();
TaskNotifcation.instance.clearContext();
}
class TaskNotifcation{
/// 其他代码
///......
/// jump(){};
void clearContext(){
if (_context != null) {
_context = null;
}
}
}
我这里只提供思路和部分代码,具体怎么实现需要结合实际项目情况做调整,相信也有比我更好的方法。第一次写文档,不知道表诉清楚没有,欢迎留言。