InAppBilling 在 AndroidManifest 中注册 BroadcastReceiver

InAppBilling registering BroadcastReceiver in AndroidManifest

我正在我的 Android 应用中实施应用内结算。一切正常,但是,我正在尝试将广播接收器从 activity 分离到清单中。 特别是在 Android 的简单驱动示例中提出了这个建议:

// Important: Dynamically register for broadcast messages about updated purchases.
// We register the receiver here instead of as a <receiver> in the Manifest
// because we always call getPurchases() at startup, so therefore we can ignore
// any broadcasts sent while the app isn't running.
// Note: registering this listener in an Activity is a bad idea, but is done here
// because this is a SAMPLE. Regardless, the receiver must be registered after
// IabHelper is setup, but before first call to getPurchases().

目前有一个 class 扩展 BroadcastReceiver:

public class IabBroadcastReceiver extends BroadcastReceiver {
/**
 * The Intent action that this Receiver should filter for.
 */
public static final String ACTION = "com.android.vending.billing.PURCHASES_UPDATED";
private final IabBroadcastListener mListener;

public IabBroadcastReceiver(IabBroadcastListener listener) {
    mListener = listener;
}

@Override
public void onReceive(Context context, Intent intent) {
    if (mListener != null) {
        mListener.receivedBroadcast();
    }
}

/**
 * Listener interface for received broadcast messages.
 */
public interface IabBroadcastListener {
    void receivedBroadcast();
}
}

还有一个 class 实现 IabBroadcastReceiver.IabBroadcastListener:

public class Subscription extends AppCompatActivity implements 
IabBroadcastReceiver.IabBroadcastListener {

IabHelper mHelper;

// Provides purchase notification while this app is running
IabBroadcastReceiver mBroadcastReceiver;

...


// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        Log.d("IAB", "Query inventory finished.");

        if (mHelper == null) return;

        if (result.isFailure()) {
            Log.d("IAB", "Failed to query inventory: " + result);
            return;
        }

        if (inventory.getSkuDetails(SKU_MONTHLY_TTS) != null
                && inventory.getSkuDetails(SKU_YEARLY_TTS) != null) {
            ...
          }


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

        /*
         * Check for items we own. Notice that for each purchase, we check
         * the developer payload to see if it's correct! See
         * verifyDeveloperPayload().
         */

        ...

        Log.d("IAB", "Initial inventory query finished; enabling main UI.");
    }
};
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
        Log.d("IAB", "Purchase finished: " + result + ", purchase: " + purchase);

        // if we were disposed of in the meantime, quit.
        if (mHelper == null) return;

        if (result.isFailure()) {
            Log.d("IAB", "Error purchasing: " + result);
            return;
        }
        if (!verifyDeveloperPayload(purchase)) {
            Log.d("IAB", "Error purchasing. Authenticity verification failed.");
            return;
        }

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

        ...
    }
};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_subscription);


    mHelper = new IabHelper(this, compiledKy);

    mHelper.enableDebugLogging(true);

    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        @Override
        public void onIabSetupFinished(IabResult result) {
            Log.d("Subscription", "InSetUpFinished: " + result);
            if (!result.isSuccess()) {
                Log.d("Subscription", "Problem setting up In-app Billing: " + result);
                return;
            }

            if (mHelper == null) return;

            // Important: Dynamically register for broadcast messages about updated purchases.
            // We register the receiver here instead of as a <receiver> in the Manifest
            // because we always call getPurchases() at startup, so therefore we can ignore
            // any broadcasts sent while the app isn't running.
            // Note: registering this listener in an Activity is a bad idea, but is done here
            // because this is a SAMPLE. Regardless, the receiver must be registered after
            // IabHelper is setup, but before first call to getPurchases().
            mBroadcastReceiver = new IabBroadcastReceiver(Subscription.this);
            IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
            registerReceiver(mBroadcastReceiver, broadcastFilter);

            // IAB is fully set up. Now, let's get an inventory of stuff we own.
            Log.d("IAB", "Setup successful. Querying inventory.");
            try {
                List<String> additionalSkuList = new ArrayList<String>();
                ...
                mHelper.queryInventoryAsync(true, null, additionalSkuList, mGotInventoryListener);
            } catch (IabHelper.IabAsyncInProgressException e) {
                Log.d("IAB", "Error querying inventory. Another async operation in progress.");
            }
        }
    });

    Button monthlySubButton = (Button) findViewById(R.id.monthlySubButton);
    monthlySubButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (!mHelper.subscriptionsSupported()) {
                Log.d("IAB","Subscriptions not supported on your device yet. Sorry!");
                return;
            }
            try {
                ...
                mHelper.launchPurchaseFlow(Subscription.this, ..., IabHelper.ITEM_TYPE_SUBS,
                        oldSku, 10001, mPurchaseFinishedListener, "");
            } catch (IabHelper.IabAsyncInProgressException e) {
                Log.d("IAB", e.getMessage());
            }
        }
    });

    ...
}

/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
    String payload = p.getDeveloperPayload();

    ...
    return true;
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (mBroadcastReceiver != null) {
        unregisterReceiver(mBroadcastReceiver);
    }

    Log.d("IAB", "Destroying helper.");
    if (mHelper != null) {
        mHelper.disposeWhenFinished();
        mHelper = null;
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.d("IAB", "onActivityResult(" + requestCode + "," + resultCode + "," + data);
    if (mHelper == null) return;

    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        super.onActivityResult(requestCode, resultCode, data);
    }
    else {
        Log.d("IAB", "onActivityResult handled by IABUtil.");
    }
}

@Override
public void receivedBroadcast() {
    // Received a broadcast notification that the inventory of items has changed
    Log.d("IAB", "Received broadcast notification. Querying inventory.");
    try {
        mHelper.queryInventoryAsync(mGotInventoryListener);
    } catch (IabHelper.IabAsyncInProgressException e) {
        Log.d("IAB", "Error querying inventory. Another async operation in progress.");
    }
}
}

我正在尝试在清单中添加接收器,但出现错误:

</application>
...
<activity
        android:name=".controller.Subscription"
        android:label="Subscription"
        android:parentActivityName=".controller.MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".controller.MainActivity" />
    </activity>

    <receiver android:name=".controller.Subscription"  android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
        </intent-filter>
    </receiver>
</application>

Error Message:.controller.Subscription is not assignable to 'android.content.BroadcastReceiver'

Subscription class 在正确的目录中(在控制器包下)。 我的 Subscription class 是否必须扩展 IabBroadcastReceiver class 而不是实施 IabBroadcastReceiver.IabBroadcastListener?我仍然想扩展 AppCompactActivity,想知道是否有任何方法可以解决这个问题。似乎没有在线示例显示如何使用在清单中注册的广播接收器实现应用内计费 api。提前感谢您的帮助!

这通常是因为您没有在 \controller\ 包下的 订阅 class。您的代码中的一切看起来都很好(假设您在清单中添加了相应的 intentfilters)。因此,请确保您的 订阅 class 位于正确的包(或文件夹)中。

controller.Subscription is not assignable to 'android.content.BroadcastReceiver'

这意味着 Subscription 不是 BroadcastReceiver 的后代。您在清单中将 Subscription 注册为 receiver,但实际上它不是 BroadcastReceiver 的子 class。

Does my Subscription class have to extend IabBroadcastReceiver class and not be implementing IabBroadcastReceiver.IabBroadcastListener instead?

为了在清单中将 class 注册为 receiver,它应该是 BroadcastReceiver 的后代(直接或间接)。因此,Subscription 应该 extends BroadcastReceiverextends IabBroadcastReceiver

I'll still like to extend AppCompactActivity.

您不能同时创建 class activity 和接收器(Java 不支持多重继承)。

您仍然可以通过清单将 IabBroadcastReceiver 注册为 <receiver>。 但我想知道这背后的原因是什么?显然,当您的应用程序处于非活动状态时,您永远不会收到任何广播,因为您应该在您的应用程序内启动购买流程,这就是动态注册和取消注册 BroadcastReceiver 更有意义的原因。请注意,通过清单注册接收器会让您收到来自其他应用程序的购买广播,您很可能对此不感兴趣。

参见docs

Don't register this broadcast receiver in the app manifest. Declaring the receiver in the manifest can cause the system to launch the app to handle the intent if the user makes a purchase while the app isn't running. This behavior is not necessary and may be annoying to the user. To find out about any purchases the user made while the app wasn't running, call getPurchases() when the user launches the app.