2012-07-12 86 views
5

当用户按下购买按钮时,它们会被带到Android播放屏幕以购买应用内商品。但是,当他们按回去,他们得到这个异常有关无法启动计费服务:Android应用内结算 - 按下购买时的例外情况,然后从购买屏幕中按下

java.lang.RuntimeException: Unable to start service [email protected] 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(
+0

我注意到的一个可能的问题是,在我的BillingService中,我必须为方法注释掉@override指令:onServiceDisconnected和onServiceConnected - 否则它不会编译。任何想法为什么它不会编译,如果这可能是我在这里致命的问题? – Genadinik 2012-07-12 17:12:03

+0

如果用户推回按钮,则会调用onRequestPurchaseResponse(RequestPurchase请求,ResponseCode responseCode)回调。 logProductActivity中的问题(request.mProductId,“驳回的购买对话框”);方法的情况下(responseCode == ResponseCode.RESULT_USER_CANCELED)。尝试评论logProductActivity(请求。mProductId,“解除购买对话框”);那里。 – 2012-07-19 10:58:36

+0

如果你不想要日志注释掉所有的logBlahBlah()方法,然后尝试..其中一个崩溃是由日志记录部分引起的。第二个是第一次崩溃的副作用..它无法启动计费服务.. – Ronnie 2012-07-19 19:09:56

回答

3

哎呀。

mLogTextView从不初始化。

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

我相信你已经知道,

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

+0

感谢您的回答。我有点困惑,我应该添加这个,以及.... findViewById(R.id.blaaa)是什么?我的意思是......我的看法是什么?我没有为logTextView制作特别的视图,我没有注意到在Android教程中的Dungeons示例中。 – Genadinik 2012-07-17 06:09:58

+0

在'onCreate'函数中'setContentView'之后,您应该初始化'mLogTextView'。让我看看'extra_help.xml'。哦,顺便说一句,我认为这个'TextView'用于显示Dungeons示例中的事务,所以现在可能不需要真正的应用程序,您可以更改此记录机制。无论如何,让我看看'extra_help.xml' – 2012-07-17 06:14:46

+0

我用我的extra_help.xml添加了一个答案,因为我在原始问题中遇到了字符限制。 – Genadinik 2012-07-17 06:16:34

3

是你给menifest文件权限?

<uses-permission android:name="com.android.vending.BILLING" />

+0

是肯定使用的。即使我可以告诉你,如果用户完成购买并发生了类似的异常,他们将得到结算,但在登陆我的应用程序后,他们会得到一个强制关闭的异常。 – Genadinik 2012-07-12 16:38:29

+0

您将创建辅助类。你可以发布吗? – 2012-07-12 16:47:40

+0

我会发布全班...现在发布... – Genadinik 2012-07-12 16:48:58

1

我加入了一个答案,因为我到了角色限制我原来的问题,因为我的类是这么久。

这是extra_help.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:orientation="vertical" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     >   

<include android:id="@+id/header" 
     layout="@layout/header" 
     android:layout_height="wrap_content" 
     android:layout_width="fill_parent"/> 

<ScrollView 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:padding="5px">  

<LinearLayout 

     android:orientation="vertical" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:padding="5px" 
     > 

<TextView 
    android:id="@+id/heading_1"  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textColor="@color/light_best_blue" 
    android:text="THE APP IS FREE TO HELP AS MANY PEOPLE AS POSSIBLE, PLEASE GIVE BACK" 
    /> 

    <Button 
    android:id="@+id/donate" 
    android:layout_marginTop ="10dp"   
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Donate $1.99 Since the App is Free" 
    /> 

</LinearLayout> 

</ScrollView> 

</LinearLayout>