安卓手游sdk 面试题

44 阅读18分钟

安卓手游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. 各平台支付文档链接

平台文档地址
Google Playdeveloper.android.com/google/play…
华为IAPdeveloper.huawei.com/consumer/cn…
三星IAPdeveloper.samsung.com/iap
小米支付dev.mi.com/console/doc…
OPPO支付open.oppomobile.com/wiki/doc#id…
vivo支付dev.vivo.com.cn/documentCen…

B. 常见支付状态码对照表

状态Google华为三星
成功00200
取消1600002201
已拥有7600011202
网络错误12600003203

C. 面试准备建议

  1. 基础知识:深入理解Android生命周期、IPC机制、Handler原理
  2. 支付专项:熟悉各平台支付流程、安全验证、异常处理
  3. 架构能力:掌握设计模式、组件化、模块化设计
  4. 性能优化:内存优化、启动优化、APK瘦身
  5. 新技术:Kotlin协程、Jetpack Compose、Flutter

文档版本: v1.0 最后更新: 2026年