我正在尝试在我的Android应用中实施应用内结算作为一种捐赠形式
每当我从主Java文件中调用startService(new Intent(mContext, BillingService.class));
时,app force就会在我运行它时关闭。所以要么我错过了某些地方,要么BillingService出了问题。任何帮助都将不胜感激,我已经坚持了几天。
LOG:
12-07 22:56:08.983: E/dalvikvm(23762): could not disable core file generation for pid 23762: Operation not permitted
12-07 22:56:09.764: I/BillingService(23762): Starting
12-07 22:56:09.819: D/AndroidRuntime(23762): Shutting down VM
12-07 22:56:09.819: W/dalvikvm(23762): threadid=1: thread exiting with uncaught exception (group=0x40018560)
12-07 22:56:09.819: E/AndroidRuntime(23762): FATAL EXCEPTION: main
12-07 22:56:09.819: E/AndroidRuntime(23762): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.Calculator.SalesTaxCalculator/com.Calculator.SalesTaxCalculator.Calculator}: java.lang.NullPointerException
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1696)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1716)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread.access$1500(ActivityThread.java:124)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:968)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.os.Handler.dispatchMessage(Handler.java:99)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.os.Looper.loop(Looper.java:123)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread.main(ActivityThread.java:3806)
12-07 22:56:09.819: E/AndroidRuntime(23762): at java.lang.reflect.Method.invokeNative(Native Method)
12-07 22:56:09.819: E/AndroidRuntime(23762): at java.lang.reflect.Method.invoke(Method.java:507)
12-07 22:56:09.819: E/AndroidRuntime(23762): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-07 22:56:09.819: E/AndroidRuntime(23762): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-07 22:56:09.819: E/AndroidRuntime(23762): at dalvik.system.NativeStart.main(Native Method)
12-07 22:56:09.819: E/AndroidRuntime(23762): Caused by: java.lang.NullPointerException
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.content.ComponentName.<init>(ComponentName.java:75)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.content.Intent.<init>(Intent.java:2720)
12-07 22:56:09.819: E/AndroidRuntime(23762): at com.Calculator.SalesTaxCalculator.Calculator.onCreate(Calculator.java:86)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
12-07 22:56:09.819: E/AndroidRuntime(23762): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1660)
12-07 22:56:09.819: E/AndroidRuntime(23762): ... 11 more
清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.Calculator.SalesTaxCalculator"
android:versionCode="3" android:versionName="2">
<uses-sdk android:minSdkVersion="4" />
<application android:icon="@drawable/calculator_icon" android:label="@string/app_name">
<activity android:name=".Calculator"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.Calculator.SalesTaxCalculator.BillingService" />
<receiver android:name="com.Calculator.SalesTaxCalculator.BillingReceiver">
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
</intent-filter>
</receiver>
</application>
<uses-permission android:name="com.android.vending.BILLING" />
<activity android:name=".BillingHelper" />
<activity android:name=".BillingSecurity" />
<activity android:name=".C" />
</manifest>
答案 0 :(得分:1)
我希望这会对你有所帮助。它适合我。
public class CheckoutActivity extends Activity implements OnClickListener,
OnItemSelectedListener {
private static final String TAG = "HCA";
private ProgressDialog dialog;
/**
* 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 static Bundle bundle = null;
private DungeonsPurchaseObserver mDungeonsPurchaseObserver;
private Handler mHandler;
private BillingService mBillingService;
private Button mBuyButton;
private Button mEditPayloadButton;
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;
/**
* Each product in the catalog is either MANAGED or UNMANAGED. 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.
*/
private enum Managed { MANAGED, UNMANAGED }
/**
* 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 DungeonsPurchaseObserver extends PurchaseObserver {
public DungeonsPurchaseObserver (Handler handler) {
super(CheckoutActivity.this, handler);
}
@Override
public void onBillingSupported(boolean supported) {
if (Consts.DEBUG) {
Log.i(TAG, "supported: " + supported);
}
if (supported) {
restoreDatabase();
mBuyButton.setEnabled(true);
mEditPayloadButton.setEnabled(true);
} else {
showDialog(DIALOG_BILLING_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);
new BackgroundTask ().execute();
}
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 ("sword_001", R.string.two_handed_sword, Managed.MANAGED),
new CatalogEntry ("potion_001", R.string.potions, Managed.UNMANAGED),
new CatalogEntry ("android.test.purchased", R.string.android_test_purchased,
Managed.UNMANAGED),
new CatalogEntry ("android.test.canceled", R.string.android_test_canceled,
Managed.UNMANAGED),
new CatalogEntry ("android.test.refunded", R.string.android_test_refunded,
Managed.UNMANAGED),
new CatalogEntry ("android.test.item_unavailable", R.string.android_test_item_unavailable,
Managed.UNMANAGED),
};
private String mItemName;
private String mSku;
private CatalogAdapter mCatalogAdapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* ImageView userImage = (ImageView)findViewById(R.id.header_userImg);
userImage.setImageResource(R.drawable.logo);*/
ImageView backBtn =(ImageView) findViewById (R.id.header_optionImg);
backBtn.setImageResource(R.drawable.back_btn_img);
backBtn.setOnClickListener(new OnClickListener () {
@Override
public void onClick(View v) {
Intent intent = new Intent(CheckoutActivity.this,
SelectRegistrationTypeActivity.class);
startActivity(intent);
CheckoutActivity.this.finish();
}
});
TextView headerTitle =(TextView) findViewById (R.id.header_screenTitle);
headerTitle.setText("Credit Card");
mHandler = new Handler ();
dialog = new ProgressDialog (CheckoutActivity.this);
mDungeonsPurchaseObserver = new DungeonsPurchaseObserver (mHandler);
mBillingService = new BillingService ();
mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase (this);
bundle = getIntent().getBundleExtra("RegistrationData");
setupWidgets();
// Check if billing is supported.
ResponseHandler.register(mDungeonsPurchaseObserver);
if (!mBillingService.checkBillingSupported()) {
showDialog(DIALOG_CANNOT_CONNECT_ID);
}
}
/**
* Called when this activity becomes visible.
*/
@Override
protected void onStart() {
super.onStart();
ResponseHandler.register(mDungeonsPurchaseObserver);
initializeOwnedItems();
}
/**
* Called when this activity is no longer visible.
*/
@Override
protected void onStop() {
super.onStop();
ResponseHandler.unregister(mDungeonsPurchaseObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
mPurchaseDatabase.close();
mBillingService.unbind();
}
/**
* 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(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);
default:
return null;
}
}
private Dialog createDialog(int titleId, int messageId) {
String helpUrl = replaceLanguageAndRegion (getString(R.string.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(R.string.learn_more, 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() {
mLogTextView = (TextView) findViewById (R.id.log);
mBuyButton = (Button) findViewById (R.id.buy_button);
mBuyButton.setEnabled(false);
mBuyButton.setOnClickListener(this);
mEditPayloadButton = (Button) findViewById (R.id.payload_edit_button);
mEditPayloadButton.setEnabled(false);
mEditPayloadButton.setOnClickListener(this);
mSelectItemSpinner = (Spinner) findViewById (R.id.item_choices);
mCatalogAdapter = new CatalogAdapter (this, CATALOG);
mSelectItemSpinner.setAdapter(mCatalogAdapter);
mSelectItemSpinner.setOnItemSelectedListener(this);
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, R.string.restoring_transactions, Toast.LENGTH_LONG).show();
}
}
/**
* 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);
}
String temSku = null;
switch(Integer.parseInt(bundle.getString("PlanTypeValue"))) {
case 0:
// payment.setSubtotal(new BigDecimal("2.99"));
temSku = "hca1";
break;
case 1:
// payment.setSubtotal(new BigDecimal("25.00"));
temSku = "hca2";
break;
case 2:
// payment.setSubtotal(new BigDecimal("100.00"));
temSku = "hca3";
break;
case 3:
// payment.setSubtotal(new BigDecimal("500.00"));
temSku = "hca4";
break;
case 4:
// payment.setSubtotal(new BigDecimal("1000.00"));
temSku = "hca5";
break;
default:
// payment.setSubtotal(new BigDecimal("2.99"));
temSku = "hca1";
break;
}
if (!mBillingService.requestPurchase(temSku, mPayloadContents)) {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
}
} else if (v == mEditPayloadButton) {
showPayloadEditDialog();
}
}
/**
* 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 () {
public void onCancel(DialogInterface dialog) {
if (dialog != null) {
dialog.cancel();
}
}
});
dialog.show();
}
/**
* Called when an item in the spinner is selected.
*/
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mItemName = getString(CATALOG[position].nameId);
mSku = CATALOG[position].sku;
mSku = "sword_001";
}
public void onNothingSelected(AdapterView<?> arg0) {
mSku = "sword_001";
}
/**
* An adapter used for displaying a catalog of products. If a product is
* managed by Android Market and already purchased, then it will be "grayed-out" in
* the list and not selectable.
*/
private static class CatalogAdapter extends ArrayAdapter<String> {
private CatalogEntry [] mCatalog;
private Set < String > mOwnedItems = new HashSet<String>();
public CatalogAdapter (Context context, CatalogEntry[] catalog) {
super(context, android.R.layout.simple_spinner_item);
mCatalog = catalog;
for (CatalogEntry element : catalog) {
add(context.getString(element.nameId));
}
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
public void setOwnedItems(Set<String> ownedItems) {
mOwnedItems = ownedItems;
notifyDataSetChanged();
}
@Override
public boolean areAllItemsEnabled() {
// Return false to have the adapter call isEnabled()
return false;
}
@Override
public boolean isEnabled(int position) {
// If the item at the given list position is not purchasable,
// then prevent the list item from being selected.
CatalogEntry entry = mCatalog [position];
if (entry.managed == Managed.MANAGED && mOwnedItems.contains(entry.sku)) {
return false;
}
return true;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// If the item at the given list position is not purchasable, then
// "gray out" the list item.
View view = super.getDropDownView(position, convertView, parent);
view.setEnabled(isEnabled(position));
return view;
}
}
private class BackgroundTask extends AsyncTask<Void, Void, String> {
protected void onPreExecute() {
dialog.setMessage("Please wait...");
dialog.show();
}
@Override
protected String doInBackground(Void... arg0) {
String response = null;
try {
String.valueOf(bundle.getString("UserTypeValue")),
String.valueOf(bundle.getString("PlanTypeValue")),
String.valueOf(bundle.getString("RegistrationFees")));
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
protected void onPostExecute(String response) {
dialog.dismiss();
AlertDialog alert = new AlertDialog.Builder(
CheckoutActivity.this).create();
alert.setTitle("Thank you");
alert.setMessage("Your account has been created");
alert.setButton("Ok",
new DialogInterface . OnClickListener () {
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();
Intent intent = new Intent(CheckoutActivity.this,
HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
CheckoutActivity.this.finish();
}
});
alert.show();
TextView messageView =(TextView) alert . findViewById (android.R.id.message);
messageView.setGravity(Gravity.CENTER_HORIZONTAL);
messageView.setTextSize(14);
}
}
}
答案 1 :(得分:0)
从LogCat看一下这一行:
at com.Calculator.SalesTaxCalculator.Calculator.onCreate(Calculator.java:86
您的NullPointerException
方法中有onCreate
,因此请检查第86
行。