flutter多渠道打包

正常的flutter打包是进入工程根目录执行flutter build ios或者flutter build apk,当然前提是已经根据flutter官网的教程进行配置,这里不多说
Android构建发布https://flutterchina.club/android-release/
iOS构建发布https://flutterchina.club/ios-release/

我们这里所说的多渠道打包其实还是Android原生的多渠道打包,没有实现执行命令flutter build apk就生成多个渠道包的操作。

说到安卓原生的多渠道打包应该分为两块来说,第一是打出渠道包,第二是能统计到各个渠道包的信息,那么我们首先进行第一步:打出渠道包

一、打渠道包

这一步很简单,只是在app的build.gradle中增加两处配置就可以了

//这里不知道具体有啥用,但是不写就报错
defaultConfig {

        ...

        flavorDimensions "versionCode"

       ...
    }
android {

  ...

    productFlavors {
        yingyongbao {}
        channel360 {}
        wandoujia {}
        xiaomi {}
        huawei {}
        baidu {}
        oppo {}
        vivo {}
        sanxing {}
        lianxiang {}
    }
    productFlavors.all { flavor ->
        flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
    //    修改命名规则
    applicationVariants.all { variant ->
        variant.outputs.all {
            def formattedDate = new Date().format('yyyy_MM_dd_HH_mm_ss')
            outputFileName = rootProject.getName() + "-" + variant.flavorName + "-" + buildType.name + "-" + formattedDate + "-v" + defaultConfig.versionName + "-" + defaultConfig.versionCode + ".apk";
        }
    }

...

}

我这里遇到了问题,flutter项目增加了这些配置之后就不能直接连接Android手机连调了,会报以下错误,一直没有解决,如果有人解决烦劳相告🙏
但是以Android项目打开是可以连调的

The Gradle project does not define a task suitable for the requested build.
The android/app/build.gradle file defines product flavors: baidu, channel360,
huawei, lianxiang, oppo, sanxing, vivo, wandoujia, xiaomi, yingyongbao
You must specify a --flavor option to select one of them.
Gradle build aborted.

二、统计各个渠道包下载量等信息

统计我选择的友盟统计,flutter的第三方包选择的是flutter_umplus,地址:
https://pub.dev/packages/flutter_umplus

我们首先需要去友盟官网注册app信息,Android和iOS要注册两个,获取到AndroidKey和iOSKey然后在flutter项目中合适的地方初始化友盟

//集成友盟统计
    if(Platform.isAndroid){//Android平台
      FlutterUmplus.init(AndroidKey,channel:"Android的渠道名称",reportCrash: false,logEnable: true,encrypt: true);
    }else if(Platform.isIOS){//iOS平台
      FlutterUmplus.init(iOSKey,channel: "appstore",reportCrash: false,logEnable: true,encrypt: true);
    }

iOS只有AppStore一个渠道所以固定值appstore就可以了,Android项目我们怎么获取到当前渠道包的名称呢?很简单,在AndroidManifest.xml中添加meta-data原数据

<application>

        ...

        <!--友盟统计-->
        <meta-data android:value="${UMENG_CHANNEL_VALUE}"  android:name="UMENG_CHANNEL"  />

      ...

    </application>

这里的UMENG_CHANNEL_VALUE和build.gradle中的UMENG_CHANNEL_VALUE对应起来,在Android项目中可以读取这里的value值

public static String getChannel(Context context) {
        try {
            PackageManager pm = context.getPackageManager();
            ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            return appInfo.metaData.getString("UMENG_CHANNEL");
        } catch (PackageManager.NameNotFoundException ignored) {
        }
        return "";
    }

⚠️⚠️但是现在问题来了,我不知道flutter代码中怎么读取AndroidManifest.xml中的meta-data值,于是乎我就开始思考,想到了第一个☝️个方案

1. SharedPreferences

使用SharedPreferences存储数据,然后在flutter中使用shared_preferences中读取,于是我在MainActivity中读取数据并存储

//在flutter中不知道怎么获取manifest中的meta数据,所以在这里先获取了存起来,在flutter里边取出来用😂
    try {
      PackageManager pm = this.getPackageManager();
      ApplicationInfo appInfo = pm.getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA);
      String str = appInfo.metaData.getString("UMENG_CHANNEL");

      SharedPreferences sharedPref = this.getApplication().getSharedPreferences("CONFIG_SETTING", Context.MODE_PRIVATE);
      SharedPreferences.Editor editor = sharedPref.edit();
      editor.putString("channelName",str);
      boolean isSucc = editor.commit();
      
      String theStr = sharedPref.getString("channelName","default");
      Log.d("str",theStr);
    } catch (PackageManager.NameNotFoundException ignored) {
    }
SharedPreferences.getInstance().then((sp){
    String channelName = sp.getString("channelName");
    print("渠道名称"+channelName);
  });

很不幸,读取的时候一直是空,读取不到,这里并不是说这种方式不行,而是我不会用😂😂,所以就暂时放弃这种方式了,待以后我对Android进一步熟悉再来解决。这种方式不行并且现在还没有找到有人封装这种第三方的工具,那么就需要自己动手进行原生交互了,这里我偷了个懒,我没有自己新建原生交互的plugin,而是修改了别人的代码,项目中用到了package_info,但是看源码只提供了四种属性,没有我们需要的,不行就改,改到我们能用就好了。

/// The app name. `CFBundleDisplayName` on iOS, `application/label` on Android.
  final String appName;

  /// The package name. `bundleIdentifier` on iOS, `getPackageName` on Android.
  final String packageName;

  /// The package version. `CFBundleShortVersionString` on iOS, `versionName` on Android.
  final String version;

  /// The build number. `CFBundleVersion` on iOS, `versionCode` on Android.
  final String buildNumber;
2.扩展package_info,增加channelName

package_info.dart

class PackageInfo {
  PackageInfo({
    this.appName,
    this.packageName,
    this.version,
    this.buildNumber,
    this.channelName, //自己新增的渠道名称
  });

  static Future<PackageInfo> _fromPlatform;

  /// Retrieves package information from the platform.
  /// The result is cached.
  static Future<PackageInfo> fromPlatform() async {
    if (_fromPlatform == null) {
      final Completer<PackageInfo> completer = Completer<PackageInfo>();

      // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
      // https://github.com/flutter/flutter/issues/26431
      // ignore: strong_mode_implicit_dynamic_method
      _kChannel.invokeMethod('getAll').then((dynamic result) {
        final Map<dynamic, dynamic> map = result;

        completer.complete(PackageInfo(
          appName: map["appName"],
          packageName: map["packageName"],
          version: map["version"],
          buildNumber: map["buildNumber"],
          channelName: map["channelName"], //自己新增的渠道名称
        ));
      }, onError: completer.completeError);

      _fromPlatform = completer.future;
    }
    return _fromPlatform;
  }

  /// The app name. `CFBundleDisplayName` on iOS, `application/label` on Android.
  final String appName;

  /// The package name. `bundleIdentifier` on iOS, `getPackageName` on Android.
  final String packageName;

  /// The package version. `CFBundleShortVersionString` on iOS, `versionName` on Android.
  final String version;

  /// The build number. `CFBundleVersion` on iOS, `versionCode` on Android.
  final String buildNumber;

  ///自己新增的渠道名称
  final String channelName;
}

PackageInfoPlugin

/** PackageInfoPlugin */
public class PackageInfoPlugin implements MethodCallHandler {
  ...
  @Override
  public void onMethodCall(MethodCall call, Result result) {
    try {
      Context context = mRegistrar.context();
      if (call.method.equals("getAll")) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);

        //获取渠道名使用
        ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);

        Map<String, String> map = new HashMap<String, String>();
        map.put("appName", info.applicationInfo.loadLabel(pm).toString());
        map.put("packageName", context.getPackageName());
        map.put("version", info.versionName);
        map.put("buildNumber", String.valueOf(getLongVersionCode(info)));
        map.put("channelName", String.valueOf(appInfo.metaData.getString("UMENG_CHANNEL")));

        result.success(map);
      } else {
        result.notImplemented();
      }
    } catch (PackageManager.NameNotFoundException ex) {
      result.error("Name not found", ex.getMessage(), null);
    }
  }
...
}

使用方法

//集成友盟统计
    if(Platform.isAndroid){//Android平台
      PackageInfo.fromPlatform().then((package){
        String channelName = package.channelName;
        print("渠道名"+channelName);
        FlutterUmplus.init(ChannelClass.androidKey,channel: channelName,reportCrash: false,logEnable: true,encrypt: true);
      });
    }else if(Platform.isIOS){//iOS平台
      FlutterUmplus.init(ChannelClass.iosKey,channel: ChannelClass.appstore,reportCrash: false,logEnable: true,encrypt: true);
    }

终于获取到了渠道名称👏👏👏

我是修改了第三方的包才读取到了Android原生的数据,如果以后别的项目也有这种需要,那么代码还要再改一次,后面还面临着第三方包升级等问题,最好还是自己写一个plugin进行原生交互一劳永逸。

参考文章
https://blog.csdn.net/cs_lwb/article/details/82813909

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342