Android 应用内计费不';t工作:“;未设置IAB帮助程序“;

Android 应用内计费不';t工作:“;未设置IAB帮助程序“;,android,in-app-purchase,google-play,in-app-billing,Android,In App Purchase,Google Play,In App Billing,我尝试在我的应用程序中包含应用内计费,出于测试目的,整个过程基于应用内计费版本3的“平凡驱动”示例(并实现演示的“util”子目录中提供的未修改版本的IAB文件),但它不适用于我-在LogCat上,就在应用程序因错误而终止之前,它给出了一条消息“应用内计费错误:操作的非法状态(launchPurchaseFlow):IAB Helper未设置。”(在启动StartRegister()函数并向我提供日志消息“注册按钮已单击;启动购买流以进行升级。”) 知道这里出了什么问题吗 以下是我的代码的相关部

我尝试在我的应用程序中包含应用内计费,出于测试目的,整个过程基于应用内计费版本3的“平凡驱动”示例(并实现演示的“util”子目录中提供的未修改版本的IAB文件),但它不适用于我-在LogCat上,就在应用程序因错误而终止之前,它给出了一条消息“应用内计费错误:操作的非法状态(launchPurchaseFlow):IAB Helper未设置。”(在启动StartRegister()函数并向我提供日志消息“注册按钮已单击;启动购买流以进行升级。”)

知道这里出了什么问题吗

以下是我的代码的相关部分:

package com.mytest;

(..)
import com.mytest.iab.IabHelper; // the originals from the demo example, unmodified
import com.mytest.iab.IabResult;
import com.mytest.iab.Inventory;
import com.mytest.iab.Purchase;

public class Result3 extends Activity implements OnClickListener {

private static final String TAG = "BillingService";

private Context mContext;

boolean mIsRegistered = false;

    // this has already been set up for my app at the publisher's console
static final String IS_REGISTERED = "myregistered";

static final int RC_REQUEST = 10001;

// The helper object
IabHelper mHelper; 

/** Call when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.result3);
    mContext = this;

    String base64EncodedPublicKey = "[my public key]"; // (from publisher's console for my app)

    // Create the helper, passing it our context and the public key to verify signatures with
    Log.d(TAG, "Creating IAB helper.");
    mHelper = new IabHelper(this, base64EncodedPublicKey);

    // enable debug logging (for a production application, you should set this to false).
    mHelper.enableDebugLogging(true);

    // Start setup. This is asynchronous and the specified listener
    // will be called once setup completes.
    Log.d(TAG, "Starting setup.");
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        public void onIabSetupFinished(IabResult result) {
            Log.d(TAG, "Setup finished.");

            if (!result.isSuccess()) {
                complain("Problem setting up in-app billing: " + result);
                return;
            }

            // Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own.
            Log.d(TAG, "Setup successful. Querying inventory.");
            mHelper.queryInventoryAsync(mGotInventoryListener);
        }
    });

   // Set the onClick listeners
   findViewById(R.id.btnPurchase).setOnClickListener(this);
}

// Listener that's called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        Log.d(TAG, "Query inventory finished.");
        if (result.isFailure()) {
            complain("Failed to query inventory: " + result);
            return;
        }

        Log.d(TAG, "Query inventory was successful.");

        // Do we have the premium upgrade?
        mIsRegistered = inventory.hasPurchase(IS_REGISTERED);
        Log.d(TAG, "User is " + (mIsRegistered ? "REGISTERED" : "NOT REGISTERED"));

        setWaitScreen(false);
        Log.d(TAG, "Initial inventory query finished; enabling main UI.");
    }
};      

// User clicked the "Register" button.
private void startRegistered() {
    Log.d(TAG, "Register button clicked; launching purchase flow for upgrade.");
    setWaitScreen(true);
    mHelper.launchPurchaseFlow(this, IS_REGISTERED, RC_REQUEST, mPurchaseFinishedListener);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);

    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here's where you'd
        // perform any handling of activity results not related to in-app billing..
        super.onActivityResult(requestCode, resultCode, data);
    }
    else {
        Log.d(TAG, "onActivityResult handled by IABUtil.");
    }
}

// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
        Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
        if (result.isFailure()) {
            // Oh noes!
            complain("Error purchasing: " + result);
            setWaitScreen(false);
            return;
        }

        Log.d(TAG, "Purchase successful.");

        if (purchase.getSku().equals(IS_REGISTERED)) {
            Log.d(TAG, "User has registered..");
            alert("Thank you.");
            mIsRegistered = true;
            setWaitScreen(false);
        }
    }
};

// We're being destroyed. It's important to dispose of the helper here!
@Override
public void onDestroy() {
    // very important:
    Log.d(TAG, "Destroying helper.");
    if (mHelper != null) mHelper.dispose();
    mHelper = null;
}

void complain(String message) {
    Log.e(TAG, "**** Register Error: " + message);
    alert("Error: " + message);
}

void setWaitScreen(boolean set) {
    // just a dummy for now
}

void alert(String message) {
    AlertDialog.Builder bld = new AlertDialog.Builder(this);
    bld.setMessage(message);
    bld.setNeutralButton("OK", null);
    Log.d(TAG, "Showing alert dialog: " + message);
    bld.create().show();
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btnPurchase:
        startRegistered();
        break;
    default:
        break;
    }
}
}

这里有来自Logcat的更多行:

12-20 01:06:36.701: D/dalvikvm(299): GC_FOR_MALLOC freed 4262 objects / 308592 bytes in 84ms
12-20 01:06:36.701: D/webviewglue(299): nativeDestroy view: 0x2ea718
12-20 01:06:36.771: W/webcore(299): Can't get the viewWidth after the first layout
12-20 01:07:07.111: W/webcore(299): Can't get the viewWidth after the first layout
12-20 01:07:18.510: D/webviewglue(299): nativeDestroy view: 0x2dd458
12-20 01:07:18.510: D/dalvikvm(299): GC_FOR_MALLOC freed 6042 objects / 544504 bytes in 50ms
12-20 01:07:18.530: D/webviewglue(299): nativeDestroy view: 0x2ea8d0
12-20 01:07:18.660: D/BillingService(299): Creating IAB helper.
12-20 01:07:18.660: D/BillingService(299): Starting setup.
12-20 01:07:18.660: D/IabHelper(299): Starting in-app billing setup.
12-20 01:07:19.621: W/webcore(299): Can't get the viewWidth after the first layout
12-20 01:07:20.160: W/webcore(299): Can't get the viewWidth after the first layout
12-20 01:07:32.481: D/webviewglue(299): nativeDestroy view: 0x3f88e8
12-20 01:07:32.491: D/dalvikvm(299): GC_FOR_MALLOC freed 5798 objects / 513640 bytes in 50ms
12-20 01:07:32.511: D/BillingService(299): Register button clicked; launching purchase flow for upgrade.    
12-20 01:07:32.511: E/IabHelper(299): In-app billing error: Illegal state for operation (launchPurchaseFlow): IAB helper is not set up.
12-20 01:07:32.521: D/AndroidRuntime(299): Shutting down VM
12-20 01:07:32.521: W/dalvikvm(299): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
12-20 01:07:32.541: E/AndroidRuntime(299): FATAL EXCEPTION: main
12-20 01:07:32.541: E/AndroidRuntime(299): java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: launchPurchaseFlow
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.test_ed.iab.IabHelper.checkSetupDone(IabHelper.java:673)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:315)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:294)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.test_ed.Result3.startRegistered(Result3.java:157)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.test_ed.Result3.onClick(Result3.java:248)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.view.View.performClick(View.java:2408)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.view.View$PerformClick.run(View.java:8816)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.os.Handler.handleCallback(Handler.java:587)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.os.Handler.dispatchMessage(Handler.java:92)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.os.Looper.loop(Looper.java:123)
12-20 01:07:32.541: E/AndroidRuntime(299):  at android.app.ActivityThread.main(ActivityThread.java:4627)
12-20 01:07:32.541: E/AndroidRuntime(299):  at java.lang.reflect.Method.invokeNative(Native Method)
12-20 01:07:32.541: E/AndroidRuntime(299):  at java.lang.reflect.Method.invoke(Method.java:521)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
12-20 01:07:32.541: E/AndroidRuntime(299):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
12-20 01:07:32.541: E/AndroidRuntime(299):  at dalvik.system.NativeStart.main(Native Method)

执行purchaseFlow函数时出现相同问题。看看Google示例中的Activity类,特别是方法
protectedvoidonActivityResult(int-requestCode,int-resultCode,Intent-data)
。你可能忘了实现这个。该功能对于整个机构正常工作至关重要

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.i(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);

    // Pass on the activity result to the helper for handling
    if (!inappBillingHelper.handleActivityResult(requestCode, resultCode, data)) {
        super.onActivityResult(requestCode, resultCode, data);
    }
    else {
        Log.i(TAG, "onActivityResult handled by IABUtil.");
    }
}
编辑:
此外,当你在手机上的gmail账户密码错误时,这个问题也会出现(这件事今天发生在我身上)。当然,所有Inapp计费功能都应该在手机上进行测试,但我认为这是显而易见的。

我刚刚结束了对完全相同问题的思考。IbaHelper安装程序启动,但在此之后,没有其他事情发生。点击应用程序内购买会返回与您相同的错误

我发现:我只使用eclipse的模拟器。当我读到需要某个Google Play版本时,我注意到Google Play在我的测试模拟驱动器上完全缺失了

当我使用一部真正的手机时,它工作得完美无缺!所以,如果你碰巧还在这个问题上,试着使用一个真正的设备(如果你有一个可用的)。这应该能奏效

基本问题是startRegistered()是在直接响应UI用户单击时调用的,而IabHelper对象的设置是异步触发的,因此在通过ONIBsetupFinished()接收到异步响应之前,无法知道已完成

您的startRegistered()方法由用户单击触发,该方法调用launchPurchaseFlow(),这反过来要求IabHelper对象已经完成安装,但是如果用户在收到确认之前单击触发购买(因为安装失败或因为用户在绘图时异常快速),然后安装程序将无法完成,launchPurchaseFlow()将报告您看到的错误。在logcat的情况下,延迟是14秒,这通常是足够的时间,但是……在这种情况下可能不是。或者,可能是出了什么问题,无论你等了多久,你都无法连接

在您的logcat中,没有指示“计费服务已连接”的消息,如果要完成设置,这是必须首先发生的事情之一。由于没有出现这种情况,因此您也看不到来自onIabSetupFinished()的任何消息(成功或失败)

这是一个棘手的问题,因为需要异步响应。一种方法是禁用用于触发购买的按钮,直到onIabSetupFinished()成功返回。这将阻止在成功设置IabHelper对象之前触发购买。当然,如果安装失败,您将有一个不起作用的按钮,但至少您可以告诉用户发生了什么(通过显示一条消息,表明您正在等待安装完成-例如,作为按钮文本的一部分)

即使如此,一旦你的购买开始,用户看到付款对话框,你的应用程序也有可能会经历一个onStop()循环,在用户考虑购买时从内存中刷新你的应用程序(因为购买对话框是Google Play的一部分,而不是你的应用程序的一部分,而且操作系统可能需要内存来运行它,并且可以通过停止你的应用程序来获得内存)。这将破坏你的应用程序()对象,然后必须再次创建并异步设置。而且,由于这是在onCreate()方法中异步触发的,因此Google Play服务可能会调用onActivityResult(),以在IabHelper对象的设置完成之前以及在onActivityResult()中报告用户的购买操作您需要使用IABHeloper实例,这可能会导致错误。似乎您必须做好任何准备


这应该会让你了解你正在处理的事情。IAB之所以困难,正是因为这些原因——多线程异步的东西(例如,设置vs.购买vs.Android操作系统停止你的应用程序获取内存供你的应用程序等待获取购买结果的Google Play应用程序购买操作使用的操作)。实现了很多功能(包括通过驱动示例)是脆弱的,因为它隐含地依赖于你的应用程序留在内存中,而事实上它可能会被回收,或者因为它依赖于比赛条件(如设置)的一段在另一段之前完成(如购买启动)是的,等等。

我遇到的另一件事;虽然您的设备上可能有最新版本的google play,它支持最新版本的应用内计费,但其他用户可能没有。虽然理论上由此导致的崩溃应该出现在开发人员控制台中,但在我实现firebase之前,我无法看到这些崩溃…一个然后我看到了很多,最后我使用了一个try-catch,链接那些没有最新版本的google play或者在google play商店端遇到问题的用户
try {
        mHelper.launchPurchaseFlow(this, SKU_PRO_LT, RC_REQUEST,
                mPurchaseFinishedListener, payload);
    } catch (Exception e) { //with IabHelper.IabAsyncInProgressException the code still fatally crashes for some reason
        //complain("Error launching purchase flow. Another async operation in progress.");
        alert2("[error msg]");
        setWaitScreen(false);
    }