前言
本文档详细介绍如何从零开始构建一个组件化的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.xml→login_activity.xmlbtn_login→login_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: 如何添加新功能模块?
- 创建新模块(参考第四章)
- 在ModuleMediator中添加路由常量
- 在settings.gradle中include
- 在主模块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组件化方案:
- 反射式资源加载 - 解决资源ID冲突问题
- ModuleMediator - 统一模块初始化和页面路由
- AppInitial接口 - 模块热插拔
- 单例模式 - 核心类全局唯一实例
- 回调接口 - 异步通信标准化
整个方案不依赖任何第三方路由框架,代码量少,易于理解和维护,非常适合中小型手游SDK开发。