Flutter重构: plugin 插件方法标准化实现

635 阅读2分钟

工作中经常实现 plugin 插件,随着项目人员的增加,功能的增加,代码有些混乱,随想实现一种通用的,标准化的实现方案,最终实现如下:

一. 创建插件 user(iOS 必须基于OC)

终端执行以下三个命令:

flutter create --template=plugin -i objc -a kotlin user

cd user

flutter create example

二. 实现插件方法

1. 定义插件 dart 方法
class User {
  static const MethodChannel _channel = const MethodChannel('user');

  static Future<String> getPlatformVersion() async {
    String value = await _channel.invokeMethod('getPlatformVersion', "iOS");
    return value;
  }

  static Future<String> getAppVersion() async {
    String value = await _channel.invokeMethod('getAppVersion');
    return value;
  }

}
2. 实现 iOS 方法
···
├── Assets
├── Classes
│   ├── UserPlugin.h
│   └── UserPlugin.m
│   ├── UserManager.h(功能类)
│   ├── UserManager.m(功能类)
│   ├── ···
└── user.podspec
···

目录如上,如不想功能代码和插件代码混在一起,会创建功能类,便于维护,功能类示例如下;

UserManager.m 代码:
//
//  UserManager.m
//  User
//
//  Created by shang on 2021/10/19.
//

#import "UserManager.h"

@implementation UserManager

- (void)getAppVersion:(id)params callback:(FlutterResult)callback{
    NSDictionary *info = NSBundle.mainBundle.infoDictionary;
    NSString *result = info[@"CFBundleShortVersionString"];
    callback(result);
}

+ (void)getPlatformVersion:(id)params callback:(FlutterResult)callback{
    callback(UIDevice.currentDevice.systemVersion);
}

@end
UserPlugin.m 代码:
#import "UserPlugin.h"
#import "UserManager.h"

@implementation UserPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"user" binaryMessenger:[registrar messenger]];
  UserPlugin* instance = [[UserPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result{
    // NSLog(@"call.arguments: %@", call.arguments);
    [self reflectMethod:[UserManager new]
                   call:call
                 result:result];
}

/// iOS 类方法/实例方法映射(方法格式: * (void)*MethodName*:(id)params callback:(FlutterResult)callback;)
///
/// @param cls 类参数: UserManager.class
/// @param instance 类方法传 nil, 实例方法传对应实例
/// @param call FlutterPlugin 参数
/// @param result FlutterPlugin 参数
- (void)reflectMethod:(nullable NSObject *)instance
                 call:(FlutterMethodCall *)call
               result:(FlutterResult)result {
    NSLog(@"call.method: %@, call.arguments: %@", call.method, call.arguments);
    
    NSString *method = call.method; //获取函数名
    id arguments = call.arguments; //获取参数列表
    SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@:callback:", method]);
    
    if ([instance.class respondsToSelector:selector]) {
        NSMethodSignature *methodSignature = [instance.class methodSignatureForSelector:selector]; // Signature

        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        invocation.target = instance.class;// target
        
        invocation.selector = selector;
        [invocation setArgument:&arguments atIndex:2];
        [invocation setArgument:&result atIndex:3];
        [invocation invoke];
        return;
    }
    
    if (instance && [instance respondsToSelector:selector]) {
        NSMethodSignature *methodSignature = [instance.class instanceMethodSignatureForSelector:selector]; // Signature
    
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        invocation.target = instance;// target
        
        invocation.selector = selector;
        [invocation setArgument:&arguments atIndex:2];
        [invocation setArgument:&result atIndex:3];
        [invocation invoke];
        return;
    }
    NSLog(@"call.method: %@, call.arguments: %@", call.method, call.arguments);
    result(FlutterMethodNotImplemented);
}

@end

三. 方法标准化

1. 什么是方法标准化?

答案:按照一定规则声明 iOS 方法; 本实现指此方法定义应遵守方法格式:

//dart 方法名 +  callback:(FlutterResult)callback
* (void)*MethodName*:(id)params callback:(FlutterResult)callback;
2. 为什么要方法标准化?

答案:dart 调用 oc 方法本质是方法的字符串转化,统一方法声明方法可以极大的减少冗余代码;且一次实现多插件复用;

3. 为什么要如此定义呢?

答案:flutter 调用 iOS 方法转化如下:

dart 方法1 《= Plugin 方法1 《= iOS 方法1
dart 方法2 《= Plugin 方法2 《= iOS 方法2
dart 方法3 《= Plugin 方法3 《= iOS 方法3

使用标准化,使 Plugin 方法通用化;

dart 方法1 《= Plugin 通用方法 《= iOS 方法1
dart 方法2 《= Plugin 通用方法 《= iOS 方法2
dart 方法3 《= Plugin 通用方法 《= iOS 方法3

是与 user.dart 文件方法保持一致; 通道的 invokeMethod 方法的参数是数组列表;为了方法标准化,我们缩减为一个固定参数,当 _channel.invokeMethod 方法没有传参时,UserManager.h 中的方法 params 参数为空;

//flutter 源码

  @optionalTypeArgs
  Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) {
    return _invokeMethod<T>(method, missingOk: false, arguments: arguments);
  }

flutter_templates