0%

Flutter原生互操作

简介

在使用Flutter开发的过程总要调用到原生的一些能力,这里以实现一个Flutter的推送plugin作为例子进行说明

iOS端

plugin注册

直接贴代码好了,关键点是

  1. [registrar addApplicationDelegate:instance]:注册plugin的appdelegate回调,不然无法注册和接受推送;
  2. [registrar addMethodCallDelegate:instance channel:channel]:注册Flutter跟原生方法的信道,这里的信道名为:apns;
  3. FlutterBasicMessageChannel:注册Flutter跟原生互操作的通讯信道,这里是用于把推送消息回调给Flutter端,信道名为:com.chh.plugin/apns;
  4. handleMethodCall:override方法,Flutter触发信道通讯时会回调此方法,调用方法名以字符串方法传入,参数以对应的互操作类型传入,可能是NSString、NSNumber、NSDictionary etc.,这里判断是setupAPNS后会进行推送服务的初始化;
  5. 这里特别要注意:[application: didReceiveRemoteNotification:],这个delegate方法我是注释掉了,因为Flutter的iOS SDK并没有实现此回调,实际是会回调了[application: didReceiveRemoteNotification: fetchCompletionHandler:],这里调试花了一点时间;

APNSPlugin.h

1
2
3
4
#import <Flutter/Flutter.h>

@interface APNSPlugin : NSObject<FlutterPlugin>
@end

APNSPlugin.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@interface APNSPlugin()
@property (nonatomic, strong) FlutterBasicMessageChannel *messageChannel;
@end

@implementation APNSPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
APNSPlugin *instance = [[APNSPlugin alloc] init];
instance.messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"com.chh.plugin/apns" binaryMessenger:[registrar messenger]];
FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"apns" binaryMessenger:[registrar messenger]];
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"setupAPNS" isEqualToString:call.method]) {
NSString *channelId = call.arguments;
if([channelId isKindOfClass:[NSString class]] && channelId) {
[[APNS sharedInstance] setup:channelId];
[APNS sharedInstance].reciveNotification = ^(NSDictionary *userinfo) {
if (self.messageChannel) {
[self.messageChannel sendMessage:userinfo];
}
};
}
result(nil);
} else if ([@"registerDeviceToServer" isEqualToString:call.method]) {
[[APNS sharedInstance] registerDeviceToServer:^(NSDictionary *res) {
result(res);
} fail:^(NSError *error) {
result([FlutterError errorWithCode:[NSString stringWithFormat:@"%ld", error.code] message:error.localizedDescription details:nil]);
}];
else {
result(FlutterMethodNotImplemented);
}
}

#pragma mark - 推送

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[[APNS sharedInstance] registerForRemoteNotificationsWithDeviceToken:deviceToken];
}

//- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)dict {
// [[APNS sharedInstance] receiveRemoteNotification:dict];
//}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)dict fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler {
[[APNS sharedInstance] receiveRemoteNotification:dict];
}

@end

推送服务

  1. 在Xcode Capabilities里面打开推送服务
    打开推送服务

  2. 在开发者账号后台生成iOS的APNs证书,csr文件生成参照链接:创建证书签名请求生成apns证书

  3. APNS.m就是注册推送服务的一些代码,主要是针对iOS10+进行了适配处理,使用UNUserNotificationCenter进行推送注册和处理;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    @implementation APNS

    + (instancetype)sharedInstance {
    static APNS *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    instance = [self new];
    });
    return instance;
    }

    - (void)registerAPNS {
    if (@available(iOS 10, *)) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) {
    if (UNAuthorizationStatusDenied == settings.authorizationStatus) {
    // 异步线程里执行的,如果要弹框提醒用户开启通知,需要切换到主线程
    } else if (UNAuthorizationStatusAuthorized == settings.authorizationStatus || UNAuthorizationStatusNotDetermined == settings.authorizationStatus) {
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError *error) {
    if (granted) {
    dispatch_async(dispatch_get_main_queue(), ^{
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    });
    } else {
    // 用户拒绝了
    }
    }];
    }
    }];
    } else {
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert| UIUserNotificationTypeSound) categories:nil]];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
    }

    - (void)setup:(NSString *)channelId {
    self.channelId = channelId;
    [self registerAPNS];
    }

    - (void)registerForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    self.deviceToken = [NSString stringWithFormat:@"%@", deviceToken];
    [self registerDeviceToServer:^(NSDictionary *res) {
    NSLog(@"%@", res);
    } fail:^(NSError *error) {
    NSLog(@"%@", error);
    }];
    }

    - (void)registerDeviceToServer:(APNSSuccess)success fail:(APNSFail)fail {
    // 跟服务器注册设备
    }

    - (void)receiveRemoteNotification:(NSDictionary *)dict {
    if (self.reciveNotification) {
    self.reciveNotification(dict);
    }
    }

    - (void)receiveNotification:(UNNotification *)notification __IOS_AVAILABLE(10.0) {
    UNNotificationRequest *request = notification.request;
    UNNotificationContent *content = request.content;
    NSDictionary *userInfo = content.userInfo;
    if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    // Remote
    [self receiveRemoteNotification:userInfo];
    } else {
    // Local
    }
    }

    #pragma mark - UNUserNotificationCenterDelegate

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) {
    [self receiveNotification:notification];
    completionHandler(UNNotificationPresentationOptionNone);
    }

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler __IOS_AVAILABLE(10.0) {
    [self receiveNotification:response.notification];
    completionHandler();
    }

    @end

Android端

Android端的推送实现代码就不贴了,网上代码一堆;

Flutter端

Flutter端的代码就很简单了

  1. MethodChannel:注意对应上面iOS注册的方法信道名就可以了,这里是:apns;
  2. BasicMessageChannel:注意对应上面iOS注册的通讯信道名就可以了,这里是:com.chh.plugin/apns;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import 'dart:async';
    import 'package:flutter/services.dart';

    class Base {
    static const MethodChannel _channel = const MethodChannel('apns');
    static const BasicMessageChannel _messageChannel = const BasicMessageChannel('com.chh.plugin/apns', StandardMessageCodec());

    ///配置推送服务
    ///
    ///[channelId] 渠道标识
    Future<void> setupAPNS(String channelId) async {
    return await _channel.invokeMethod('setupAPNS', channelId);
    }

    ///把设备注册到推送服务器,登录态发生变化后请重新调用此接口
    ///并在调用接口setupAPNS和registerAPNSWithDeviceToken后再调用此接口
    Future<Map> registerDeviceToServer() async {
    return await _channel.invokeMethod('registerDeviceToServer');
    }

    ///注册推送回调
    ///
    ///[handler] 推送回调
    void apnsListener(Future<dynamic> Function(dynamic) handler) {
    _messageChannel.setMessageHandler(handler);
    }
    }

最后

  1. 通过pub引入此plugin

    1
    flutter pub get
  2. 使用

    1
    2
    3
    4
    5
    6
    7
    8
    void testAPNS() {
    String channelId = 'xxxxxx';
    base.setupAPNS(channelId);
    base.apnsListener((dynamic message) {
    Map reuslt = message;
    print(reuslt);
    });
    }
  3. 最后推荐使用’Easy APNs Provider’这个工具来做推送测试,非常好用;