gpt4 book ai didi

Android 应用内结算 - 按购买时出现异常,然后从购买屏幕按回

转载 作者:塔克拉玛干 更新时间:2023-11-02 22:27:53 25 4
gpt4 key购买 nike

当用户按下购买按钮时,他们将被带到 Android 播放屏幕以购买应用内商品。但是当他们反击时,他们得到了关于无法启动计费服务的异常:

java.lang.RuntimeException: Unable to start service com.problemio.BillingService@405704f0 with Intent { act=com.android.vending.billing.RESPONSE_CODE cmp=com.problemio/.BillingService (has extras) }: java.lang.NullPointerException
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2387)
at android.app.ActivityThread.access$2800(ActivityThread.java:132)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1111)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:4293)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.problemio.ExtraHelpActivity.prependLogEntry(ExtraHelpActivity.java:561)
at com.problemio.ExtraHelpActivity.logProductActivity(ExtraHelpActivity.java:569)
at com.problemio.ExtraHelpActivity.access$4(ExtraHelpActivity.java:565)
at com.problemio.ExtraHelpActivity$ExtraHelpPurchaseObserver.onRequestPurchaseResponse(ExtraHelpActivity.java:187)
at com.problemio.ResponseHandler.responseCodeReceived(ResponseHandler.java:137)
at com.problemio.BillingService$RequestPurchase.responseCodeReceived(BillingService.java:285)
at com.problemio.BillingService.checkResponseCode(BillingService.java:582)
at com.problemio.BillingService.handleCommand(BillingService.java:427)
at com.problemio.BillingService.onStart(BillingService.java:398)
at android.app.Service.onStartCommand(Service.java:428)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2370)
... 10 more
java.lang.NullPointerException
at com.problemio.ExtraHelpActivity.prependLogEntry(ExtraHelpActivity.java:561)
at com.problemio.ExtraHelpActivity.logProductActivity(ExtraHelpActivity.java:569)
at com.problemio.ExtraHelpActivity.access$4(ExtraHelpActivity.java:565)
at com.problemio.ExtraHelpActivity$ExtraHelpPurchaseObserver.onRequestPurchaseResponse(ExtraHelpActivity.java:187)
at com.problemio.ResponseHandler.responseCodeReceived(ResponseHandler.java:137)
at com.problemio.BillingService$RequestPurchase.responseCodeReceived(BillingService.java:285)
at com.problemio.BillingService.checkResponseCode(BillingService.java:582)
at com.problemio.BillingService.handleCommand(BillingService.java:427)
at com.problemio.BillingService.onStart(BillingService.java:398)
at android.app.Service.onStartCommand(Service.java:428)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2370)
at android.app.ActivityThread.access$2800(ActivityThread.java:132)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1111)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:4293)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)

它在我的课上特别提到了这些行:

原因:

java.lang.NullPointerException
at com.problemio.ExtraHelpActivity.prependLogEntry(ExtraHelpActivity.java:561)
at com.problemio.ExtraHelpActivity.logProductActivity(ExtraHelpActivity.java:569)
at com.problemio.ExtraHelpActivity.access$4(ExtraHelpActivity.java:565)
at com.problemio.ExtraHelpActivity$ExtraHelpPurchaseObserver.onRequestPurchaseResponse(ExtraHelpActivity.java:187)

在第 561 行我有这段代码:

private void prependLogEntry(CharSequence cs) {
SpannableStringBuilder contents = new SpannableStringBuilder(cs);
contents.append('\n');
contents.append(mLogTextView.getText()); // Line 561
mLogTextView.setText(contents);
}

在第 569 行我有这段代码:

private void logProductActivity(String product, String activity) {
SpannableStringBuilder contents = new SpannableStringBuilder();
contents.append(Html.fromHtml("<b>" + product + "</b>: "));
contents.append(activity);
prependLogEntry(contents); // Line 569
}

对了,这是哪个日志?真的需要吗?也许值得评论这些方法?

无论如何,错误指向的另一行是 187,这是它的代码:

@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
if (Consts.DEBUG) {
Log.d(TAG, request.mProductId + ": " + responseCode);
}
if (responseCode == ResponseCode.RESULT_OK) {
if (Consts.DEBUG) {
Log.i(TAG, "purchase was successfully sent to server");
}
logProductActivity(request.mProductId, "sending purchase request");
} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
if (Consts.DEBUG) {
Log.i(TAG, "user canceled purchase");
}
logProductActivity(request.mProductId, "dismissed purchase dialog");
// This is line 187 right above this comment.
} else {
if (Consts.DEBUG) {
Log.i(TAG, "purchase failed");
}
logProductActivity(request.mProductId, "request purchase returned " + responseCode);
}
}

所以你看,它在这个日志记录方面一直有问题。这是什么日志?我在哪里可以看到它?我该如何修复它才不会出错?

我不确定,但也许更大的问题是请求为空。为什么这里的请求可能为空?

这是整个 ExtraHelpActivity 类:

public class ExtraHelpActivity extends BaseActivity implements ServiceConnection 
{
// , OnClickListener
private static final String TAG = "Pay";

String issueProductIdWebMarketing = "1";
String issueProductIdDonate = "2";
String issueProductIdPsych = "3";

/**
* The developer payload that is sent with subsequent
* purchase requests.
*/
private String payloadContents = null;

/**
* Used for storing the log text.
*/
private static final String LOG_TEXT_KEY = "DUNGEONS_LOG_TEXT";

/**
* The SharedPreferences key for recording whether we initialized the
* database. If false, then we perform a RestoreTransactions request
* to get all the purchases for this user.
*/
private static final String DB_INITIALIZED = "db_initialized";

private ExtraHelpPurchaseObserver mExtraHelpPurchaseObserver;
private Handler mHandler;
private Handler handler;


private BillingService mBillingService;
private Button mBuyButton;
private Button mEditPayloadButton;
private Button mEditSubscriptionsButton;
private TextView mLogTextView;
private Spinner mSelectItemSpinner;
private ListView mOwnedItemsTable;
private SimpleCursorAdapter mOwnedItemsAdapter;
private PurchaseDatabase mPurchaseDatabase;
private Cursor mOwnedItemsCursor;
private Set<String> mOwnedItems = new HashSet<String>();

/**
* The developer payload that is sent with subsequent
* purchase requests.
*/
private String mPayloadContents = null;

private static final int DIALOG_CANNOT_CONNECT_ID = 1;
private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2;
private static final int DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID = 3;

/**
* Each product in the catalog can be MANAGED, UNMANAGED, or SUBSCRIPTION. MANAGED
* means that the product can be purchased only once per user (such as a new
* level in a game). The purchase is remembered by Android Market and
* can be restored if this application is uninstalled and then
* re-installed. UNMANAGED is used for products that can be used up and
* purchased multiple times (such as poker chips). It is up to the
* application to keep track of UNMANAGED products for the user.
* SUBSCRIPTION is just like MANAGED except that the user gets charged monthly
* or yearly.
*/
private enum Managed { MANAGED, UNMANAGED, SUBSCRIPTION }

/**
* A {@link PurchaseObserver} is used to get callbacks when Android Market sends
* messages to this application so that we can update the UI.
*/
private class ExtraHelpPurchaseObserver extends PurchaseObserver {
public ExtraHelpPurchaseObserver(Handler handler) {
super(ExtraHelpActivity.this, handler);
}

@Override
public void onBillingSupported(boolean supported, String type) {
if (Consts.DEBUG) {
Log.i(TAG, "supported: " + supported);
}
if (type == null || type.equals(Consts.ITEM_TYPE_INAPP)) {
if (supported) {
restoreDatabase();
mBuyButton.setEnabled(true);
mEditPayloadButton.setEnabled(true);
} else {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
}
} else if (type.equals(Consts.ITEM_TYPE_SUBSCRIPTION)) {
mCatalogAdapter.setSubscriptionsSupported(supported);
} else {
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
}

@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
if (Consts.DEBUG) {
Log.i(TAG, "onPurchaseStateChange() itemId: " + itemId + " " + purchaseState);
}

if (developerPayload == null) {
logProductActivity(itemId, purchaseState.toString());
} else {
logProductActivity(itemId, purchaseState + "\n\t" + developerPayload);
}

if (purchaseState == PurchaseState.PURCHASED) {
mOwnedItems.add(itemId);

// If this is a subscription, then enable the "Edit
// Subscriptions" button.
for (CatalogEntry e : CATALOG) {
if (e.sku.equals(itemId) &&
e.managed.equals(Managed.SUBSCRIPTION)) {
mEditSubscriptionsButton.setVisibility(View.VISIBLE);
}
}
}
mCatalogAdapter.setOwnedItems(mOwnedItems);
mOwnedItemsCursor.requery();
}

@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
if (Consts.DEBUG) {
Log.d(TAG, request.mProductId + ": " + responseCode);
}
if (responseCode == ResponseCode.RESULT_OK) {
if (Consts.DEBUG) {
Log.i(TAG, "purchase was successfully sent to server");
}
logProductActivity(request.mProductId, "sending purchase request");
} else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
if (Consts.DEBUG) {
Log.i(TAG, "user canceled purchase");
}
logProductActivity(request.mProductId, "dismissed purchase dialog");
} else {
if (Consts.DEBUG) {
Log.i(TAG, "purchase failed");
}
logProductActivity(request.mProductId, "request purchase returned " + responseCode);
}
}

@Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode) {
if (responseCode == ResponseCode.RESULT_OK) {
if (Consts.DEBUG) {
Log.d(TAG, "completed RestoreTransactions request");
}
// Update the shared preferences so that we don't perform
// a RestoreTransactions again.
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED, true);
edit.commit();
} else {
if (Consts.DEBUG) {
Log.d(TAG, "RestoreTransactions error: " + responseCode);
}
}
}
}

private static class CatalogEntry {
public String sku;
public int nameId;
public Managed managed;

public CatalogEntry(String sku, int nameId, Managed managed) {
this.sku = sku;
this.nameId = nameId;
this.managed = managed;
}
}

/** An array of product list entries for the products that can be purchased. */
private static final CatalogEntry[] CATALOG = new CatalogEntry[] {
new CatalogEntry("marketing_001", 1 , Managed.MANAGED),
new CatalogEntry("potion_001", 2 , Managed.UNMANAGED),
new CatalogEntry("subscription_monthly", 3,
Managed.SUBSCRIPTION),
new CatalogEntry("subscription_yearly", 4 ,
Managed.SUBSCRIPTION)
};


private String mItemName;
private String mSku;
private Managed mManagedType;
private CatalogAdapter mCatalogAdapter;


//outside onCreate() Within class
// public Handler mTransactionHandler = new Handler(){
// public void handleMessage(android.os.Message msg) {
// Log.i(TAG, "Transaction complete");
// Log.i(TAG, "Transaction status: "+BillingHelper.latestPurchase.purchaseState);
// Log.i(TAG, "Item purchased is: "+BillingHelper.latestPurchase.productId);
//
// if(BillingHelper.latestPurchase.isPurchased()){
// //code here which is to be performed after successful purchase
// }
// };
//
// };


// TODO:
// TODO:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

setContentView(R.layout.extra_help);

//Now setup the in-app billing</pre>
handler = new Handler();

mExtraHelpPurchaseObserver = new ExtraHelpPurchaseObserver(handler);

mBillingService = new BillingService();

mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase(this);

ResponseHandler.register(mExtraHelpPurchaseObserver);

//Check if billing is supported. (Optional)
//boolean check = mBillingService.checkBillingSupported();




// startService(new Intent(mContext, BillingService.class));
// BillingHelper.setCompletedHandler(mTransactionHandler);

// Make sure the user is logged in
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( ExtraHelpActivity.this);
final String user_id = prefs.getString( "user_id" , null );

Button donate = (Button)findViewById(R.id.donate);
donate.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{

// Send me an email that a comment was submitted on a question.

// boolean val = mBillingService.requestPurchase(
// "android.test.purchased", payloadContents);
//Replace "android.test.purchased" with your product ID that you added to Google Play or make it a variable that is populated from a list.
//Place this code in a button event or where ever it fits in your process flow.

if (mBillingService.requestPurchase(issueProductIdDonate, Consts.ITEM_TYPE_INAPP , null))
{

}
else
{

Log.i("tag", "Can't purchase on this device");

}
}
});


try
{
boolean bindResult = bindService(
new Intent("com.android.vending.billing.MarketBillingService.BIND"),
this,
Context.BIND_AUTO_CREATE);
if (bindResult)
{
Log.i( "Err" , "Service bind successful.");
}
else
{
Log.e( "Err", "Could not bind to the MarketBillingService.");
}
}
catch (SecurityException e)
{
Log.e( "Err" , "Security exception: " + e);
}

}




/**
* Save the context of the log so simple things like rotation will not
* result in the log being cleared.
*/
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
//outState.putString(LOG_TEXT_KEY, Html.toHtml((Spanned) mLogTextView.getText()));
}

/**
* Restore the contents of the log if it has previously been saved.
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null)
{
//mLogTextView.setText(Html.fromHtml(savedInstanceState.getString(LOG_TEXT_KEY)));
}
}

@Override
protected Dialog onCreateDialog(int id)
{
switch (id)
{
case DIALOG_CANNOT_CONNECT_ID:
return createDialog(1,1);
case DIALOG_BILLING_NOT_SUPPORTED_ID:
return createDialog(2,2);
case DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID:
return createDialog(3,3);

// case DIALOG_CANNOT_CONNECT_ID:
// return createDialog(R.string.cannot_connect_title,
// R.string.cannot_connect_message);
// case DIALOG_BILLING_NOT_SUPPORTED_ID:
// return createDialog(R.string.billing_not_supported_title,
// R.string.billing_not_supported_message);
// case DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID:
// return createDialog(R.string.subscriptions_not_supported_title,
// R.string.subscriptions_not_supported_message);
default:
return null;
}
}

private Dialog createDialog(int titleId, int messageId) {
String helpUrl = replaceLanguageAndRegion("help_url");
if (Consts.DEBUG) {
Log.i(TAG, helpUrl);
}
final Uri helpUri = Uri.parse(helpUrl);

// TODO: replace 1 with the thing its supposed to be - I think learn more url :)
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(titleId)
.setIcon(android.R.drawable.stat_sys_warning)
.setMessage(messageId)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_VIEW, helpUri);
startActivity(intent);
}
});
return builder.create();
}

/**
* Replaces the language and/or country of the device into the given string.
* The pattern "%lang%" will be replaced by the device's language code and
* the pattern "%region%" will be replaced with the device's country code.
*
* @param str the string to replace the language/country within
* @return a string containing the local language and region codes
*/
private String replaceLanguageAndRegion(String str) {
// Substitute language and or region if present in string
if (str.contains("%lang%") || str.contains("%region%")) {
Locale locale = Locale.getDefault();
str = str.replace("%lang%", locale.getLanguage().toLowerCase());
str = str.replace("%region%", locale.getCountry().toLowerCase());
}
return str;
}

/**
* Sets up the UI.
*/
private void setupWidgets()
{
mOwnedItemsCursor = mPurchaseDatabase.queryAllPurchasedItems();
startManagingCursor(mOwnedItemsCursor);
String[] from = new String[] { PurchaseDatabase.PURCHASED_PRODUCT_ID_COL,
PurchaseDatabase.PURCHASED_QUANTITY_COL
};
// int[] to = new int[] { R.id.item_name, R.id.item_quantity };
// mOwnedItemsAdapter = new SimpleCursorAdapter(this, R.layout.item_row,
// mOwnedItemsCursor, from, to);
// mOwnedItemsTable = (ListView) findViewById(R.id.owned_items);
// mOwnedItemsTable.setAdapter(mOwnedItemsAdapter);
}

private void prependLogEntry(CharSequence cs) {
SpannableStringBuilder contents = new SpannableStringBuilder(cs);
contents.append('\n');
contents.append(mLogTextView.getText());
mLogTextView.setText(contents);
}

private void logProductActivity(String product, String activity) {
SpannableStringBuilder contents = new SpannableStringBuilder();
contents.append(Html.fromHtml("<b>" + product + "</b>: "));
contents.append(activity);
prependLogEntry(contents);
}

/**
* If the database has not been initialized, we send a
* RESTORE_TRANSACTIONS request to Android Market to get the list of purchased items
* for this user. This happens if the application has just been installed
* or the user wiped data. We do not want to do this on every startup, rather, we want to do
* only when the database needs to be initialized.
*/
private void restoreDatabase() {
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
boolean initialized = prefs.getBoolean(DB_INITIALIZED, false);
if (!initialized) {
mBillingService.restoreTransactions();
Toast.makeText(this, 3, Toast.LENGTH_LONG).show();
// Used to be R.string.restoring_transactions instead of 3
}
}

/**
* Creates a background thread that reads the database and initializes the
* set of owned items.
*/
private void initializeOwnedItems() {
new Thread(new Runnable() {
public void run() {
doInitializeOwnedItems();
}
}).start();
}

/**
* Reads the set of purchased items from the database in a background thread
* and then adds those items to the set of owned items in the main UI
* thread.
*/
private void doInitializeOwnedItems() {
Cursor cursor = mPurchaseDatabase.queryAllPurchasedItems();
if (cursor == null) {
return;
}

final Set<String> ownedItems = new HashSet<String>();
try {
int productIdCol = cursor.getColumnIndexOrThrow(
PurchaseDatabase.PURCHASED_PRODUCT_ID_COL);
while (cursor.moveToNext()) {
String productId = cursor.getString(productIdCol);
ownedItems.add(productId);
}
} finally {
cursor.close();
}

// We will add the set of owned items in a new Runnable that runs on
// the UI thread so that we don't need to synchronize access to
// mOwnedItems.
mHandler.post(new Runnable() {
public void run() {
mOwnedItems.addAll(ownedItems);
mCatalogAdapter.setOwnedItems(mOwnedItems);
}
});
}

/**
* Called when a button is pressed.
*/
public void onClick(View v) {
if (v == mBuyButton) {
if (Consts.DEBUG) {
Log.d(TAG, "buying: " + mItemName + " sku: " + mSku);
}

if (mManagedType != Managed.SUBSCRIPTION &&
!mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
} else if (!mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_SUBSCRIPTION, mPayloadContents)) {
// Note: mManagedType == Managed.SUBSCRIPTION
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
} else if (v == mEditPayloadButton) {
showPayloadEditDialog();
} else if (v == mEditSubscriptionsButton) {
editSubscriptions();
}
}

/** List subscriptions for this package in Google Play
*
* This allows users to unsubscribe from this apps subscriptions.
*
* Subscriptions are listed on the Google Play app detail page, so this
* should only be called if subscriptions are known to be present.
*/
private void editSubscriptions() {
// Get current package name
String packageName = getPackageName();
// Open app detail in Google Play
Intent i = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + packageName));
startActivity(i);
}

/**
* Displays the dialog used to edit the payload dialog.
*/
private void showPayloadEditDialog()
{
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
final View view = View.inflate(this, R.layout.edit_payload, null);
final TextView payloadText = (TextView) view.findViewById(R.id.payload_text);
if (mPayloadContents != null) {
payloadText.setText(mPayloadContents);
}

dialog.setView(view);
dialog.setPositiveButton(
R.string.edit_payload_accept,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mPayloadContents = payloadText.getText().toString();
}
});
dialog.setNegativeButton(
R.string.edit_payload_clear,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (dialog != null) {
mPayloadContents = null;
dialog.cancel();
}
}
});
dialog.setOnCancelListener(new DialogInterface.OnCancelListener(

最佳答案

糟糕。

mLogTextView 从未被初始化。

添加 mLogTextView = findViewById(R.id.blaaa);

相信你已经知道了

如果我错了请告诉我,以便我删除这个答案(:

关于Android 应用内结算 - 按购买时出现异常,然后从购买屏幕按回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11456246/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com