Flutter多渠道打包的解决方案(walle)

背景

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

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

应用解决方案

walle

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

flutter插件

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

注意

经过线上测试,存在部分机型获取不到渠道的情况,请慎重使用

插件使用

参考example

插件源码
  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. 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. 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. 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;
    }
  }
}

提升

使用脚本的方式打一个渠道包需要3-4分钟,总共就需要等待半个多小时。现在一共只需要三分钟左右,效率方面提升很大。

本文已同步到个人博客

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter支持在不同的渠道上进行打包,可以根据不同的渠道配置不同的应用包。下面是实现Flutter多渠道打包的步骤: 1. 在Flutter项目根目录下的`android/app`目录下创建一个名为`channel`的文件夹,用于存放不同渠道的配置文件。 2. 在`channel`文件夹下创建不同渠道的配置文件,例如`channel/dev.properties`、`channel/prod.properties`等。配置文件的内容可以包括应用包名、应用名称、应用图标、应用版本号等信息。例如: ``` # dev.properties # 应用包名 package_name=com.example.dev # 应用名称 app_name=dev_app # 应用图标 app_icon=dev_icon # 应用版本号 app_version=1.0.0 ``` 3. 在`android/app/build.gradle`文件中添加如下配置: ``` android { // ... defaultConfig { // ... //应用包名 applicationId "com.example.app" //应用名称 manifestPlaceholders = [ appName: "My App" ] //应用版本号 versionCode 1 versionName "1.0.0" // ... } // ... buildTypes { release { // ... //在release模式下,根据不同渠道打包 applicationVariants.all { variant -> variant.outputs.all { def outputFile = outputFileName if (outputFile.endsWith('.apk') || outputFile.endsWith('.aab')) { def fileName = outputFile.replace(".${outputFile.split("\\.").last()}", "") def channelPropertiesFile = file("channel/${variant.flavorName}.properties") if (channelPropertiesFile.exists()) { def channelProperties = new Properties() channelProperties.load(new FileInputStream(channelPropertiesFile)) outputFileName = "${fileName}_${channelProperties.getProperty("app_version")}_${channelProperties.getProperty("app_name")}.apk" output.outputFileName = outputFileName manifestPlaceholders = [ appName: channelProperties.getProperty("app_name"), appIcon: channelProperties.getProperty("app_icon") ] } } } } } } } ``` 4. 在Flutter项目根目录下运行以下命令,生成不同渠道的应用包: ``` flutter build apk --flavor dev flutter build apk --flavor prod ``` 其中,`--flavor`参数指定了打包时所使用的渠道配置文件。 5. 打包完成后,在`build\app\outputs\flutter-apk`目录下可以看到生成的不同渠道的应用包。 注意事项: - 渠道名称需要与`buildTypes`中的名称一致。 - 配置文件中的属性名需要与`build.gradle`文件中指定的属性名一致。 - 打包时需要分别运行不同渠道的打包命令。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值