安卓手游SDK面试题大全
目录
一、Google Play 内购(IAP)
1.1 基础概念
Q1: Google Play Billing Library 的最新版本是什么?主要有哪些变化?
参考答案:
-
目前最新版本是 Billing Library 7.0
-
主要变化:
- 支持一次性购买和订阅
- 简化了API调用流程
- 增强了安全性(服务端验证)
- 弃用了
querySkuDetails,改用queryProductDetails
Q2: Google Play 内购的商品类型有哪些?
参考答案: | 商品类型 | 说明 | 使用场景 | |---------|------|---------| | inapp (一次性商品) | 消耗型/非消耗型 | 游戏币、解锁关卡 | | subs (订阅型) | 周期性付费 | VIP会员、月卡 |
Q3: 消耗型商品和非消耗型商品的区别?如何处理?
参考答案:
// 消耗型商品 - 需要手动消耗
BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.build();
// 消耗购买
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.consumeAsync(consumeParams, (result, token) -> {
// 处理消耗结果
});
// 非消耗型商品 - 不需要消耗,Google会记录购买状态
// 订阅型商品 - 同样不需要消耗
1.2 支付流程
Q4: 请描述完整的Google Play内购流程?
参考答案:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App客户端 │ │ Google Play │ │ 服务端 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ 1.初始化连接 │ │
│─────────────────>│ │
│ │ │
│ 2.查询商品信息 │ │
│─────────────────>│ │
│ │ │
│ 3.发起购买请求 │ │
│─────────────────>│ │
│ │ │
│ 4.用户完成支付 │ │
│<─────────────────│ │
│ │ │
│ 5.返回PurchaseToken │
│<─────────────────│ │
│ │ │
│ 6.服务端验证 │ │
│─────────────────────────────────>│
│ │ │
│ │ 7.调用Google API验证
│ │<─────────────────│
│ │ │
│ 8.发放商品 │ │
│<─────────────────────────────────│
│ │ │
│ 9.消耗商品(消耗型) │ │
│─────────────────>│ │
Q5: 如何处理购买结果回调?
参考答案:
private PurchasesUpdatedListener purchasesUpdatedListener = (result, purchases) -> {
if (result.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
} else if (result.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
// 用户取消
} else if (result.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
// 商品已拥有,需要消耗或确认
} else {
// 其他错误处理
}
};
private void handlePurchase(Purchase purchase) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
// 1. 先进行服务端验证
verifyPurchaseOnServer(purchase);
// 2. 如果是消耗型商品,验证成功后消耗
if (isConsumableProduct(purchase)) {
consumePurchase(purchase);
}
}
}
1.3 安全与验证
Q6: 如何进行服务端购买验证?
参考答案:
// 服务端验证流程(以Java为例)
public class GooglePlayVerifier {
public boolean verifyPurchase(String packageName, String productId,
String purchaseToken) {
try {
// 使用Google Play Developer API
AndroidPublisher publisher = getAndroidPublisher();
ProductPurchase purchase = publisher.purchases()
.products()
.get(packageName, productId, purchaseToken)
.execute();
// 验证关键信息
return purchase.getPurchaseState() == 0 // 已购买
&& purchase.getConsumptionState() == 0 // 未消耗
&& verifyDeveloperPayload(purchase.getDeveloperPayload());
} catch (Exception e) {
log.error("验证失败", e);
return false;
}
}
}
关键验证点:
purchaseState= 0 表示购买成功consumptionState= 0 表示未消耗- 验证
orderId唯一性,防止重复发货 - 验证
packageName和productId匹配
Q7: 如何防止支付作弊?
参考答案: | 作弊方式 | 防护措施 | |---------|---------| | 重放攻击 | 服务端记录orderId,防止重复使用 | | 伪造收据 | 服务端调用Google API验证 | | 中间人攻击 | 使用HTTPS,证书校验 | | 本地修改 | 关键逻辑放服务端,不信任客户端数据 | | 越狱/Root检测 | 检测设备环境,增加风控 |
1.4 常见问题处理
Q8: 用户支付成功但未收到商品怎么处理?
参考答案:
// 方案1: 启动时查询未完成订单
billingClient.queryPurchasesAsync(ProductType.INAPP, (result, purchases) -> {
for (Purchase purchase : purchases) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
&& !isAcknowledged(purchase)) {
// 重新处理未确认的订单
handlePurchase(purchase);
}
}
});
// 方案2: 服务端主动查询
// 使用Google Play Developer API的purchases.products.list接口
// 定时查询pending状态的订单
// 方案3: 本地缓存订单状态
// 客户端保存支付记录,下次启动时检查
Q9: Google Play Billing常见错误码及处理方式?
参考答案: | 错误码 | 说明 | 处理方式 | |-------|------|---------| | BILLING_UNAVAILABLE | 设备不支持内购 | 提示用户更新Google Play服务 | | ITEM_ALREADY_OWNED | 商品已拥有 | 查询并消耗或确认购买 | | ITEM_NOT_OWNED | 商品未拥有 | 重新查询商品状态 | | USER_CANCELED | 用户取消 | 正常流程,不做特殊处理 | | NETWORK_ERROR | 网络错误 | 重试机制 | | SERVICE_UNAVAILABLE | 服务不可用 | 稍后重试 | | DEVELOPER_ERROR | 开发者错误 | 检查商品ID配置 | | ITEM_UNAVAILABLE | 商品不存在 | 检查后台配置 |
二、华为内购
2.1 基础概念
Q10: 华为IAP SDK的接入流程是什么?
参考答案:
// 1. 添加依赖
implementation 'com.huawei.hms:iap:6.10.0.300'
// 2. 配置agconnect-services.json文件
// 3. 初始化
IapClient iapClient = Iap.getIapClient(activity);
Q11: 华为内购商品类型有哪些?
参考答案: | 商品类型 | 常量 | 说明 | |---------|------|------| | 消耗型商品 | IapClient.PriceType.IN_APP_CONSUMABLE | 可重复购买 | | 非消耗型商品 | IapClient.PriceType.IN_APP_NONCONSUMABLE | 一次购买永久拥有 | | 订阅型商品 | IapClient.PriceType.IN_APP_SUBSCRIPTION | 周期性付费 |
2.2 支付流程
Q12: 华为内购完整代码示例?
参考答案:
public class HuaweiIAPManager {
private IapClient iapClient;
// 初始化
public void init(Context context) {
iapClient = Iap.getIapClient(context);
}
// 查询商品
public void queryProducts(List<String> productIds) {
ProductInfoReq req = new ProductInfoReq.Builder()
.setPriceType(IapClient.PriceType.IN_APP_CONSUMABLE)
.setProductIds(productIds)
.build();
Task<ProductInfoResult> task = iapClient.obtainProductInfo(req);
task.addOnSuccessListener(result -> {
List<ProductInfo> productInfos = result.getProductInfoList();
// 处理商品信息
}).addOnFailureListener(e -> {
Log.e(TAG, "查询商品失败: " + e.getMessage());
});
}
// 发起购买
public void purchase(Activity activity, ProductInfo productInfo) {
PurchaseIntentReq req = new PurchaseIntentReq.Builder()
.setProductId(productInfo.getProductId())
.setPriceType(productInfo.getPriceType())
.setDeveloperPayload("自定义数据")
.build();
Task<PurchaseIntentResult> task = iapClient.createPurchaseIntent(req);
task.addOnSuccessListener(result -> {
// 启动支付页面
Status status = result.getStatus();
if (status != null) {
status.startResolutionForResult(activity, REQUEST_CODE);
}
}).addOnFailureListener(e -> {
Log.e(TAG, "创建购买意图失败: " + e.getMessage());
});
}
// 处理购买结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
PurchaseResultInfo result = Iap.getIapClient(this)
.parsePurchaseResultInfoFromIntent(data);
switch (result.getReturnCode()) {
case OrderStatusCode.ORDER_STATE_SUCCESS:
// 购买成功,进行服务端验证
verifyPurchase(result);
break;
case OrderStatusCode.ORDER_STATE_CANCEL:
// 用户取消
break;
default:
// 处理错误
break;
}
}
}
// 消耗商品
public void consumePurchase(String purchaseToken) {
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq.Builder()
.setPurchaseToken(purchaseToken)
.build();
Task<ConsumeOwnedPurchaseResult> task =
iapClient.consumeOwnedPurchase(req);
task.addOnSuccessListener(result -> {
// 消耗成功
});
}
}
2.3 服务端验证
Q13: 华为内购服务端验证流程?
参考答案:
// 服务端验证代码示例
public class HuaweiIAPVerifier {
private static final String VERIFY_URL =
"https://subscr-drcn.iap.hicloud.com/sub/applications/purchases/verify/v2";
public VerifyResult verifyPurchase(String purchaseToken, String productId) {
// 1. 获取access_token
String accessToken = getAccessToken();
// 2. 构建请求
Map<String, String> params = new HashMap<>();
params.put("purchaseToken", purchaseToken);
params.put("productId", productId);
// 3. 发送验证请求
String response = HttpClient.post(VERIFY_URL)
.header("Authorization", "Bearer " + accessToken)
.body(params)
.execute();
// 4. 解析响应
return parseVerifyResult(response);
}
private String getAccessToken() {
// 使用client_id和client_secret获取token
String url = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
// ... 获取token逻辑
}
}
Q14: 华为内购常见错误码?
参考答案: | 错误码 | 说明 | 处理方式 | |-------|------|---------| | ORDER_STATE_SUCCESS | 购买成功 | 正常处理 | | ORDER_STATE_CANCEL | 用户取消 | 不做特殊处理 | | ORDER_STATE_FAILED | 购买失败 | 提示用户重试 | | ORDER_PRODUCT_NOT_EXIST | 商品不存在 | 检查商品配置 | | ORDER_STATE_PRODUCT_OWNED | 商品已拥有 | 查询并消耗 | | ORDER_STATE_PRODUCT_CONSUMED | 商品已消耗 | 正常情况 |
三、三星内购
3.1 基础概念
Q15: 三星IAP SDK接入流程?
参考答案:
// 1. 添加依赖
implementation 'com.samsung.android:iap:6.1.1'
// 2. 初始化
IapHelper iapHelper = IapHelper.getInstance(context);
iapHelper.startSetup(new IapHelper.OnIapSetupFinishedListener() {
@Override
public void onIapSetupFinished(IapResult result) {
if (result.isSuccess()) {
// 初始化成功
}
}
});
Q16: 三星内购商品类型?
参考答案: | 类型 | 常量 | 说明 | |-----|------|------| | 消耗型 | ITEM_TYPE_CONSUMABLE | 可重复购买 | | 非消耗型 | ITEM_TYPE_NON_CONSUMABLE | 一次购买 | | 订阅型 | ITEM_TYPE_SUBSCRIPTION | 周期性付费 |
3.2 支付流程
Q17: 三星内购完整代码示例?
参考答案:
public class SamsungIAPManager {
private IapHelper iapHelper;
// 初始化
public void init(Context context) {
iapHelper = IapHelper.getInstance(context);
iapHelper.startSetup(result -> {
if (result.isSuccess()) {
Log.d(TAG, "三星IAP初始化成功");
}
});
}
// 查询商品
public void queryProducts(List<String> itemIds) {
iapHelper.getItemList(IapHelper.ITEM_TYPE_CONSUMABLE, itemIds,
new IapHelper.OnGetItemListener() {
@Override
public void onGetItem(List<ItemVo> items, IapResult result) {
if (result.isSuccess()) {
// 处理商品列表
}
}
});
}
// 发起购买
public void purchase(Activity activity, String itemId) {
iapHelper.startPayment(itemId, "", true,
new IapHelper.OnPaymentListener() {
@Override
public void onPayment(PurchaseVo purchase, IapResult result) {
if (result.isSuccess()) {
// 购买成功,验证并消耗
verifyAndConsume(purchase);
}
}
});
}
// 消耗商品
public void consumePurchase(PurchaseVo purchase) {
iapHelper.consumePurchase(purchase.getPurchaseId(),
new IapHelper.OnConsumeListener() {
@Override
public void onConsume(IapResult result) {
if (result.isSuccess()) {
Log.d(TAG, "消耗成功");
}
}
});
}
// 查询未完成订单
public void queryOwnedItems() {
iapHelper.getOwnedList(IapHelper.ITEM_TYPE_CONSUMABLE,
new IapHelper.OnGetOwnedItemsListener() {
@Override
public void onGetOwnedItems(List<PurchaseVo> purchases,
IapResult result) {
if (result.isSuccess()) {
for (PurchaseVo purchase : purchases) {
// 处理未消耗的订单
consumePurchase(purchase);
}
}
}
});
}
}
3.3 服务端验证
Q18: 三星内购服务端验证?
参考答案:
public class SamsungIAPVerifier {
// 三星验证API
private static final String VERIFY_URL =
"https://iap.samsungapps.com/iap/v6/receipt";
public VerifyResult verifyPurchase(String purchaseId) {
try {
// 构建请求
String url = VERIFY_URL + "?purchaseId=" + purchaseId;
// 发送请求(需要使用HTTPS)
String response = HttpClient.get(url)
.header("Authorization", "Bearer " + getAccessToken())
.execute();
JSONObject json = new JSONObject(response);
// 验证关键字段
if ("200".equals(json.getString("statusCode"))) {
JSONObject item = json.getJSONObject("item");
return new VerifyResult(
item.getString("itemId"),
item.getString("paymentId"),
item.optString("purchaseDate")
);
}
} catch (Exception e) {
Log.e(TAG, "验证失败", e);
}
return null;
}
}
四、支付系统通用问题
4.1 架构设计
Q19: 如何设计一个多渠道支付SDK架构?
参考答案:
// 统一支付接口
public interface IPaymentChannel {
void init(Context context, PaymentCallback callback);
void queryProducts(List<String> productIds, ProductCallback callback);
void purchase(Activity activity, String productId, PurchaseCallback callback);
void consume(String purchaseToken, ConsumeCallback callback);
void queryPurchases(QueryCallback callback);
String getChannelName();
}
// Google实现
public class GooglePaymentChannel implements IPaymentChannel {
// ... Google Play实现
}
// 华为实现
public class HuaweiPaymentChannel implements IPaymentChannel {
// ... 华为实现
}
// 三星实现
public class SamsungPaymentChannel implements IPaymentChannel {
// ... 三星实现
}
// 支付管理器
public class PaymentManager {
private static PaymentManager instance;
private IPaymentChannel currentChannel;
private Map<String, IPaymentChannel> channels = new HashMap<>();
private PaymentManager() {
channels.put("google", new GooglePaymentChannel());
channels.put("huawei", new HuaweiPaymentChannel());
channels.put("samsung", new SamsungPaymentChannel());
}
public static PaymentManager getInstance() {
if (instance == null) {
instance = new PaymentManager();
}
return instance;
}
public void setChannel(String channelName) {
currentChannel = channels.get(channelName);
}
public IPaymentChannel getChannel() {
return currentChannel;
}
// 自动检测渠道
public void autoDetectChannel(Context context) {
if (isGooglePlayAvailable(context)) {
setChannel("google");
} else if (isHuaweiAvailable(context)) {
setChannel("huawei");
} else if (isSamsungAvailable(context)) {
setChannel("samsung");
}
}
}
Q20: 支付SDK的初始化时机和注意事项?
参考答案:
初始化时机建议:
┌─────────────────────────────────────────────────────┐
│ Application.onCreate() │
│ ├── 优点:提前初始化,支付时可直接使用 │
│ └── 缺点:可能影响启动速度 │
├─────────────────────────────────────────────────────┤
│ SplashActivity.onCreate() │
│ ├── 优点:用户看启动动画时初始化 │
│ └── 缺点:需要处理好初始化完成的回调 │
├─────────────────────────────────────────────────────┤
│ 进入充值页面时 │
│ ├── 优点:按需加载,不影响启动 │
│ └── 缺点:首次支付可能有延迟 │
└─────────────────────────────────────────────────────┘
注意事项:
1. 初始化是异步的,需要处理初始化失败的情况
2. 多渠道SDK可能冲突,需要按需初始化
3. 需要处理网络不可用的情况
4. 建议做初始化状态缓存
4.2 订单管理
Q21: 如何设计订单防重机制?
参考答案:
public class OrderManager {
// 本地订单缓存
private Set<String> processedOrders = new HashSet<>();
// 服务端订单验证
public void verifyAndDeliver(Purchase purchase, VerifyCallback callback) {
String orderId = purchase.getOrderId();
// 1. 本地检查(快速过滤)
if (processedOrders.contains(orderId)) {
callback.onAlreadyProcessed();
return;
}
// 2. 服务端验证(确保安全)
apiService.verifyPurchase(purchase.getPurchaseToken())
.enqueue(new Callback<VerifyResponse>() {
@Override
public void onResponse(VerifyResponse response) {
if (response.isValid() && !response.isDelivered()) {
// 发放商品
deliverProduct(purchase, response);
processedOrders.add(orderId);
} else if (response.isDelivered()) {
// 已发货,直接消耗
consumePurchase(purchase);
processedOrders.add(orderId);
}
}
@Override
public void onFailure(Exception e) {
// 网络失败,保存订单等待重试
savePendingOrder(purchase);
}
});
}
// 启动时检查未完成订单
public void checkPendingOrders() {
List<Purchase> pendingOrders = loadPendingOrders();
for (Purchase order : pendingOrders) {
verifyAndDeliver(order, defaultCallback);
}
}
}
Q22: 如何处理网络异常导致的订单状态不一致?
参考答案:
场景分析:
┌─────────────────────────────────────────────────────────────┐
│ 场景1: 用户支付成功 → 服务端验证失败 → 商品未发放 │
│ 解决: 客户端保存purchaseToken,下次启动时重新验证 │
├─────────────────────────────────────────────────────────────┤
│ 场景2: 服务端验证成功 → 发放商品成功 → 消耗失败 │
│ 解决: 服务端记录发货状态,客户端查询未消耗订单并消耗 │
├─────────────────────────────────────────────────────────────┤
│ 场景3: 服务端验证成功 → 发放商品失败 → 订单状态不一致 │
│ 解决: 服务端事务处理,验证和发放原子操作 │
└─────────────────────────────────────────────────────────────┘
解决方案:
1. 客户端:
- 持久化所有支付中的订单
- 启动时主动查询未完成订单
- 提供手动刷新订单入口
2. 服务端:
- 订单状态机设计
- 定时任务补偿机制
- 提供订单查询接口
3. 运营后台:
- 订单状态监控
- 手动补单功能
- 异常订单告警
4.3 安全防护
Q23: 支付SDK的安全最佳实践?
参考答案:
安全措施清单:
【客户端】
├── 代码混淆
│ └── 使用ProGuard/R8混淆关键代码
├── 反调试
│ └── 检测调试器、模拟器、Root设备
├── 证书校验
│ └── 网络请求校验服务器证书
├── 本地加密
│ └── 敏感数据加密存储(SharedPreferences加密)
└── 完整性校验
└── 检测APK是否被重新打包
【服务端】
├── 签名验证
│ └── 验证支付平台返回的签名
├── 订单唯一性
│ └── orderId去重,防止重复发货
├── 时间戳校验
│ └── 验证请求时间,防止重放攻击
├── IP白名单
│ └── 限制服务端API调用IP
└── 敏感信息保护
└── 密钥不暴露,使用密钥管理服务
【业务层】
├── 风控系统
│ └── 异常行为检测(短时间大量购买等)
├── 金额校验
│ └── 服务端校验商品价格,不信任客户端传值
└── 日志审计
└── 完整记录支付流程,便于追溯
五、SDK架构设计
5.1 整体架构
Q24: 手游SDK通常包含哪些模块?
参考答案:
┌─────────────────────────────────────────────────────────────┐
│ Game SDK │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 登录模块 │ │ 支付模块 │ │ 广告模块 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 数据统计 │ │ 推送模块 │ │ 分享模块 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 更新模块 │ │ 客服模块 │ │ 社交模块 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 核心基础层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 网络模块 │ │ 存储模块 │ │ 工具模块 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 日志模块 │ │ 安全模块 │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
Q25: SDK初始化流程设计?
参考答案:
public class SDKInitializer {
private static volatile boolean isInitialized = false;
public static void init(Context context, SDKConfig config,
InitCallback callback) {
if (isInitialized) {
callback.onSuccess();
return;
}
// 1. 参数校验
if (!validateConfig(config)) {
callback.onError("配置参数无效");
return;
}
// 2. 异步初始化
ExecutorService executor = Executors.newFixedThreadPool(4);
CountDownLatch latch = new CountDownLatch(4);
// 初始化各模块
executor.execute(() -> {
initLoginModule(context, config);
latch.countDown();
});
executor.execute(() -> {
initPaymentModule(context, config);
latch.countDown();
});
executor.execute(() -> {
initAnalyticsModule(context, config);
latch.countDown();
});
executor.execute(() -> {
initPushModule(context, config);
latch.countDown();
});
// 等待所有模块初始化完成
new Thread(() -> {
try {
latch.await(10, TimeUnit.SECONDS);
isInitialized = true;
callback.onSuccess();
} catch (InterruptedException e) {
callback.onError("初始化超时");
}
}).start();
}
}
5.2 设计模式应用
Q26: SDK中常用的设计模式有哪些?
参考答案:
1. 单例模式 (Singleton)
└── SDK管理器、支付管理器等核心类
2. 工厂模式 (Factory)
└── 根据渠道创建不同的支付实现
3. 策略模式 (Strategy)
└── 多渠道登录/支付策略切换
4. 观察者模式 (Observer)
└── 支付结果回调、登录状态监听
5. 适配器模式 (Adapter)
└── 统一不同平台的接口差异
6. 建造者模式 (Builder)
└── SDK配置、请求参数构建
7. 责任链模式 (Chain of Responsibility)
└── 登录流程:检查登录态 → 刷新Token → 重新登录
8. 代理模式 (Proxy)
└── 网络请求代理、安全代理
Q27: 如何设计SDK的回调机制?
参考答案:
// 统一回调接口
public interface SDKCallback<T> {
void onSuccess(T result);
void onError(int code, String message);
void onCancel();
}
// 支付回调示例
public interface PaymentCallback extends SDKCallback<PurchaseResult> {
default void onPending() {
// 支付处理中(如银行处理中)
}
}
// 回调分发器(处理线程切换)
public class CallbackDispatcher {
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
public static <T> void dispatchSuccess(SDKCallback<T> callback, T result) {
if (callback == null) return;
if (Looper.myLooper() == Looper.getMainLooper()) {
callback.onSuccess(result);
} else {
mainHandler.post(() -> callback.onSuccess(result));
}
}
public static <T> void dispatchError(SDKCallback<T> callback,
int code, String message) {
if (callback == null) return;
if (Looper.myLooper() == Looper.getMainLooper()) {
callback.onError(code, message);
} else {
mainHandler.post(() -> callback.onError(code, message));
}
}
}
六、主流面试题
6.1 基础知识
Q28: Android应用启动流程?
参考答案:
启动流程:
Launcher点击图标
↓
ActivityManagerService.startActivity
↓
Zygote进程fork新进程
↓
ActivityThread.main()
↓
创建Application
↓
Application.attach() → onCreate()
↓
创建Activity
↓
Activity.attach() → onCreate() → onStart() → onResume()
Q29: Activity启动模式及应用场景?
参考答案: | 启动模式 | 说明 | 应用场景 | |---------|------|---------| | standard | 默认模式,每次新建实例 | 普通页面 | | singleTop | 栈顶复用 | 消息推送页面 | | singleTask | 栈内复用,清除上方Activity | 主页、登录页 | | singleInstance | 独立任务栈 | 来电页面、闹钟提醒 |
Q30: Handler机制详解?
参考答案:
// 核心组件关系
Looper (消息循环)
├── 持有 MessageQueue
└── 持有 ThreadLocal
Handler (消息处理)
├── 持有 Looper
├── 持有 MessageQueue引用
└── 持有 Callback
MessageQueue (消息队列)
└── 存储Message链表
// 工作流程
Handler.sendMessage(Message)
→ MessageQueue.enqueueMessage()
→ Looper.loop() 不断取出Message
→ Handler.dispatchMessage()
→ Handler.handleMessage()
// 内存泄漏问题
// 非静态内部类持有外部类引用
// 解决:使用静态内部类 + 弱引用
private static class SafeHandler extends Handler {
private WeakReference<Activity> ref;
SafeHandler(Activity activity) {
ref = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = ref.get();
if (activity != null && !activity.isFinishing()) {
// 处理消息
}
}
}
6.2 性能优化
Q31: 内存泄漏常见场景及解决方案?
参考答案: | 场景 | 原因 | 解决方案 | |-----|------|---------| | Handler | 持有Activity引用 | 静态内部类+弱引用 | | 匿名内部类 | 持有外部类引用 | 改为静态类 | | 单例模式 | 持有Context | 使用Application Context | | 注册未取消 | 观察者未注销 | 及时unregister | | Bitmap未回收 | 大对象未释放 | recycle()或使用Glide | | WebView | 持有Activity | 独立进程或销毁时清理 |
Q32: ANR产生原因及排查方法?
参考答案:
ANR类型:
├── KeyDispatchTimeout (5秒)
│ └── 输入事件无响应
├── BroadcastTimeout (10秒前台/60秒后台)
│ └── 广播处理超时
├── ServiceTimeout (20秒前台/200秒后台)
│ └── Service启动超时
└── ContentProviderTimeout (10秒)
└── Provider发布超时
排查方法:
1. 查看 traces.txt 文件
adb pull /data/anr/traces.txt
2. 使用 StrictMode 检测
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
3. 使用 BlockCanary 监控
4. 使用 Systrace/Perfetto 分析
预防措施:
- 主线程不做耗时操作
- 使用线程池处理后台任务
- 合理使用AsyncTask/HandlerThread
Q33: APK瘦身方案?
参考答案:
1. 资源优化
├── 删除无用资源
├── 压缩图片(WebP格式)
├── 资源混淆 (andresguard)
└── 只保留必要分辨率资源
2. 代码优化
├── 代码混淆 (ProGuard/R8)
├── 移除无用代码
└── 使用更小的库
3. So库优化
├── 只保留必要架构(armeabi-v7a, arm64-v8a)
└── 使用NDK精简符号表
4. 其他优化
├── 插件化/动态加载
├── 资源动态下发
└── 使用Android App Bundle
6.3 多线程与并发
Q34: Android线程池使用?
参考答案:
// ThreadPoolExecutor核心参数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
// 常用线程池
// 1. 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(4);
// 2. 缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 3. 单线程池(顺序执行)
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 4. 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
// 推荐自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), // 核心线程数=CPU核心数
Runtime.getRuntime().availableProcessors() * 2, // 最大线程数
30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(128),
new ThreadFactoryBuilder().setNameFormat("worker-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者执行策略
);
Q35: synchronized与ReentrantLock区别?
参考答案: | 特性 | synchronized | ReentrantLock | |-----|--------------|---------------| | 锁获取 | JVM实现,自动释放 | 手动lock/unlock | | 公平性 | 非公平锁 | 可选公平/非公平 | | 中断 | 不可中断 | 可中断 lockInterruptibly() | | 条件变量 | 单一条件 | 多条件 Condition | | 性能 | 优化后差距不大 | 高竞争时稍优 | | 使用场景 | 简单同步 | 复杂同步需求 |
6.4 网络与存储
Q36: HTTPS握手过程?
参考答案:
HTTPS握手流程:
客户端 服务器
│ │
│ 1. ClientHello (支持的加密套件) │
│───────────────────────────────────────>│
│ │
│ 2. ServerHello (选择的加密套件) │
│ + 证书 │
│<───────────────────────────────────────│
│ │
│ 3. 验证证书 │
│ 生成预主密钥 │
│ 用公钥加密预主密钥 │
│───────────────────────────────────────>│
│ │
│ 4. 服务端用私钥解密 │
│ 双方生成会话密钥 │
│ │
│ 5. 完成握手,开始加密通信 │
│<──────────────────────────────────────>│
Q37: SharedPreferences的优化方案?
参考答案:
SP的问题:
├── 主线程IO操作导致ANR
├── 全量写入,效率低
└── 多进程不安全
优化方案:
1. 使用 commit() 改为 apply()
- commit() 同步写入,可能阻塞
- apply() 异步写入
2. 按功能拆分SP文件
- 避免单个文件过大
3. 使用 MMKV 替代
- 腾讯开源,性能优异
- 支持多进程
- 增量写入
4. 使用 Jetpack DataStore
- 协程友好
- 数据一致性保证
- 支持事务
6.5 架构与设计
Q38: MVP、MVVM、MVI架构对比?
参考答案:
MVP (Model-View-Presenter)
├── View: UI展示,用户交互
├── Presenter: 业务逻辑,连接View和Model
└── Model: 数据层
优点: View和Model解耦,便于单元测试
缺点: Presenter可能过于臃肿,接口过多
MVVM (Model-View-ViewModel)
├── View: UI展示,绑定ViewModel
├── ViewModel: 业务逻辑,数据驱动UI
└── Model: 数据层
优点: 数据驱动,减少UI更新代码
缺点: 数据绑定可能影响性能
MVI (Model-View-Intent)
├── View: UI展示,发送Intent
├── Intent: 用户意图
├── Model: 不可变状态
└── State: 单一数据源
优点: 单向数据流,状态可预测
缺点: 状态管理复杂,学习成本高
Q39: 组件化与插件化区别?
参考答案: | 特性 | 组件化 | 插件化 | |-----|--------|--------| | 编译时 | 各模块独立编译 | 插件动态加载 | | 安装 | 打包在主APK中 | 可独立更新 | | 加载 | ClassLoader加载 | 动态加载Dex/APK | | 热更新 | 不支持 | 支持 | | 复杂度 | 较低 | 较高 | | 应用场景 | 大型项目解耦 | 动态功能、热修复 |
Q40: 如何设计一个网络请求框架?
参考答案:
// 核心模块设计
public class NetworkFramework {
// 1. 请求构建
public static class Request {
private String url;
private String method;
private Map<String, String> headers;
private Map<String, Object> params;
private Object body;
}
// 2. 响应处理
public static class Response<T> {
private int code;
private String message;
private T data;
private long costTime;
}
// 3. 拦截器机制
public interface Interceptor {
Response intercept(Chain chain);
interface Chain {
Request request();
Response proceed(Request request);
}
}
// 4. 核心执行器
public class RealCall {
private List<Interceptor> interceptors = new ArrayList<>();
public Response execute(Request request) {
// 构建责任链
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, 0, request);
return chain.proceed(request);
}
}
// 5. 默认拦截器
// - 重试拦截器
// - 日志拦截器
// - 缓存拦截器
// - 连接拦截器
}
// 使用示例
NetworkFramework.getInstance()
.newRequest()
.url("https://api.example.com/payment/verify")
.method("POST")
.header("Authorization", "Bearer " + token)
.body(verifyRequest)
.execute(new Callback<VerifyResponse>() {
@Override
public void onSuccess(VerifyResponse response) {
// 处理成功
}
@Override
public void onError(int code, String message) {
// 处理错误
}
});
6.6 最新技术
Q41: Kotlin协程在SDK中的应用?
参考答案:
// 协程处理异步支付流程
class PaymentRepository(
private val paymentApi: PaymentApi,
private val paymentDao: PaymentDao
) {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
// 支付验证
suspend fun verifyPayment(purchaseToken: String): Result<VerifyResponse> {
return withContext(Dispatchers.IO) {
try {
val response = paymentApi.verifyPurchase(purchaseToken)
if (response.isSuccess) {
// 保存订单
paymentDao.insertOrder(response.order)
Result.success(response)
} else {
Result.failure(PaymentException(response.message))
}
} catch (e: Exception) {
Result.failure(e)
}
}
}
// 流式处理订单状态
fun observeOrderStatus(orderId: String): Flow<OrderStatus> {
return paymentDao.getOrderFlow(orderId)
.map { it.status }
.distinctUntilChanged()
}
// 超时处理
suspend fun verifyWithTimeout(token: String): Result<VerifyResponse> {
return withTimeoutOrNull(10_000) {
verifyPayment(token)
} ?: Result.failure(TimeoutException())
}
}
Q42: Jetpack Compose在SDK UI中的应用?
参考答案:
// 支付弹窗组件
@Composable
fun PaymentDialog(
products: List<Product>,
onProductSelected: (Product) -> Unit,
onDismiss: () -> Unit
) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(8.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "选择商品",
style = MaterialTheme.typography.h6
)
Spacer(modifier = Modifier.height(16.dp))
LazyColumn {
items(products) { product ->
ProductItem(
product = product,
onClick = { onProductSelected(product) }
)
}
}
}
}
}
}
@Composable
fun ProductItem(
product: Product,
onClick: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = product.name)
Spacer(modifier = Modifier.weight(1f))
Text(
text = product.price,
color = MaterialTheme.colors.primary
)
}
}
附录
A. 各平台支付文档链接
B. 常见支付状态码对照表
| 状态 | 华为 | 三星 | |
|---|---|---|---|
| 成功 | 0 | 0 | 200 |
| 取消 | 1 | 600002 | 201 |
| 已拥有 | 7 | 600011 | 202 |
| 网络错误 | 12 | 600003 | 203 |
C. 面试准备建议
- 基础知识:深入理解Android生命周期、IPC机制、Handler原理
- 支付专项:熟悉各平台支付流程、安全验证、异常处理
- 架构能力:掌握设计模式、组件化、模块化设计
- 性能优化:内存优化、启动优化、APK瘦身
- 新技术:Kotlin协程、Jetpack Compose、Flutter
文档版本: v1.0 最后更新: 2026年