Android无法绑定到服务(在App-Billing中)

And*_*oid 2 android in-app-purchase

I'm trying to implement in app-billing in my application, but I have a little problem with it. I'm using the example from android developer's site and everytime I start the activity which will connect to the billing service it's showing me a dialog that I cannot connect to server and when I press learn more it's going to a web page which is explaining me to update my android market app, but it's already the latest one. And the other thing, before implementing the code on my application I create a test application where I can connect with the same code and I don't get problems. But in my application I can't do that. Is there any connection with the developer api key, which you can use only in one application or something like that. Because I'm using the same for test and real app.

这是我的代码,如果你能看到一些不太应该的东西:

public class StampiiStore extends Activity {
    String servername;
    int userId;
    int storageID;
    RPCCommunicator rpc;
    String path;
    Button envelope1, envelope2, envelope3;
    private static final String TAG = "STAMPII";
    /**
     * 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 mStampiiPurchaseObserver mStampiiPurchaseObserver;
    private Handler mHandler;
    private BillingService mBillingService;
    private TextView mLogTextView;
    private Cursor mOwnedItemsCursor;
    private PurchaseDatabase mPurchaseDatabase;
    private Set<String> mOwnedItems = new HashSet<String>();
    /**
     * The developer payload that is sent with subsequent
     * purchase requests.
     */
    private static final int DIALOG_CANNOT_CONNECT_ID = 1;
    private static final int DIALOG_BILLING_NOT_SUPPORTED_ID = 2;

    @SuppressWarnings("static-access")
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.store);


        SystemDatabaseHelper sysDbHelper = new SystemDatabaseHelper(this, null, 1);
        sysDbHelper.initialize(this);

        ImageView icon = (ImageView) findViewById (R.id.store_img);

        final int collId = getIntent().getIntExtra("collection_id",0);
        Log.e("collId","collId : "+collId);

        // Getting all variables from SharedPreferences to build the right path to images
        servername = rpc.getCurrentServerName(this);
        Log.d("","Current Server Name : "+servername);
        userId = rpc.getUserId(this);
        Log.d("","User Id : "+userId);
        storageID = rpc.getCurrentStoragePath(this);
        Log.d("","storage ID : "+storageID);
        //

        TextView colltitle = (TextView) findViewById(R.id.collection_title);
        TextView sTitle = (TextView) findViewById(R.id.store_collId);
        TextView collInfo = (TextView) findViewById(R.id.store_coll_info);

        envelope1 = (Button) findViewById(R.id.buyone);
        envelope1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                mBillingService.requestPurchase("android.test.purchased", "");
            }
        });

        envelope2 = (Button) findViewById(R.id.buytwo);
        envelope2.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                mBillingService.requestPurchase("android.test.canceled", "");
            }
        });

        envelope3 = (Button) findViewById(R.id.buythree);
        envelope3.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                mBillingService.requestPurchase("com.stampii.stampii.envelopesthree", "");
            }
        });

        mHandler = new Handler();
        mStampiiPurchaseObserver = new mStampiiPurchaseObserver(mHandler);
        mBillingService = new BillingService();
        mBillingService.setContext(this);

        mPurchaseDatabase = new PurchaseDatabase(this);
        setupWidgets();

        // Check if billing is supported.
        ResponseHandler.register(mStampiiPurchaseObserver);
        if (!mBillingService.checkBillingSupported()) {
            showDialog(DIALOG_CANNOT_CONNECT_ID);
        }



        Button back = (Button) findViewById(R.id.store_back_button);
        back.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                finish();
            }
        });
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        boolean isLoggedIn = settings.getBoolean("isLoggedIn", false);

        ImageButton sync = (ImageButton) findViewById(R.id.sync_store);
        if(isLoggedIn){
            sync.setVisibility(View.VISIBLE);
        } else if(!isLoggedIn){
            sync.setVisibility(View.GONE);
        }
        sync.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(StampiiStore.this, Synchronization.class);
                intent.putExtra("process", 2);
                startActivity(intent);              
            }
        });

        String titleSql = "SELECT title FROM collection_lang WHERE collection_id= " + collId + " AND lang_code='en_US'";
        Cursor title = sysDbHelper.executeSQLQuery(titleSql);
        if(title.getCount()==0){
            title.close();
        } else if(title.getCount()>0){
            for(title.move(0); title.moveToNext(); title.isAfterLast()){
                String collectionTitle = title.getString(title.getColumnIndex("title"));
                sTitle.setText(collectionTitle);
                if (collectionTitle.length() > 20){
                       String newTitle = collectionTitle.substring(0, 20);
                       colltitle.setText(newTitle + "...");
                } else {
                    colltitle.setText(collectionTitle);
                }
            }
        }
        title.close();

        String infoSql =  "SELECT DISTINCT  c.total_cards AS cardsCount, " +
                            " c.cards_per_envelope AS cardsPerEnvelope " +
                            "FROM collections AS c, collection_lang AS cl " +
                            "WHERE c.collection_id = cl.collection_id AND c.collection_id=" + collId;
        Cursor info = sysDbHelper.executeSQLQuery(infoSql);
        if(info.getCount()==0){
            info.close();
        } else if (info.getCount()>0){
            info.moveToFirst();
                int cardsCount = info.getInt(info.getColumnIndex("cardsCount"));
                int cardsPerEnvelope = info.getInt(info.getColumnIndex("cardsPerEnvelope"));
                collInfo.setText(cardsCount+" Stampii\n"+cardsPerEnvelope+" cards per envelope");
        }

        String sqlite2 = "SELECT media_id FROM collection_media WHERE collection_id="+collId+" AND media_type_id="+3018;
        Cursor bg = sysDbHelper.executeSQLQuery(sqlite2);
        if (bg.getCount() == 0) {
            bg.close();
        } else if (bg.getCount() > 0) {
            for (bg.move(0); bg.moveToNext(); bg.isAfterLast()) {
                int objectId = Integer.parseInt(bg.getString(bg.getColumnIndex("media_id")));
                String filename = "mediacollection-"+objectId+".png";
                //if(storageID==1){
                    path = rpc.getPublicPathsInternal(servername, 3018, filename, StampiiStore.this);
            /*} else if(storageID==2){
                path = rpc.getPublicPathsExternal(servername, 3007, objectId);
            }*/
            }
        }
        bg.close();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inTempStorage = new byte[3*1024];

        Bitmap ops = BitmapFactory.decodeFile(path, options);
        BitmapDrawable bitmapDrawable = new BitmapDrawable(ops);
        icon.setBackgroundDrawable(bitmapDrawable);

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

                SharedPreferences isSelectedCode = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
                String isSelected = isSelectedCode.getString("isSelected", "");
                Log.i("isSelected", "isSelected" + isSelected);
                EditText input = new EditText(StampiiStore.this);
                input.setText(isSelected);

                final int loggedOut = getIntent().getIntExtra("statement", 0);
                if(loggedOut==0){
                    new AlertDialog.Builder(getParent())
                    .setTitle("Promotional Code")
                    .setView(input)
                    .setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d("AlertDialog", "Positive");
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d("AlertDialog","Negative");
                            dialog.cancel();
                        }
                    }).show();
                } else if (loggedOut==1){
                    new AlertDialog.Builder(Collections.parentActivity)
                    .setTitle("Promotional Code")
                    .setView(input)
                    .setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d("AlertDialog", "Positive");
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d("AlertDialog","Negative");
                            dialog.cancel();
                        }
                    }).show();
                }
            }
        });

        Button savedCodes = (Button) findViewById(R.id.saved_codes_btn);
        savedCodes.setOnClickListener(new OnClickListener(){
            public void onClick(View v){
                Intent previewMessage = new Intent(getParent(), SavedCodes.class);
                TabGroupActivity parentActivity = (TabGroupActivity)getParent();
                parentActivity.startChildActivity("Saved Codes", previewMessage);
            }
        });
    }


    /**
     * Sets up the UI.
     */
    private void setupWidgets() {
        //TODO: If need any changes in the UI!
    }


    /**
     * 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, "Restoring Transactions", Toast.LENGTH_LONG).show();
        }
    }

    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);
    }


    //PurchaseObserver
    private class mStampiiPurchaseObserver extends PurchaseObserver {
        public mStampiiPurchaseObserver(Handler handler) {
            super(StampiiStore.this, handler);
        }

        @Override
        public void onBillingSupported(boolean supported) {
            if (Consts.DEBUG) {
                Log.i(TAG, "supported: " + supported);
            }
            if (supported) {
                restoreDatabase();
                envelope1.setEnabled(true);
                envelope2.setEnabled(true);
                envelope3.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);
            }
            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);
                }
            }
        }
    }


    /**
     * Called when this activity becomes visible.
     */
    @Override
    protected void onStart() {
        super.onStart();
        ResponseHandler.register(mStampiiPurchaseObserver);
    }

    /**
     * Called when this activity is no longer visible.
     */
    @Override
    protected void onStop() {
        super.onStop();
        ResponseHandler.unregister(mStampiiPurchaseObserver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPurchaseDatabase.close();
        mBillingService.unbind();
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case DIALOG_CANNOT_CONNECT_ID:
            return createDialog("Server cannot Connect",
                    "Server cannot connect");
        case DIALOG_BILLING_NOT_SUPPORTED_ID:
            return createDialog("Billing not supported",
                    "Billing not supported");
        default:
            return null;
        }
    }

    /**
     * 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;
    }

    private Dialog createDialog(String titleId, String 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 = null;
        final int loggedOut = getIntent().getIntExtra("statement", 0);
        if(loggedOut==0){
            builder = new AlertDialog.Builder(getParent());
            builder.setTitle(titleId)
                .setIcon(android.R.drawable.stat_sys_warning)
                .setMessage(messageId)
                .setCancelable(false)
                .setPositiveButton(android.R.string.ok, null)
                .setNegativeButton("Learn More", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Intent.ACTION_VIEW, helpUri);
                        startActivity(intent);
                    }
                });
        } else if(loggedOut==1){
            builder = new AlertDialog.Builder(Collections.parentActivity);
            builder.setTitle(titleId)
                .setIcon(android.R.drawable.stat_sys_warning)
                .setMessage(messageId)
                .setCancelable(false)
                .setPositiveButton(android.R.string.ok, null)
                .setNegativeButton("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();

    }


    @Override
    public void onRestart(){
        super.onRestart();
        Intent previewMessage = new Intent(StampiiStore.this, StampiiStore.class);
        TabGroupActivity parentActivity = (TabGroupActivity)getParent();
        parentActivity.startChildActivity("StampiiStore", previewMessage);
        this.finish();
    }

}
Run Code Online (Sandbox Code Playgroud)

以下是我在清单文件中声明服务的方式:

<service android:name="BillingService" />

    <receiver android:name="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>
Run Code Online (Sandbox Code Playgroud)

有关如何连接市场的任何建议?

And*_*oid 6

我有同样的问题,可能你的错误和我的一样.我在我的活动中使用了TabHost,我发现TabSpec无法绑定到服务.所以检查一下:

使用getApplicationContext().bindService而不仅仅是活动上的bindService解决了问题,因为它使用的是更高级别的应用程序上下文.