电商导购app移动端架构:组件化与插件化在多平台适配中的实践

电商导购app移动端架构:组件化与插件化在多平台适配中的实践

大家好,我是省赚客APP研发者阿宝!"省赚客"作为聚娃科技旗下的高佣金导购平台,需同时支持华为、小米、OPPO等应用商店及自有H5渠道,且不同渠道对UI风格、功能模块、广告策略有差异化要求。为提升开发效率与包体控制能力,我们采用"组件化 + 插件化"混合架构,核心功能以AAR组件形式集成,渠道专属逻辑通过动态插件加载,实现一套代码、多端灵活发布。

组件化分层设计

我们将工程拆分为以下模块:

  • :app:宿主壳工程,仅包含基础配置与启动逻辑
  • :base:公共基础库(网络、日志、埋点)
  • :user:用户中心组件
  • :task:任务广场组件
  • :rebate:返利明细组件
  • :channel-api:渠道抽象接口

各业务组件通过implementation project(':xxx')方式依赖,编译期集成。

gradle 复制代码
// app/build.gradle
dependencies {
    implementation project(':base')
    implementation project(':user')
    implementation project(':task')
    implementation project(':rebate')
}

组件间通信采用路由框架,避免直接依赖:

java 复制代码
// juwatech.cn.base.router.Router
public class Router {
    public static void navigate(Context context, String path) {
        if ("/task/list".equals(path)) {
            Intent intent = new Intent(context, TaskListActivity.class);
            context.startActivity(intent);
        } else if ("/rebate/detail".equals(path)) {
            Intent intent = new Intent(context, RebateDetailActivity.class);
            context.startActivity(intent);
        }
    }
}

渠道插件化:动态加载差异化逻辑

对于华为渠道需接入Push Kit、小米渠道需定制弹窗样式等场景,我们将渠道逻辑封装为独立插件APK,运行时动态加载。

定义统一接口:

java 复制代码
// juwatech.cn.channel.api.ChannelPlugin
package juwatech.cn.channel.api;

import android.content.Context;

public interface ChannelPlugin {
    void init(Context context);
    String getChannelName();
    boolean shouldShowCustomPopup();
}

华为插件实现:

java 复制代码
// 华为插件工程 huawei-plugin/src/main/java/juwatech/cn/plugin/huawei/HuaweiChannelImpl.java
package juwatech.cn.plugin.huawei;

import juwatech.cn.channel.api.ChannelPlugin;
import android.content.Context;
import com.huawei.hms.api.HuaweiApiClient;

public class HuaweiChannelImpl implements ChannelPlugin {
    @Override
    public void init(Context context) {
        HuaweiApiClient client = new HuaweiApiClient.Builder(context).build();
        // 初始化Push Kit
    }

    @Override
    public String getChannelName() {
        return "huawei";
    }

    @Override
    public boolean shouldShowCustomPopup() {
        return true; // 华为渠道显示定制弹窗
    }
}

宿主App通过DexClassLoader加载插件:

java 复制代码
// juwatech.cn.app.PluginLoader
public class PluginLoader {
    private static ChannelPlugin instance;

    public static void loadPlugin(Context context, String pluginPath) {
        try {
            DexClassLoader loader = new DexClassLoader(
                pluginPath,
                context.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath(),
                null,
                context.getClassLoader()
            );
            Class<?> clazz = loader.loadClass("juwatech.cn.plugin." + getChannelSuffix() + ".HuaweiChannelImpl");
            instance = (ChannelPlugin) clazz.getDeclaredConstructor().newInstance();
            instance.init(context);
        } catch (Exception e) {
            Log.e("PluginLoader", "Failed to load plugin", e);
        }
    }

    public static ChannelPlugin get() {
        return instance;
    }
}

插件APK由CI流水线按渠道单独构建,下发至CDN,App启动时根据设备信息下载对应插件。

资源隔离与混淆兼容

为避免插件与宿主资源ID冲突,所有插件使用固定packageId

xml 复制代码
<!-- huawei-plugin/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="juwatech.cn.plugin.huawei"
    android:sharedUserId="juwatech.uid">

并在build.gradle中指定:

gradle 复制代码
android {
    aaptOptions {
        additionalParameters "--package-id", "0x7F"
    }
}

同时,宿主开启minifyEnabled时,保留插件接口类:

proguard 复制代码
# app/proguard-rules.pro
-keep interface juwatech.cn.channel.api.**
-keep class juwatech.cn.plugin.** { *; }

多平台构建自动化

通过Gradle变体实现一键打包多渠道:

gradle 复制代码
// app/build.gradle
flavorDimensions "channel"
productFlavors {
    huawei {
        dimension "channel"
        buildConfigField "String", "PLUGIN_URL", "\"https://cdn.juwatech.cn/plugins/huawei_v1.apk\""
    }
    xiaomi {
        dimension "channel"
        buildConfigField "String", "PLUGIN_URL", "\"https://cdn.juwatech.cn/plugins/xiaomi_v1.apk\""
    }
    defaultChannel {
        dimension "channel"
        buildConfigField "String", "PLUGIN_URL", "\"\""
    }
}

CI脚本自动上传插件并更新URL:

bash 复制代码
# build_plugin.sh
./gradlew :huawei-plugin:assembleRelease
aws s3 cp huawei-plugin-release.apk s3://juwatech-cdn/plugins/huawei_v$(date +%s).apk

性能与稳定性保障

插件加载失败时降级至通用逻辑:

java 复制代码
// juwatech.cn.app.MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    String pluginUrl = BuildConfig.PLUGIN_URL;
    if (!TextUtils.isEmpty(pluginUrl)) {
        PluginDownloader.downloadAndLoad(this, pluginUrl);
    }
    // 无论插件是否加载成功,均正常启动主界面
    setContentView(R.layout.activity_main);
}

// 使用时判空
if (PluginLoader.get() != null && PluginLoader.get().shouldShowCustomPopup()) {
    showHuaweiPopup();
} else {
    showDefaultPopup();
}

通过组件化解耦与插件化动态扩展,"省赚客"App包体减少35%,渠道适配周期从3天缩短至2小时。

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

相关推荐
lincats14 分钟前
大白话讲解Addy Osmani 的循环工程Loop Engineering工作流
人工智能·驱动开发·架构·prompt·状态模式·知识图谱
IT_Octopus1 小时前
JSON 日志里的 `{“$ref“:“$.xxx“}`:从 fastjson 兼容包到原生 fastjson2 的迁移实录
开发语言·python·json
geovindu1 小时前
python:HandWriting Recognition using paddlepaddle
开发语言·后端·python·paddlepaddle
禾高网络1 小时前
养老系统开发:背景、架构与养老行业展望
java·大数据·人工智能·小程序·架构
珞光电子USRP SDR软件无线电平台1 小时前
国产嵌入式SDR平台LW-E320-G详解:射频、FPGA、ARM架构与应用选型
arm开发·fpga开发·架构
wuyk5551 小时前
Python 零基础入门第九章:用户输入与 While 循环
开发语言·python
兮动人1 小时前
Python变量与常量
开发语言·python·机器学习·python变量与常量
风筝在晴天搁浅2 小时前
解题代码清爽版
java·开发语言
Java后端的Ai之路2 小时前
21、Python - 命令模式
开发语言·人工智能·python·命令模式·外观模式
astronautyi2 小时前
Go 运行时内存分配与 GC 位图深度剖析
开发语言·后端·golang