Android应用内购买NullPointerException

5
我遇到了一个错误,让我很烦恼,因为它只在某些情况下发生,但我真的不知道什么时候会发生,也许有更专业的人可以帮助我:
从Google教程中,我学习了制作应用内购买的代码,简要地说,这段代码做了以下事情:
1. 初始化应用内服务。 2. 检索对象的价格。 3. 如果用户点击“购买”按钮,则启动应用内购买。
现在,在我所做的所有测试中,这个功能都正常工作(使用了多台设备和API),但我收到了大量报告,说:在mHelper上抛出了NullPointerException。
我认为这是在onDestroy()方法中发生的,当服务被销毁时,但我不确定,并且我无法解决它(完整的错误日志在最后)。
以下是我尽可能清理并注释的代码:
IabHelper mHelper;
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener;
Activity c;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_show_room);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    c=this;

    //Initialize the In-App Service
    mHelper = new IabHelper(this, "my_key");
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {
                if (!result.isSuccess()) {
                        //Problem setting up In-app Billing
                    return;
                }
                if (mHelper == null) return;

                //Ask for the price
                List additionalSkuList = new ArrayList();
                additionalSkuList.add("SKU_ID");
                try {
                    mHelper.queryInventoryAsync(true, additionalSkuList, mQueryFinishedListener);
                }catch (Exception e){
                    //Fail while asking the price
                }
            }
        });

    //Buy Button Listener
    b_buy.setOnClickListener(new View.OnClickListener()
    {
        public void onClick(View V)
        {
            try {
            String payload= "my_payload"
            mHelper.launchPurchaseFlow(c, "SKU_ID",1111,mPurchaseFinishedListener, payload);
            } catch (Exception e) {
                //Error launching purchase flow. Another async operation in progress
            }
        }
    });

    // Callback for when a purchase is finished
    mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            if (result.isFailure()) {
                //Error while buying
                return;
            }

            if (purchase.getSku().equals("SKU_ID")) {
                // bought the premium upgrade!
            }
            }
        };
}

//For retrieve the price:
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        if (result.isFailure()) {
            return;
        }
        String z = inventory.getSkuDetails("SKU_ID").getPrice();
        //The price of the object is + z !!!
    }

};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (mHelper == null) return;

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

@Override
public void onDestroy() {
    super.onDestroy();
        if (mHelper != null) mHelper.dispose();
        mHelper = null;
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    finish();
}
}

以下是错误信息:

Exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
com.myproj.util.IabHelper.startSetup (IabHelper.java)
__null__.dispose (IabHelper.java)
__null__.launchPurchaseFlow (IabHelper.java)
__null__.handleActivityResult (IabHelper.java)
__null__.queryInventory (IabHelper.java)
__null__.queryInventoryAsync (IabHelper.java)
__null__.getResponseDesc (IabHelper.java)
__null__.checkSetupDone (IabHelper.java)
__null__.getResponseCodeFromBundle (IabHelper.java)
__null__.getResponseCodeFromIntent (IabHelper.java)
__null__.queryPurchases (IabHelper.java)
__null__.querySkuDetails (IabHelper.java)
com.myproj.util.IabHelper.startSetup (IabHelper.java)
__null__.dispose (IabHelper.java)
__null__.launchPurchaseFlow (IabHelper.java)
__null__.handleActivityResult (IabHelper.java)
__null__.queryInventory (IabHelper.java)
__null__.queryInventoryAsync (IabHelper.java)
__null__.getResponseDesc (IabHelper.java)
__null__.checkSetupDone (IabHelper.java)
__null__.getResponseCodeFromBundle (IabHelper.java)
__null__.getResponseCodeFromIntent (IabHelper.java)
__null__.queryPurchases (IabHelper.java)
__null__.querySkuDetails (IabHelper.java)
com.myproj.util.IabHelper.startSetup (IabHelper.java)
__null__.dispose (IabHelper.java)
__null__.launchPurchaseFlow (IabHelper.java)
__null__.handleActivityResult (IabHelper.java)
__null__.queryInventory (IabHelper.java)
__null__.queryInventoryAsync (IabHelper.java)
__null__.getResponseDesc (IabHelper.java)
__null__.checkSetupDone (IabHelper.java)
__null__.getResponseCodeFromBundle (IabHelper.java)
__null__.getResponseCodeFromIntent (IabHelper.java)
__null__.queryPurchases (IabHelper.java)
__null__.querySkuDetails (IabHelper.java)
com.myproj.util.IabHelper$2.run (IabHelper.java)
java.lang.Thread.run (Thread.java:818)

这里是Google IabHelper类的一些复杂方法(可能你不需要阅读此内容),它们只是在错误日志中提到并由Google编写:

Dispose:

public void dispose() {
    logDebug("Disposing.");
    mSetupDone = false;
    if (mServiceConn != null) {
        logDebug("Unbinding from service.");
        if (mContext != null) mContext.unbindService(mServiceConn);
    }
    mDisposed = true;
    mContext = null;
    mServiceConn = null;
    mService = null;
    mPurchaseListener = null;
}

开始设置:

    public void startSetup(final OnIabSetupFinishedListener listener) {
    // If already set up, can't do it again.
    checkNotDisposed();
    if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");

    // Connection to IAB service
    logDebug("Starting in-app billing setup.");
    mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            logDebug("Billing service disconnected.");
            mService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (mDisposed) return;
            logDebug("Billing service connected.");
            mService = IInAppBillingService.Stub.asInterface(service);
            String packageName = mContext.getPackageName();
            try {
                logDebug("Checking for in-app billing 3 support.");

                // check for in-app billing v3 support
                int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);
                if (response != BILLING_RESPONSE_RESULT_OK) {
                    if (listener != null) listener.onIabSetupFinished(new IabResult(response,
                            "Error checking for billing v3 support."));

                    // if in-app purchases aren't supported, neither are subscriptions.
                    mSubscriptionsSupported = false;
                    return;
                }
                logDebug("In-app billing version 3 supported for " + packageName);

                // check for v3 subscriptions support
                response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);
                if (response == BILLING_RESPONSE_RESULT_OK) {
                    logDebug("Subscriptions AVAILABLE.");
                    mSubscriptionsSupported = true;
                }
                else {
                    logDebug("Subscriptions NOT AVAILABLE. Response: " + response);
                }

                mSetupDone = true;
            }
            catch (RemoteException e) {
                if (listener != null) {
                    listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
                                                "RemoteException while setting up in-app billing."));
                }
                e.printStackTrace();
                return;
            }

            if (listener != null) {
                listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
            }
        }
    };

    Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
    serviceIntent.setPackage("com.android.vending");
    if (!mContext.getPackageManager().queryIntentServices(serviceIntent, 0).isEmpty()) {
        // service available to handle that Intent
        mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
    }
    else {
        // no service available to handle that Intent
        if (listener != null) {
            listener.onIabSetupFinished(
                    new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
                    "Billing service unavailable on device."));
        }
    }
}

以及IabHelper的构造函数:

    public IabHelper(Context ctx, String base64PublicKey) {
        mContext = ctx.getApplicationContext();
        mSignatureBase64 = base64PublicKey;
        logDebug("IAB helper created.");
    }

这里 是完整的 IabHelper 类。

总结:

请告诉我你是否能看到 变量 mHelperNullPointerException 在何时/何处被抛出。 我从代码和虚拟/物理设备测试中都无法看出,因为它们都正常工作。

我非常确定错误是在 Activity 关闭后抛出的,但我不知道如何修复它。

如果需要更多信息,请写下来。谢谢大家!


1
你能否更新你的问题并提供完整的堆栈跟踪信息? - Amod Gokhale
当然,问题已更新。 - GMX
@GMX,在代码中 mHelper 的使用会抛出 NPE 异常吗? - user1079425
在OnDestroy()中,99%的情况下,当我更改Activity时会崩溃,但只有在极少数情况下。 - GMX
@GMX 发布你的应用程序:Gradle - Charuක
显示剩余2条评论
2个回答

2

尽可能避免使用getApplicationContext(),因为这样会大大增加强制关闭的概率。

请使用Activity上下文。如果您在Activity中创建IabHelper对象,则传递ActivityName.this(即Activity上下文)。

如果您在Fragment中,则使用getActivity()。

在IabHelper类中使用如下:

public IabHelper(Context ctx, String base64PublicKey) {
    mContext = ctx;
    mSignatureBase64 = base64PublicKey;
    logDebug("IAB helper created.");
}

我希望它能正常工作。


我会尽快测试这个。 - GMX

0
你将new IabHelper.OnIabSetupFinishedListener() {...}传递给startSetup()函数,但是mHelper.startSetup()并没有保留引用,而onCreate()也没有保留引用。看起来你有一个属性(mPurchaseListener)来保存一个监听器,但是没有使用它——而是创建了一个匿名类并将其传递给startSetup()函数……然后你的实例被垃圾回收了,因为没有引用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接