java - Java - app crash when using in app purchase
问题描述
So I have experienced some issue with in app purchase in my app.
I have started working on an old (8 months old) project I had earlier, but I am having some issue with app purchase. The app is already live on Play Store, so in app purchase is active.
In build.gradle (:app) I have changed from:
dependencies {
implementation 'com.android.billingclient:billing:2.2.1'
to this:
dependencies {
def billing_version = "3.0.0" // In App Purchase
implementation "com.android.billingclient:billing:$billing_version"
This is the full code of my UpgradeActivity.java:
package com.pinloop.testproj;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private List skuList = new ArrayList();
private String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
upgradeButton.setEnabled(true);
skuList.add(sku);
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (Object skuDetailsObject : skuDetailsList) {
final SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku() == sku)
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
upgradeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(skuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
});
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
Basically what happens when pressing the upgradeButton
is that the app crashes. I cannot figure out what I am doing wrong. I am using the correct SKU, and the app has been live on Play Store for over a year now.
This is the error log I get when pressing the upgradeButton
:
I/zygote: Do partial code cache collection, code=124KB, data=72KB
After code cache collection, code=124KB, data=72KB
Increasing code cache capacity to 512KB
D/EGL_emulation: eglMakeCurrent: 0xdb928540: ver 3 0 (tinfo 0xdb92bbd0)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.pinloop.testproj, PID: 13352
java.lang.IllegalArgumentException: SKU cannot be null.
at com.android.billingclient.api.BillingFlowParams$Builder.build(com.android.billingclient:billing@@3.0.0:23)
at com.pinloop.testproj.UpgradeActivity.upgradeAction(UpgradeActivity.java:130)
at com.pinloop.testproj.UpgradeActivity.access$000(UpgradeActivity.java:31)
at com.pinloop.testproj.UpgradeActivity$1.onClick(UpgradeActivity.java:56)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Not sure what to look at here, but I can see that java.lang.IllegalArgumentException: SKU cannot be null.
, but sku has already been declared: private String sku = "com.pinloop.testproj.pro";
. Any ideas?
解决方案
这是更新代码
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.aumhum.aumhum.R;
import java.util.ArrayList;
import java.util.List;
public class UpgradeActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private Button upgradeButton;
private TextView restoreButton;
private BillingClient billingClient;
private final List<String> skuList = new ArrayList();
private final String sku = "com.pinloop.testproj.pro";
private SkuDetails mSkuDetails;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upgrade);
upgradeButton = (Button) findViewById(R.id.upgradeButton);
upgradeButton.setEnabled(false);
upgradeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
upgradeAction();
}
});
// Check In App Purchase
skuList.add(sku);
setButtonStatus();
}
private void setButtonStatus(){
Boolean pro = getBoolFromPref(this,"myPref", sku);
if (pro)
{
Toast.makeText(this, "you are a premium user", Toast.LENGTH_SHORT).show();
//upgradeButton.setVisibility(View.INVISIBLE);
upgradeButton.setText(R.string.you_are_premium);
}
else
{
Toast.makeText(this, "not pro", Toast.LENGTH_SHORT).show();
setupBillingClient();
}
}
private void upgradeAction() {
if (mSkuDetails==null){
Toast.makeText(this,"Please wait while we get details",Toast.LENGTH_SHORT).show();
return;
}
BillingFlowParams billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(mSkuDetails)
.build();
billingClient.launchBillingFlow(UpgradeActivity.this, billingFlowParams);
}
// In App Handler:
private void setupBillingClient() {
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener(){
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is setup successfully
loadAllSKUs();
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
private void loadAllSKUs() {
Toast.makeText(this, "loadAllSKUs", Toast.LENGTH_SHORT).show();
if (billingClient.isReady())
{
Toast.makeText(this, "billingclient ready", Toast.LENGTH_SHORT).show();
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "inside query" + billingResult.getResponseCode(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& !skuDetailsList.isEmpty())
{
for (SkuDetails skuDetails : skuDetailsList) {
Toast.makeText(UpgradeActivity.this, "" + skuDetails.getSku(), Toast.LENGTH_SHORT).show();
if (skuDetails.getSku().equals(sku)) {
mSkuDetails = skuDetails;
upgradeButton.setEnabled(true);
}
}
}
}
});
}
else
Toast.makeText(this, "billingclient not ready", Toast.LENGTH_SHORT).show();
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
int responseCode = billingResult.getResponseCode();
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
else
if (responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
//Log.d(TAG, "User Canceled" + responseCode);
}
else if (responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
}
else {
//Log.d(TAG, "Other code" + responseCode);
// Handle any other error codes.
}
}
private void handlePurchase(Purchase purchase) {
if (purchase.getSku().equals(sku)) {
///mSharedPreferences.edit().putBoolean(getResources().getString(R.string.pref_remove_ads_key), true).commit();
///setAdFree(true);
setBoolInPref(this,"myPref",sku, true );
Toast.makeText(this, "Purchase done. you are now a premium member.", Toast.LENGTH_SHORT).show();
}
}
private Boolean getBoolFromPref(Context context, String prefName, String constantName) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
return pref.getBoolean(constantName, false);
}
private void setBoolInPref(Context context,String prefName, String constantName, Boolean val) {
SharedPreferences pref = context.getSharedPreferences(prefName, 0); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(constantName, val);
//editor.commit();
editor.apply();
// Update 2015: Android recommends the use of apply() now over commit(),
// because apply() operates on a background thread instead of storing the persistent data immediately,
// and possible blocking the main thread.
}
}
3个问题
- 您默认启用了升级按钮,并且在 upgradeAction 方法中没有空检查
- 您在 java 中通过 == 而不是 .equals 比较字符串
- 您正在设置升级 onclick 监听器 2 次
推荐阅读
- android - 替换android片段中的活动主题
- python-3.x - 添加多个隐藏层keras
- r - 对表格进行排序/排序/排名
- java - Does Google Cloud Speech API save my sound recordings?
- sql - Web 分析 SQL 表设计
- c# - 使用管理员更改 Windows 本地用户密码
- windows - How to obtain keyboard layout for Microsoft Edge and other windows hosted in ApplicationFrameHost.exe
- clojure - How to write a java varg function in clojure?
- python - 使用 boto3 将 csv 导出到 dynamodb
- python - Python 控制台:检测 Ctrl X 按键事件