You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓应用IsPurchased函数触发NullPointerException,求偶发崩溃排查思路

Analyzing the Random NullPointerException in Your Android App's IsPurchased Method

Hey there, since you're more comfortable with C++ and mentioned you've added multiple safeguards without luck, let's walk through this NPE issue clearly and practically.

First, let's map the crash stack to your Java code: the NullPointerException hits at line 13 of IsPurchased, which directly corresponds to this line:

if (mOwnedItems.size()>0)

This tells us the most likely culprit is mOwnedItems being null when this line executes. Let's break down why this might happen, even though you note it's "filled elsewhere":

Possible Root Causes

  • Uninitialized or late initialization: The mOwnedItems Bundle might not have been assigned a value yet when the native GameThread calls IsPurchased. Since native threads run independently of the Android main thread, it's possible the code that fills mOwnedItems (like an in-app purchase query callback running on the main thread) hasn't completed before the native thread triggers this check. Think of this like a C++ pointer being dereferenced before it's assigned to a valid object.
  • Null assignment in error paths: When handling in-app purchase failures (e.g., network errors, user cancellation), the code responsible for filling mOwnedItems might be setting it to null instead of an empty Bundle. An empty Bundle would return 0 for size(), which is safe—but a null Bundle will throw an NPE when you call size().
  • Thread safety issues: If mOwnedItems is being modified or reset on the main thread while the native thread is accessing it, there's a race condition where the native thread could see a null value (even if it was previously valid). Unlike C++ where you might use mutexes or atomic variables, Java requires explicit synchronization for shared state across threads.

Why Your Existing Safeguards Didn't Catch This

Looking at your Java code, you've already added checks for theData and ownedSkus, but you missed a critical check for mOwnedItems itself. That's why the NPE is still happening—this is the unprotected null reference slipping through.

Fixes to Implement

Here are actionable steps tailored to your background:

  1. Add an immediate null check for mOwnedItems: This is the quickest fix to stop the crash. Modify your IsPurchased method like this:
    private boolean IsPurchased(String theData) {
        if (theData == null || mOwnedItems == null) return false; // Add mOwnedItems check here
        boolean aResult = false;
        if (mOwnedItems.size() > 0) {
            // Rest of your existing code...
        }
        return aResult;
    }
    
  2. Validate the "filled elsewhere" logic: Check the code that assigns mOwnedItems. Ensure it always assigns a valid Bundle (even an empty one) instead of null, especially in error scenarios. In C++ terms, this is like ensuring a pointer always points to a valid object, not a null address.
  3. Add thread safety if needed: If mOwnedItems is accessed across multiple threads, wrap accesses in a synchronized block, or mark mOwnedItems as volatile to ensure threads see the latest value. For example:
    private volatile Bundle mOwnedItems; // Mark as volatile for cross-thread visibility
    
    Or use a synchronized block when accessing it:
    synchronized(this) {
        if (mOwnedItems == null) return false;
        // Rest of the code that uses mOwnedItems
    }
    

Bonus: JNI Layer Check (Optional)

Since you're comfortable with C++, you could add a debug check in the JNI IsPurchased method to log if g_theGlobalRefToActivityInstance is null (though the crash stack points to the Java method, so this is less likely). But the Java null check for mOwnedItems is the priority here.

内容的提问来源于stack exchange,提问作者Raptisoft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:35:21