Reputation: 531
I need some input here on how connection is made and querying the sku details. I'm working on the tutorial and copying the in app billing logic over to my app.
https://codelabs.developers.google.com/codelabs/play-billing-codelab
I followed the tutorial without any issues. The issue lies in how the connection is made and then querying the sku details.
When I made an instance of BillingManager class, it'll attempt to make a connection -
public BillingManager(Activity activity) {
mActivity = activity;
mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
@Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
Then, I will be making async query to get the sku details -
private void handleManagerAndUiReady() {
// Start querying for SKUs
List<String> inAppSkus = mBillingProvider.getBillingManager()
.getSkus(SkuType.INAPP);
mBillingProvider.getBillingManager().querySkuDetailsAsync(SkuType.INAPP,
inAppSkus,
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(int responseCode,
List<SkuDetails> skuDetailsList) {
if (responseCode == BillingResponse.OK
&& skuDetailsList != null) {
for (SkuDetails details : skuDetailsList) {
Log.w(TAG, "Got a SKU: " + details);
}
}
}
});
// Show the UI
displayAnErrorIfNeeded();
}
Then I got the listener getting an error yet the connection is made without any issues.
D/StoreListFragment: onCreate
I/StoreListFragment: SkuDetailsResponseListener response code: -1
D/StoreListFragment: onViewCreated
I/BillingManager: onBillingSetupFinished() response: 0
So I had to figure out for some time and gave up to check the basics of Play Billing Library -
https://medium.com/exploring-android/exploring-the-play-billing-library-for-android-55321f282929
That is where I found the solution, I just put the query in the connection where it is successfully connected. I realized the play billing library doesn't check the connection BEFORE it goes querying the sku details or am I doing wrong somewhere since the tutorial is working fine?
private void createBillingClient() {
mBillingClient = BillingClient.newBuilder(getActivity()).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(int billingResponse) {
if (billingResponse == BillingClient.BillingResponse.OK) {
Log.i(TAG, "onBillingSetupFinished() response: " + billingResponse);
//setting up a listener for the queries
SkuDetailsResponseListener responseListener = new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(int responseCode,
List<SkuDetails> skuDetailsList) {
Log.i(TAG, "response code: " + responseCode);
}
};
List<String> skuList = Arrays.asList("sku_01", "sku_02");
SkuDetailsParams skuDetailsParams = SkuDetailsParams.newBuilder()
.setSkusList(skuList).setType(BillingClient.SkuType.SUBS).build();
mBillingClient.querySkuDetailsAsync(skuDetailsParams, responseListener);
} else {
Log.w(TAG, "onBillingSetupFinished() error code: " + billingResponse);
}
}
@Override
public void onBillingServiceDisconnected() {
Log.w(TAG, "onBillingServiceDisconnected()");
}
});
}
I tried this logic to check if the connection is ready then execute the runnable like as trivial drive - citing this url. It looks like the logic doesn't check if the billing connection is pending....
https://github.com/zumrywahid/in_app_example
Is Billing Client connected? : false
Client is already in the process of connecting to billing service.
onBillingSetupFinished() error code: 5
Upvotes: 5
Views: 4551
Reputation: 1
I solved this issue in the following way:
I created a helper class that manages the billing and added listeners to it, so I can do things only when other actions are ready.
For example, I tried to show a list of in-app purchases in the onCreate
of the activity, but the list was always empty. That was because the result of querySkuDetailsAsync
was received after the code that displayed the list was executed.
I created a listener and I only display the list after the result is received.
interface BillingResponseIsAvailableListener {
void onBillingResponseIsAvailable(List<SkuDetails> theList);
}
public class BillingTools {
List<String> skuList = new ArrayList<>();
private List<BillingResponseIsAvailableListener> listeners = new ArrayList<>();
private BillingClient billingClient;
private Context context;
public BillingTools(Context context) {
this.context = context;
createSKUList(); //method to create the list of sku's for your app
}
public void addListener(BillingResponseIsAvailableListener toAdd) {
listeners.add(toAdd);
}
and where you query the sku list, just add the listener when the list is ready:
billingClient.querySkuDetailsAsync(params.build(), (billingResult2, skuDetailsList) -> {
if (billingResult2.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
for (BillingResponseIsAvailableListener bL : listeners) {
bL.onBillingResponseIsAvailable(skuDetailsList);
}
}
});
The above code is just an example, I recommend you create your own listeners for whatever you need to do. It is part of a method called queryOptionsList();
In the calling activity, just make the listener do whatever you need to do with the list:
BillingTools takeTheirMoney = new BillingTools(this);
takeTheirMoney.startBillingClient();
takeTheirMoney.addListener(new BillingResponseIsAvailableListener() {
@Override
public void onBillingResponseIsAvailable(List<SkuDetails> theList) {
showPurchaseOptions(findViewById(R.id.purchases_container), theList, takeTheirMoney);
}
});
takeTheirMoney.queryOptionsList();
If you are having problems with the connection not being ready, just add another listener to monitor when the connection is successful, and in that listener place additional actions (which can have their own listeners as well).
Upvotes: 0
Reputation: 439
BillingManager constructor is already starting connection and if you initialize the manager and immediately call any method that passes runnable to executeServiceRequest(Runnable runnable)
will also try to start connection at the same time. You can disable startServiceConnection()
in the constructor since connection status is always checked in executeServiceRequest()
and starts connection if needed.
Upvotes: 4