2012-07-12 30 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>