Android 手游SDK组件化开发实战指南

前言

本文档详细介绍如何从零开始构建一个组件化的Android手游SDK。整个项目采用组件化架构,将SDK的各个功能模块拆分独立开发,最终组合成一个完整的手游SDK。

效果图

单独模块运行

正式包环境运行


第一章 项目整体架构

1.1 架构一览

先来看一下我们项目的整体结构:

csharp 复制代码
zujianmodule (根项目)
├── app                      # 游戏主入口模块
├── app-base                 # SDK核心基础模块
├── app-login                # 登录模块
├── app-register             # 注册模块  
├── app-center               # 个人中心模块
├── app-changepassword       # 修改密码模块
├── app-update               # 强制更新模块
├── lib-util                 # 工具类库
├── lib-data                 # 数据层库
├── lib-logging              # 日志库
├── lib-permissions          # 权限库
└── lib-imgloader            # 图片加载库

模块分类:

  • app模块:业务功能模块(登录、注册、个人中心等)
  • lib模块:公共基础库(供所有模块复用)

1.2 模块依赖关系

scss 复制代码
app (主模块)
└── 依赖所有 app-* 模块
​
app-base (核心基础)
├── 依赖 lib-util
└── 提供:SdkCore、SdkCoreImpl、ModuleMediator、BaseActivity、回调接口
​
app-login (登录)
├── 依赖 app-base
└── 提供:LoginActivity
​
app-register (注册)
├── 依赖 app-base
└── 提供:RegisterActivity
​
app-center (个人中心)
├── 依赖 app-base
└── 提供:CenterActivity
​
app-changepassword (修改密码)
├── 依赖 app-base
└── 提供:ChangePasswordActivity
​
app-update (强制更新)
├── 依赖 app-base
└── 提供:UpdateActivity

第二章 核心技术点详解

2.1 为什么要组件化?

传统SDK开发方式:

  • 所有代码写在一个模块里
  • 代码耦合严重,难以维护
  • 功能扩展困难
  • 多人协作困难

组件化优势:

  • 模块解耦:每个功能独立模块,职责单一
  • 独立运行:开发阶段每个模块可独立运行测试
  • 按需加载:发布时可按需求组合
  • 团队协作:不同模块可分配给不同人开发

2.2 反射式资源加载(核心黑科技)

这是本项目的核心创新点 !传统方式下,每个模块的Activity都需要在主项目的AndroidManifest中注册,否则找不到。但我们使用反射技术实现了自动发现

TestResourceUtil 工具类

arduino 复制代码
// 反射获取布局资源
public static int getLayoutId(Context context, String resName) {
    return context.getResources().getIdentifier(resName, "layout", context.getPackageName());
}
​
// 反射获取字符串资源
public static int getStringId(Context context, String resName) {
    return context.getResources().getIdentifier(resName, "string", context.getPackageName());
}
​
// 反射获取Drawable资源
public static int getDrawableId(Context context, String resName) {
    return context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
}
​
// 反射获取ID
public static int getId(Context context, String resName) {
    return context.getResources().getIdentifier(resName, "id", context.getPackageName());
}

在BaseActivity中使用

scala 复制代码
public abstract class BaseActivity extends Activity {
    
    // 通过布局名称动态加载布局
    public void setContentView(String layoutId) {
        // 传入 "activity_login" 而不是 R.layout.activity_login
        super.setContentView(TestResourceUtil.getLayoutId(mContext, layoutId));
    }
    
    // 通过控件ID名称查找控件
    public View findViewById(String viewId) {
        // 传入 "btn_login" 而不是 R.id.btn_login
        return super.findViewById(TestResourceUtil.getId(mContext, viewId));
    }
}

Activity中使用

scala 复制代码
public class LoginActivity extends BaseActivity {
    @Override
    protected void loadViewLayout() {
        // 直接写字符串,框架自动找资源!
        setContentView("activity_login");
    }
    
    @Override
    protected void findViewById() {
        // 控件也是字符串形式
        Button btnLogin = findViewById("btn_login");
    }
}

💡 好处 :再也不用担心模块间资源ID冲突问题了!每个模块可以有自己的activity_login.xml

2.3 模块初始化机制

每个功能模块都需要一个App类实现AppInitial接口,这样主项目就能在运行时自动初始化所有模块:

AppInitial接口

csharp 复制代码
public interface AppInitial {
    void init(Application app);
}

登录模块的App类

typescript 复制代码
public class App implements ModuleMediator.AppInitial {
    @Override
    public void init(Application app) {
        // 这里可以初始化登录模块特有的东西
        // 比如配置第三方登录SDK
    }
}

ModuleMediator统一初始化

arduino 复制代码
public class ModuleMediator {
    // 每个模块的App类全限定名
    private static final String APP_LOGIN_CLASS = "com.github.jeffery.login.App";
    private static final String APP_CENTER_CLASS = "com.github.jeffery.center.App";
    private static final String APP_REGISTER_CLASS = "com.github.jeffery.register.App";
    private static final String APP_CHANGEPASSWORD_CLASS = "com.github.jeffery.changepassword.App";
    private static final String APP_UPDATE_CLASS = "com.github.jeffery.update.App";
    
    // 一次性初始化所有模块
    public static void init(Application app) {
        String[] appClasses = {
            APP_LOGIN_CLASS, 
            APP_CENTER_CLASS,
            APP_REGISTER_CLASS, 
            APP_CHANGEPASSWORD_CLASS, 
            APP_UPDATE_CLASS
        };
        
        for (String claName : appClasses) {
            try {
                Class<?> clz = Class.forName(claName);
                AppInitial obj = (AppInitial) clz.newInstance();
                obj.init(app);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2.4 路由跳转机制

类似于ARouter,但更轻量级!使用字符串常量来标识目标页面:

java 复制代码
public class ModuleMediator {
    // 页面路由常量
    public static final String ACTIVITY_LOGIN_CLASS = "com.github.jeffery.login.LoginActivity";
    public static final String ACTIVITY_CENTER_CLASS = "com.github.jeffery.center.CenterActivity";
    public static final String ACTIVITY_REGISTER_CLASS = "com.github.jeffery.register.RegisterActivity";
    public static final String ACTIVITY_CHANGEPASSWORD_CLASS = "com.github.jeffery.changepassword.ChangePasswordActivity";
    public static final String ACTIVITY_UPDATE_CLASS = "com.github.jeffery.update.UpdateActivity";
    
    // 单例模式
    private static volatile ModuleMediator instance;
    
    public static ModuleMediator getInstance() {
        if (instance == null) {
            synchronized (ModuleMediator.class) {
                if (instance == null) {
                    instance = new ModuleMediator();
                }
            }
        }
        return instance;
    }
    
    // 跳转Activity
    public void startActivity(Activity activity, String className) {
        startActivity(activity, className, null);
    }
    
    public void startActivity(Activity activity, String className, Bundle bundle) {
        try {
            Class<?> clazz = Class.forName(className);
            Intent intent = new Intent(activity, clazz);
            if (bundle != null) {
                intent.putExtras(bundle);
            }
            activity.startActivity(intent);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(activity, "找不到" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
}

使用示例

csharp 复制代码
// 跳转到登录页
ModuleMediator.getInstance().startActivity(this, 
    ModuleMediator.ACTIVITY_LOGIN_CLASS);
​
// 带参数跳转到注册页
Bundle params = new Bundle();
params.putString("from", "login");
ModuleMediator.getInstance().startActivity(this, 
    ModuleMediator.ACTIVITY_REGISTER_CLASS, params);

第三章 核心功能实现

3.1 SDK核心类设计

SdkCore抽象类(定义规范):

csharp 复制代码
public abstract class SdkCore {
    // 初始化
    public abstract void init(Context context);
    
    // 登录
    public abstract void login(LoginCallback callback);
    
    // 支付
    public abstract void pay(PayParams params, PayCallback callback);
    
    // 登出
    public abstract void logout(LogoutCallback callback);
    
    // 退出游戏
    public abstract void exitGame(ExitCallback callback);
    
    // 数据上报
    public abstract void reportData(String eventId, Object params);
    
    // 生命周期
    public abstract void onCreate();
    public abstract void onStart();
    public abstract void onResume();
    public abstract void onPause();
    public abstract void onStop();
    public abstract void onDestroy();
}

SdkCoreImpl实现类(单例模式):

typescript 复制代码
public class SdkCoreImpl extends SdkCore {
    
    private static volatile SdkCoreImpl instance;
    private Context mContext;
    private boolean isInitialized = false;
    private String currentUserId;
    private String currentUserName;
    private String currentToken;
    
    private SdkCoreImpl() {
        // 私有构造函数
    }
    
    public static SdkCoreImpl getInstance() {
        if (instance == null) {
            synchronized (SdkCoreImpl.class) {
                if (instance == null) {
                    instance = new SdkCoreImpl();
                }
            }
        }
        return instance;
    }
    
    @Override
    public void init(Context context) {
        this.mContext = context.getApplicationContext();
        this.isInitialized = true;
    }
    
    // ... 其他方法实现
}

3.2 回调接口设计

所有回调继承自基础接口:

csharp 复制代码
// 基础回调
public interface SdkCallback {
    void onSuccess(Object data);
    void onFailure(int code, String msg);
}
​
// 登录回调
public interface LoginCallback extends SdkCallback {
    void onLoginSuccess(String userId, String userName, String token);
    void onLoginCancel();
}
​
// 支付回调
public interface PayCallback extends SdkCallback {
    void onPaySuccess(String orderId, String productId);
    void onPayCancel();
    void onPayPending(String msg);
}
​
// 登出回调
public interface LogoutCallback extends SdkCallback {
    void onLogoutSuccess();
}
​
// 退出游戏回调
public interface ExitCallback extends SdkCallback {
    void onConfirmExit();
    void onCancelExit();
}

3.3 支付参数类

arduino 复制代码
public class PayParams {
    private String productId;      // 商品ID
    private String productName;    // 商品名称
    private int price;            // 价格(分)
    private String currency;       // 货币类型
    private String orderId;        // 订单号
    private String roleId;         // 角色ID
    private String roleName;       // 角色名称
    private String serverId;        // 服务器ID
    private String serverName;      // 服务器名称
    private String extData;        // 扩展数据
    
    // Getter and Setter...
}

第四章 模块创建步骤

4.1 创建新模块

假设我们要创建一个"礼包"模块 app-gift

1. 在settings.gradle中添加

php 复制代码
include ':app-gift'

2. 创建build.gradle

java 复制代码
plugins {
    id 'com.android.library'
}
​
android {
    compileSdk buildVersion.compileSdk
    
    defaultConfig {
        minSdk buildVersion.minSdk
        targetSdk buildVersion.targetSdk
        
        // 开发阶段可以独立运行
        if (!isRelease) {
            applicationId 'com.github.jeffery.gift'
            versionCode 1
            versionName '1.0'
        }
    }
}
​
// 资源前缀,避免资源名冲突
android.resourcePrefix 'gift_'
​
dependencies {
    // 依赖基础模块
    api project(':app-base')
}

3. 创建包结构和类

bash 复制代码
app-gift/src/main/java/com/github/jeffery/gift/
├── App.java              # 模块初始化类
├── GiftActivity.java     # 礼包Activity
└── ...

4. 创建App类

java 复制代码
package com.github.jeffery.gift;
​
import android.app.Application;
import com.github.jeffery.utils.ModuleMediator;
​
public class App implements ModuleMediator.AppInitial {
    @Override
    public void init(Application app) {
        // 初始化礼包模块
    }
}

5. 在ModuleMediator中添加路由

arduino 复制代码
public static final String ACTIVITY_GIFT_CLASS = "com.github.jeffery.gift.GiftActivity";

4.2 资源文件规范

为避免资源冲突,每个模块的资源都要有模块前缀

模块 布局前缀 字符串前缀 颜色前缀 尺寸前缀
app-login login_ login_ login_ login_
app-register register_ register_ register_ register_
app-center center_ center_ center_ center_
app-gift gift_ gift_ gift_ gift_

示例

  • activity_login.xmllogin_activity.xml
  • btn_loginlogin_btn_submit
  • @string/login_hint@string/login_hint

第五章 集成到游戏项目

5.1 主项目build.gradle配置

java 复制代码
dependencies {
    implementation project(':app-base')
    implementation project(':app-login')
    implementation project(':app-register')
    implementation project(':app-center')
    implementation project(':app-changepassword')
    implementation project(':app-update')
}

5.2 Application初始化

scala 复制代码
public class GameApplication extends Application {
​
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 1. 初始化所有SDK模块
        ModuleMediator.init(this);
        
        // 2. 初始化SDK核心
        SdkCoreImpl.getInstance().init(this);
    }
}

5.3 生命周期集成

typescript 复制代码
public class MainActivity extends AppCompatActivity {
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SdkCoreImpl.getInstance().onCreate();
    }
​
    @Override
    protected void onStart() {
        super.onStart();
        SdkCoreImpl.getInstance().onStart();
    }
​
    @Override
    protected void onResume() {
        super.onResume();
        SdkCoreImpl.getInstance().onResume();
    }
​
    @Override
    protected void onPause() {
        super.onPause();
        SdkCoreImpl.getInstance().onPause();
    }
​
    @Override
    protected void onStop() {
        super.onStop();
        SdkCoreImpl.getInstance().onStop();
    }
​
    @Override
    protected void onDestroy() {
        super.onDestroy();
        SdkCoreImpl.getInstance().onDestroy();
    }
}

5.4 常用API调用

登录

typescript 复制代码
SdkCoreImpl.getInstance().login(new LoginCallback() {
    @Override
    public void onLoginSuccess(String userId, String userName, String token) {
        Log.d("Login", "登录成功!userId=" + userId);
    }
    
    @Override
    public void onLoginCancel() {
        Log.d("Login", "用户取消登录");
    }
    
    @Override
    public void onSuccess(Object data) {}
    
    @Override
    public void onFailure(int code, String msg) {
        Log.e("Login", "登录失败:" + msg);
    }
});

支付

csharp 复制代码
PayParams params = new PayParams();
params.setProductId("com.game.gems_100");
params.setProductName("100钻石");
params.setPrice(100);
​
SdkCoreImpl.getInstance().pay(params, new PayCallback() {
    @Override
    public void onPaySuccess(String orderId, String productId) {
        Log.d("Pay", "支付成功!");
    }
    // ...
});

数据上报

csharp 复制代码
// 简单事件
SdkCoreImpl.getInstance().reportData("level_start", null);
​
// 带参数事件
Map<String, Object> params = new HashMap<>();
params.put("level_id", 1);
SdkCoreImpl.getInstance().reportData("level_complete", params);

第六章 开发与发布模式

6.1 开发模式(isRelease = false)

ini 复制代码
// env.gradle
isRelease = false

特点:

  • 每个模块都有独立的applicationId
  • 可以独立安装运行调试
  • 便于团队成员独立开发

6.2 发布模式(isRelease = true)

ini 复制代码
// env.gradle
isRelease = true

特点:

  • 所有模块合并为一个SDK
  • 只生成一个applicationId
  • 减少包体积

第七章 常见问题解答

Q1: 如何添加新功能模块?

  1. 创建新模块(参考第四章)
  2. 在ModuleMediator中添加路由常量
  3. 在settings.gradle中include
  4. 在主模块build.gradle中添加依赖

Q2: 模块间如何通信?

通过ModuleMediator单例进行通信,也可以直接使用SdkCoreImpl的实例方法。

Q3: 资源冲突怎么办?

每个模块使用独立的资源前缀(通过android.resourcePrefix配置),加上反射式加载,不会有ID冲突。

Q4: 如何调试单个模块?

在env.gradle中设置isRelease = false,该模块就可以独立安装运行。


附录:目录结构参考

bash 复制代码
项目根目录/
├── app.gradle              # app模块配置
├── build.gradle            # 根项目配置
├── env.gradle              # 环境变量配置
├── settings.gradle         # 模块列表
├── app/                    # 游戏主模块
├── app-base/               # SDK基础模块
│   └── src/main/java/com/github/jeffery/utils/
│       ├── SdkCore.java
│       ├── SdkCoreImpl.java
│       ├── ModuleMediator.java
│       ├── BaseActivity.java
│       ├── LoginCallback.java
│       ├── PayCallback.java
│       └── ...
├── app-login/              # 登录模块
├── app-register/           # 注册模块
├── app-center/             # 个人中心模块
├── app-changepassword/     # 修改密码模块
├── app-update/             # 强制更新模块
├── lib-util/               # 工具库
├── lib-data/               # 数据库
├── lib-logging/            # 日志库
├── lib-permissions/        # 权限库
└── lib-imgloader/          # 图片加载库

总结

本项目通过以下技术实现了一个轻量级的Android手游SDK组件化方案:

  1. 反射式资源加载 - 解决资源ID冲突问题
  2. ModuleMediator - 统一模块初始化和页面路由
  3. AppInitial接口 - 模块热插拔
  4. 单例模式 - 核心类全局唯一实例
  5. 回调接口 - 异步通信标准化

整个方案不依赖任何第三方路由框架,代码量少,易于理解和维护,非常适合中小型手游SDK开发。

相关推荐
煤球王子4 小时前
学习记录:Android14中的WiFi-wpa_supplicant(1)
android
张小潇5 小时前
AOSP15 Input专题InputDispatcher源码分析
android
TT_Close5 小时前
【Flutter×鸿蒙】debug 包也要签名,这点和 Android 差远了
android·flutter·harmonyos
Kapaseker6 小时前
2026年,我们还该不该学编程?
android·kotlin
雨白1 天前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk1 天前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING1 天前
RN容器启动优化实践
android·react native
恋猫de小郭1 天前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker1 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin