When the user clicks the buy button, they get to the Android game screen to buy an item in the application. But when they push back, they get this exception due to the inability to start the billing service:
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)
He specifically mentions these lines in my class:
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)
on line 561 I have this code:
private void prependLogEntry(CharSequence cs) {
SpannableStringBuilder contents = new SpannableStringBuilder(cs);
contents.append('\n');
contents.append(mLogTextView.getText());
mLogTextView.setText(contents);
}
on line 569 I have this code:
private void logProductActivity(String product, String activity) {
SpannableStringBuilder contents = new SpannableStringBuilder();
contents.append(Html.fromHtml("<b>" + product + "</b>: "));
contents.append(activity);
prependLogEntry(contents);
}
By the way, what kind of magazine is this? Is it really necessary? Maybe it's worth noting these methods?
In any case, the other line that the error points to is 187, and here is the code for this:
@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);
}
}
So you see that this problem has a problem. What kind of magazine is this? Where can I see it? How can I fix this so that it doesn't make mistakes?
, , , , null. ?
ExtraHelpActivity:
public class ExtraHelpActivity extends BaseActivity implements ServiceConnection
{
private static final String TAG = "Pay";
String issueProductIdWebMarketing = "1";
String issueProductIdDonate = "2";
String issueProductIdPsych = "3";
private String payloadContents = null;
private static final String LOG_TEXT_KEY = "DUNGEONS_LOG_TEXT";
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>();
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;
private enum Managed { MANAGED, UNMANAGED, SUBSCRIPTION }
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);
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");
}
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;
}
}
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;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.extra_help);
handler = new Handler();
mExtraHelpPurchaseObserver = new ExtraHelpPurchaseObserver(handler);
mBillingService = new BillingService();
mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase(this);
ResponseHandler.register(mExtraHelpPurchaseObserver);
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)
{
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);
}
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null)
{
}
}
@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);
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);
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();
}
private String replaceLanguageAndRegion(String str) {
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;
}
private void setupWidgets()
{
mOwnedItemsCursor = mPurchaseDatabase.queryAllPurchasedItems();
startManagingCursor(mOwnedItemsCursor);
String[] from = new String[] { PurchaseDatabase.PURCHASED_PRODUCT_ID_COL,
PurchaseDatabase.PURCHASED_QUANTITY_COL
};
}
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);
}
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();
}
}
private void initializeOwnedItems() {
new Thread(new Runnable() {
public void run() {
doInitializeOwnedItems();
}
}).start();
}
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();
}
mHandler.post(new Runnable() {
public void run() {
mOwnedItems.addAll(ownedItems);
mCatalogAdapter.setOwnedItems(mOwnedItems);
}
});
}
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)) {
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
} else if (v == mEditPayloadButton) {
showPayloadEditDialog();
} else if (v == mEditSubscriptionsButton) {
editSubscriptions();
}
}
private void editSubscriptions() {
String packageName = getPackageName();
Intent i = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + packageName));
startActivity(i);
}
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(