电商导购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开发者团队,转载请注明出处!

相关推荐
悟能不能悟6 小时前
Java CheckFailedException会去获取message.properties的内容吗
java·开发语言
shang_xs6 小时前
Java 25 ScopedValue - 作用域内安全访问的一种实现
java·开发语言·安全
sld1686 小时前
打破云服务“绑定”局限,打造高适配性、强管控力的混合云架构新范式
微服务·云原生·架构
向量引擎6 小时前
[架构师级] 压榨GPT-5.2与Sora 2的极限性能:从单体调用到高并发多模态Agent集群的演进之路(附全套Python源码与性能调优方案)
开发语言·人工智能·python·gpt·ai·ai写作·api调用
小白学大数据6 小时前
Java 异步爬虫高效获取小红书短视频内容
java·开发语言·爬虫·python·音视频
solar应急响应6 小时前
域控宕机!如何强制夺取五大角色恢复业务?
开发语言·php
数据的世界017 小时前
C# 获评2025年度编程语言-编程语言排行榜2026年1月
开发语言
2201_757830877 小时前
Bean原理篇
java·开发语言
草原上唱山歌7 小时前
推荐学习的C++书籍
开发语言·c++·学习
Xの哲學7 小时前
Linux 文件系统一致性: 从崩溃恢复到 Journaling 机制
linux·服务器·算法·架构·边缘计算