# Flutter多渠道打包(walle)

# 背景

  1. 我们的应用集成了TalkingData这个第三方工具来统计日活、事件等,需要在应用启动时初始化SDK,这个时候需要传入当前渠道,以便数据的统计。

  2. 由于最开始是用脚本一个个打包,一直想解决打包过慢的问题,但是网上大多教程是基于命令行参数或flavor,实质上并没有根本解决打包效率的问题。直到发现了文章的主角 walle

# 应用解决方案

# walle

walle是美团开源的一个打包插件,这里就不做介绍了,传送门 (opens new window)

# flutter插件

由于插件是Android端的,就想着写一个Flutter插件吧。代码也很简单,已经上传到pub仓库。使用步骤可以参考 Github (opens new window) | Pub仓库 (opens new window)

# 主要功能

  • 多渠道打包(效率高)
  • 针对不同的渠道添加不同的打包参数

# 插件源码

  1. 在 android/build.gradle 文件中添加依赖

buildscript {
    repositories {
        // 不加这个找不到walle依赖
        maven {
            url 'https://maven.aliyun.com/repository/jcenter'
        }
        maven {
            url 'https://maven.aliyun.com/repository/google'
        }
        google()
        mavenCentral()
    }
}

rootProject.allprojects {
    repositories {
        // 同上
        maven {
            url 'https://maven.aliyun.com/repository/jcenter'
        }
        maven {
            url 'https://maven.aliyun.com/repository/google'
        }
        google()
        mavenCentral()
    }
}

// ...

// 末尾添加
dependencies {
    implementation 'com.meituan.android.walle:library:1.1.7'
}
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
  1. Android端注册文件(android/src/main/java/net/niuxiaoer/package_by_walle/PackageByWallePlugin.java)
import com.meituan.android.walle.WalleChannelReader;


/** PackageByWallePlugin */
public class PackageByWallePlugin implements FlutterPlugin, MethodCallHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;
  private Context applicationContext;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    this.applicationContext = flutterPluginBinding.getApplicationContext();
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "package_by_walle");
    channel.setMethodCallHandler(this);
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if (call.method.equals("getPackingChannel")) {
      try {
        // 这里调用walle的方法
        String channel = WalleChannelReader.getChannel(this.applicationContext);
        result.success(channel);
      } catch(Exception e) {
        result.success("unknown");
      }
    }  else if (call.method.equals("getPackingInfo")) {
      // 获取打包参数
      ChannelInfo channelInfo = WalleChannelReader.getChannelInfo(this.applicationContext);
      if (channelInfo != null) {
        Map<String, String> extraInfo = channelInfo.getExtraInfo();
        result.success(extraInfo);
      } else {
        result.success(null);
      }
    } else {
      result.notImplemented();
    }
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
  }
}
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
  1. iOS端注册文件(ios/Classes/PackageByWallePlugin.m)

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

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"getPackingChannel" isEqualToString:call.method]) {
    result(@"AppStore");
  } else {
    result(FlutterMethodNotImplemented);
  }
}

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  1. dart文件(lib/package_by_walle.dart)
class PackageByWalle {
  static const MethodChannel _channel = MethodChannel('package_by_walle');

  static Future<String?> get getPackingChannel async {
    final String? version = await _channel.invokeMethod('getPackingChannel');
    return version;
  }

  static Future<Map<dynamic, dynamic>?> get getPackingInfo async {
    if (Platform.isAndroid) {
      final Map<dynamic, dynamic>? info = await _channel.invokeMethod('getPackingInfo');
      return info;
    } else {
      return null;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 插件使用

参考example (opens new window)

# 提升

使用脚本的方式打一个渠道包需要3-4分钟,总共就需要等待半个多小时。现在一共只需要三分钟左右,节省了大量的时间,真香~

上次更新时间: 4/18/2022, 1:43:32 PM