应用内账单 - BillingHelper没有完全实例化 [英] In-App-Billing - BillingHelper no fully instantiated

查看:186
本文介绍了应用内账单 - BillingHelper没有完全实例化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试做


您好,大家好,我试图创造一些在-APP-产品适用于我的应用程序,其捐赠的使用。由于IM放弃我的朋友免费MUSIK - 他斥责。不管结果如何,我创建了5应用内商品在我的开发人员帐户:

  • donate_small
  • donate_midsmall
  • donate_medium
  • donate_large
  • donate_xlarge

这是产生有参考关键的我。它们被保存并发布。 应用内:现在,如果在本教程编写的应用程序内置-服务简易教程的。在code似乎工作完美,没有错误,当我编译人口统计学code它的工作原理。但是,当我尝试它,我得到永诺这个错误:

  12-06 14:23:49.400:E / BillingService有(4719):BillingHelper不完全实例化
 

问题


那么,我需要改变,用户可以选择在应用程序,产品有多种不同的?我是否需要申报的地方他们?此外,如果你对我有一个伟大的教程,在那里这东西是一个好describen和充分的工作,请告诉我。

这是我用我所有的应用程序,服务类:

de.stepforward

  • ChannelActivity
  • SplashActivity

de.stepforward.billing

  • AppMainTest.class - >这是我的活动
  • BillingHelper.class
  • BillingReceiver.class
  • BillingSecurity.class
  • BillingService.class
  • C.class

de.stepforward.billing.util

  • Base64.class
  • Base64De coderException.class

有关的那一刻,我会为你我BillingHelper和活动的code,如果你需要更多的code就告诉我。

code


AppMainTest.class

 包de.stepforward.billing;


进口de.stepforward.R;
进口de.stepforward.R.id;
进口de.stepforward.R.layout;
进口android.app.Activity;
进口android.content.Context;
进口android.content.Intent;
进口android.os.Bundle;
进口android.os.Handler;
进口android.util.Log;
进口android.view.View;
进口android.view.View.OnClickListener;
进口android.widget.Button;
进口android.widget.ImageView;

公共类AppMainTest扩展活动实现OnClickListener {

    私有静态最后字符串变量=BillingService有;

    私人语境mContext;
    私人ImageView的purchaseableItem;
    私人按钮purchaseButton;

    / **第一次创建活动时调用。 * /
    @覆盖
    公共无效的onCreate(包savedInstanceState){
        super.onCreate(savedInstanceState);
        Log.i(BillingService有,启动);
        的setContentView(R.layout.donate);

        mContext =这一点;

        purchaseButton =(按钮)findViewById(R.id.main_purchase_yes);
        purchaseButton.setOnClickListener(本);
        purchaseableItem =(ImageView的)findViewById(R.id.main_purchase_item);

        startService(新意图(mContext,BillingService.class));
        BillingHelper.setCompletedHandler(mTransactionHandler);
    }

    公开处理程序mTransactionHandler =新的处理程序(){
            公共无效的handleMessage(android.os.Message味精){
                Log.i(TAG,交易完成);
                Log.i(TAG,交易状态:+ BillingHelper.latestPurchase.purchaseState);
                Log.i(TAG,购买的物品是:+ BillingHelper.latestPurchase.productId);

                如果(BillingHelper.latestPurchase.isPurchased()){
                    showItem();
                }
            };

    };

    公共无效的onClick(视图v){
        开关(v.getId()){
        案例R.id.main_purchase_yes:
            如果(BillingHelper.isBillingSupported()){
                BillingHelper.requestPurchase(mContextandroid.test.purchased);
                // android.test.purchased或android.test.canceled或android.test.refunded或com.blundell.item.passport
            } 其他 {
                Log.i(TAG,无法在此设备上购买);
                purchaseButton.setEnabled(假); // XXX preSS按钮服务启动将禁用之前,当它不应该
            }

            打破;
        默认:
            //滩
            Log.i(TAG。默认密码:+ v.getId());
            打破;
        }

    }

    私人无效showItem(){
        purchaseableItem.setVisibility(View.VISIBLE);
    }

    @覆盖
    保护无效的onPause(){
        Log.i(TAG的onPause()));
        super.onPause();
    }

    @覆盖
    保护无效的onDestroy(){
        BillingHelper.stopService();
        super.onDestroy();
    }
}
 

BillingHelper.class

 包de.stepforward.billing;

进口的java.util.ArrayList;

进口android.app.PendingIntent;
进口android.app.PendingIntent.CanceledException;
进口android.content.Context;
进口android.content.Intent;
进口android.os.Bundle;
进口android.os.Handler;
进口android.os.RemoteException;
进口android.util.Log;

进口com.android.vending.billing.IMarketBillingService;

进口de.stepforward.billing.BillingSecurity.VerifiedPurchase;
进口de.stepforward.billing.C.Response code;

公共类BillingHelper {

    私有静态最后字符串变量=BillingService有;

    私有静态IMarketBillingService MSERVICE;
    私有静态语境mContext;
    私有静态处理程序mCompletedHandler;

    受保护的静态VerifiedPurchase latestPurchase;

    受保护的静态无效instantiateHelper(上下文的背景下,IMarketBillingService服务){
        MSERVICE =服务;
        mContext =背景;
    }

    受保护的静态无效setCompletedHandler(处理程序处理){
        mCompletedHandler =处理程序;
    }

    受保护的静态布尔isBillingSupported(){
        如果(amIDead()){
            返回false;
        }
        包请求= makeRequestBundle(CHECK_BILLING_SUPPORTED);
        如果(MSERVICE!= NULL){
            尝试 {
                捆绑响应= mService.sendBillingRequest(要求);
                响应code code =响应code.valueOf((整数)response.get(RESPONSE_ code));
                Log.i(TAG,isBillingSupported的回答是:+ code.toString());
                如果(响应code.RESULT_OK.equals(code)){
                    返回true;
                } 其他 {
                    返回false;
                }
            }赶上(RemoteException的E){
                Log.e(TAG,isBillingSupported反应是:RemoteException的,E);
                返回false;
            }
        } 其他 {
            Log.i(TAG,isBillingSupported反应是:BillingService.mService =空);
            返回false;
        }
    }

    / **
     *一个REQUEST_PURCHASE要求也触发两个异步响应(广播意图)。
     *首先,Android Market的应用程序发送RESPONSE_ code广播的意图,它提供了有关请求的错误信息。 (我忽略)
     *接下来,如果请求成功,Android Market的应用程序发送一个IN_APP_NOTIFY广播意图。
     *此消息包含一个通知ID,您可以用它来获取交易信息的REQUEST_PURCHASE
     * @参数activityContext
     * @参数的itemId
     * /
    受保护的静态无效requestPurchase(上下文activityContext,字符串的itemId){
        如果(amIDead()){
            返回;
        }
        Log.i(TAG,requestPurchase());
        包请求= makeRequestBundle(REQUEST_PURCHASE);
        request.putString(ITEM_ID的itemId);
        尝试 {
            捆绑响应= mService.sendBillingRequest(要求);

            //该RESPONSE_ code键为您提供了请求的状态
            整数响应codeINDEX =(整数)response.get(RESPONSE_ code);
            //该PURCHASE_INTENT键为您提供了一个PendingIntent,你可以用它来启动结账UI
            PendingIntent pendingIntent =(PendingIntent)response.get(PURCHASE_INTENT);
            //该REQUEST_ID键为您提供了该请求的唯一请求标识符
            龙requestIndentifier =(长)response.get(REQUEST_ID);
            Log.i(TAG,目前的要求是:+ requestIndentifier);
            C.Response code响应code = C.Response code.valueOf(响应codeINDEX);
            Log.i(TAG,REQUEST_PURCHASE同步响应code:+响应code.toString());

            startBuyPageActivity(pendingIntent,新意图(),activityContext);
        }赶上(RemoteException的E){
            Log.e(TAG,失败,网络错误也许,E);
            Log.e(TAG,计费支持:+ isBillingSupported());
        }
    }

    / **
     *一个GET_PURCHASE_INFORMATION要求也触发两个异步响应(广播意图)。
     *首先,Android Market的应用程序发送RESPONSE_ code广播的意图,它提供了有关请求的状态和错误信息。 (我忽略)
     *接下来,如果请求成功,Android Market的应用程序发送PURCHASE_STATE_CHANGED广播意图。
     *此消息包含详细的交易信息。
     *该交易信息包含在一个签署JSON字符串(未加密)。
     *消息包括签名,以便您可以验证签名的字符串的完整性
     * @参数notifyIds
     * /
    受保护的静态无效getPurchaseInformation(字符串[] notifyIds){
        如果(amIDead()){
            返回;
        }
        Log.i(TAG,getPurchaseInformation());
        包请求= makeRequestBundle(GET_PURCHASE_INFORMATION);
        //该REQUEST_NONCE键包含加密安全随机数(一次使用的号码),您必须生成。
        //在Android Market的应用程序返回该随机数与PURCHASE_STATE_CHANGED广播意图,所以你可以验证的交易信息的完整性。
        request.putLong(现时,BillingSecurity.generateNonce());
        //该NOTIFY_IDS键包含通知的ID,您在IN_APP_NOTIFY收到的数组广播意图。
        request.putStringArray(NOTIFY_IDS,notifyIds);
        尝试 {
            捆绑响应= mService.sendBillingRequest(要求);

            //该REQUEST_ID键为您提供了该请求的唯一请求标识符
            龙requestIndentifier =(长)response.get(REQUEST_ID);
            Log.i(TAG,目前的要求是:+ requestIndentifier);
            //该RESPONSE_ code键为您提供了请求的状态
            整数响应codeINDEX =(整数)response.get(RESPONSE_ code);
            C.Response code响应code = C.Response code.valueOf(响应codeINDEX);
            Log.i(TAG,GET_PURCHASE_INFORMATION同步响应code:+响应code.toString());

        }赶上(RemoteException的E){
            Log.e(TAG,失败,网络错误也许,E);
            Log.e(TAG,计费支持:+ isBillingSupported());
        }
    }

    / **
     *要确认是否收到交易信息在发送
     * CONFIRM_NOTIFICATIONS请求。
     *
     *一个CONFIRM_NOTIFICATIONS请求触发一个异步响应,一个RESPONSE_ code广播意图。
     *该广播意图提供有关请求的状态和错误信息。
     *
     *注:作为最佳实践,您应该不发送CONFIRM_NOTIFICATIONS要求购买的项目,直到你已经交付的项目给用户。
     *这样,如果你的应用程序崩溃或其他什么东西prevents从提供产品的应用,
     *你的应用程序仍然会收到从Android Market表明您需要提供产品的IN_APP_NOTIFY广播意图
     * @参数notifyIds
     * /
    受保护的静态无效confirmTransaction(字符串[] notifyIds){
        如果(amIDead()){
            返回;
        }
        Log.i(TAG,confirmTransaction());
        包请求= makeRequestBundle(CONFIRM_NOTIFICATIONS);
        request.putStringArray(NOTIFY_IDS,notifyIds);
        尝试 {
            捆绑响应= mService.sendBillingRequest(要求);

            //该REQUEST_ID键为您提供了该请求的唯一请求标识符
            龙requestIndentifier =(长)response.get(REQUEST_ID);
            Log.i(TAG,目前的要求是:+ requestIndentifier);

            //该RESPONSE_ code键为您提供了请求的状态
            整数响应codeINDEX =(整数)response.get(RESPONSE_ code);
            C.Response code响应code = C.Response code.valueOf(响应codeINDEX);

            Log.i(TAG,CONFIRM_NOTIFICATIONS同步响应code:+响应code.toString());
        }赶上(RemoteException的E){
            Log.e(TAG,失败,网络错误也许,E);
            Log.e(TAG,计费支持:+ isBillingSupported());
        }
    }

    / **
     *
     *可用于当用户重新安装应用程序,以回馈之前购买。
     *如果出售的购买类型的项目是每个用户帐户管理,这意味着谷歌将有ofthis交易记录
     *
     *一个RESTORE_TRANSACTIONS要求也触发两个异步响应(广播意图)。
     *首先,Android Market的应用程序发送RESPONSE_ code广播的意图,它提供了有关请求的状态和错误信息。
     *接下来,如果请求成功,Android Market的应用程序发送PURCHASE_STATE_CHANGED广播意图。
     *此消息包含详细的交易信息。所述交易信息包含在签名JSON字符串(未加密)。
     *消息包括签名,以便您可以验证签名的字符串的完整性
     * @参数的随机数
     * /
    受保护的静态无效restoreTransactionInformation(长随机数){
        如果(amIDead()){
            返回;
        }
        Log.i(TAG,confirmTransaction());
        包请求= makeRequestBundle(RESTORE_TRANSACTIONS);
        //该REQUEST_NONCE键包含加密安全随机数(一次使用的号码),您必须生成
        request.putLong(现时,随机数);
        尝试 {
            捆绑响应= mService.sendBillingRequest(要求);

            //该REQUEST_ID键为您提供了该请求的唯一请求标识符
            龙requestIndentifier =(长)response.get(REQUEST_ID);
            Log.i(TAG,目前的要求是:+ requestIndentifier);

            //该RESPONSE_ code键为您提供了请求的状态
            整数响应codeINDEX =(整数)response.get(RESPONSE_ code);
            C.Response code响应code = C.Response code.valueOf(响应codeINDEX);
            Log.i(TAG,RESTORE_TRANSACTIONS同步响应code:+响应code.toString());
        }赶上(RemoteException的E){
            Log.e(TAG,失败,网络错误也许,E);
            Log.e(TAG,计费支持:+ isBillingSupported());
        }
    }

    私有静态布尔amIDead(){
        如果(MSERVICE == NULL || mContext == NULL){
            Log.e(TAG,BillingHelper不完全实例化);
            返回true;
        } 其他 {
            返回false;
        }
    }

    私有静态捆绑makeRequestBundle(字符串方法){
        包请求=新包();
        request.putString(BILLING_REQUEST,法);
        request.putInt(API_VERSION,1);
        request.putString(PACKAGE_NAME,mContext.getPackageName());
        返回请求;
    }

    / **
     *
     *
     *您必须从活动的背景下,而不是一个应用程序上下文启动挂起的意图
     *您不能使用singleTop启动模式启动挂起的意图
     * @参数pendingIntent
     * @参数意图
     * @参数方面
     * /
    私有静态无效startBuyPageActivity(PendingIntent pendingIntent,意图意图,上下文语境){
        // TODO增加2.0以上的实施与反思,现在只用1.6 implem

        //这是在Android 1.6。应用程序内结账页面活动将在其
        //自己单独的活动栈,而不是对活动栈
        // 应用程序。
        尝试 {
            pendingIntent.send(背景下,0,意图);
        }赶上(CanceledException E){
            Log.e(TAG,startBuyPageActivity CanceledException);
        }
    }

    受保护的静态无效verifyPurchase(字符串签名数据,特征码){
        ArrayList的< VerifiedPurchase>购买= BillingSecurity.verifyPurchase(签名数据,签名);
        latestPurchase = purchases.get(0);

        confirmTransaction(新的String [] {latestPurchase.notificationId});

        如果(mCompletedHandler!= NULL){
            mCompletedHandler.sendEmptyMessage(0);
        } 其他 {
            Log.e(TAG。verifyPurchase错误处理程序没有实例化你叫setCompletedHandler()?);
        }
    }

    公共静态无效stopService(){
        mContext.stopService(新意图(mContext,BillingService.class));
        MSERVICE = NULL;
        mContext = NULL;
        mCompletedHandler = NULL;
        Log.i(TAG,停止服务);
    }
}
 


提前THX对您有所帮助。

最好的问候

野生动物园

附件


清单

 < XML版本=1.0编码=UTF-8&GT?;
<舱单的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    包=de.stepforward
    安卓版code =6
    机器人:VERSIONNAME =1.3.2>

    <使用-SDK安卓的minSdkVersion =10/>

    <应用
        机器人:图标=@可绘制/ ic_launcher
        机器人:标签=@字符串/ APP_NAME
        机器人:主题=@风格/ StepForward.Theme
        机器人:可调试=假
         >
        <活动
            机器人:标签=@字符串/ APP_NAME
            机器人:名称=SplashActivity。>
            <意向滤光器>
                <作用机器人:名称=android.intent.action.MAIN/>

                <类机器人:名称=android.intent.category.LAUNCHER/>
            &所述; /意图滤光器>
        < /活性GT;
            <活动机器人:名称=。ChannelActivity>< /活性GT;
            <活动机器人:名称=。billing.AppMainTest>< /活性GT;
    < /用途>
            <使用-权限的Andr​​oid:名称=android.permission.INTERNET对/>
            <使用-权限的Andr​​oid:名称=com.android.vending.BILLING/>


 <! - 在-APP-Einkäufe献给捐赠 - >
    <服务机器人:名称=。BillingService有/>

    <接收机器人:名称=。BillingReceiver>
      <意向滤光器>
        <作用机器人:名称=com.android.vending.billing.IN_APP_NOTIFY/>
        <作用机器人:名称=com.android.vending.billing.RESPONSE_ code/>
        <作用机器人:名称=com.android.vending.billing.PURCHASE_STATE_CHANGED/>
      &所述; /意图滤光器>
    < /接收器>

< /舱单>
 

LogCat中(更多信息)

  12-07 13:58:14.334:I / BillingService有(20997):启动
12-07 13:58:14.364:D / dalvikvm(20997):GC_EXTERNAL_ALLOC释放15K,58%免费2919K / 6791K,外部414K / 926K,暂停22毫秒
12-07 13:58:23.864:E / BillingService有(20997):BillingHelper不完全实例化
12-07 13:58:23.864:I / BillingService有(20997):无法在此设备上购买
 

解决方案

如果你看一看这个方法:

 私有静态布尔amIDead(){
    如果(MSERVICE == NULL || mContext == NULL){
        Log.e(TAG,BillingHelper不完全实例化);
        返回true;
    } 其他 {
        返回false;
    }
}
 

该日志被打印,当你的服务为空或上下文。 Aaaand您服务是空的时候:

 受保护的静态无效instantiateHelper(上下文的背景下,IMarketBillingService服务){
            MSERVICE =服务;
            mContext =背景;
    }
 

instantiateHelper不叫aaand

  @覆盖
    公共无效onServiceConnected(组件名名称,服务的IBinder){
            Log.i(TAG,市场结算服务连接起来。);
            MSERVICE = IMarketBillingService.Stub.asInterface(服务);
            BillingHelper.instantiateHelper(getBaseContext(),MSERVICE);
    }
 

时调用您的服务已连接,我可以看到你尝试连接到像这样的服务:

  startService(新意图(mContext,BillingService.class));
 

这样:

你有没有宣布的清单中的服务?

 <应用...
     <服务机器人:名称=。BillingService有/>
     ...
 < /用途>
 

修改

我是对的:-)这只是你的服务标签是之外应用程序标记。

在这里看到: http://stackoverflow.com/a/5439157/413127

What I try to do


Hello Guys, I'm trying to create some In-App-Products for my App, its for donation use. Because im giving away free musik of my friend - he raps. Whatever, I created 5 In-App Products on my developer-account:

  • donate_small
  • donate_midsmall
  • donate_medium
  • donate_large
  • donate_xlarge

This are the reference key's I generated there. They are saved and published. Now if written a In-App-Service over this tutorial: In-App Easy Tutorial. The Code seems to work perfect, there are no errors and when i compile the demo-code it works. But when I try it I get allways this error:

12-06 14:23:49.400: E/BillingService(4719): BillingHelper not fully instantiated

Question


So what do I need to change the the user can choose the diffrent in-app-products? Do I need to declare them somewhere?? Also if you have a great tutorial for me, where this stuff is all well describen and fully working, please tell me.

This are the Classes I use for my In-app-service:

de.stepforward

  • ChannelActivity
  • SplashActivity

de.stepforward.billing

  • AppMainTest.class -> This is my Activity
  • BillingHelper.class
  • BillingReceiver.class
  • BillingSecurity.class
  • BillingService.class
  • C.class

de.stepforward.billing.util

  • Base64.class
  • Base64DecoderException.class

For the moment I'll provide you the Code of my BillingHelper and Activity, if you need more Code just tell me.

Code


AppMainTest.class

package de.stepforward.billing;


import de.stepforward.R;
import de.stepforward.R.id;
import de.stepforward.R.layout;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class AppMainTest extends Activity implements OnClickListener{

    private static final String TAG = "BillingService";

    private Context mContext;
    private ImageView purchaseableItem;
    private Button purchaseButton;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("BillingService", "Starting");
        setContentView(R.layout.donate);

        mContext = this;

        purchaseButton = (Button) findViewById(R.id.main_purchase_yes);
        purchaseButton.setOnClickListener(this);
        purchaseableItem = (ImageView) findViewById(R.id.main_purchase_item);

        startService(new Intent(mContext, BillingService.class));
        BillingHelper.setCompletedHandler(mTransactionHandler);
    }

    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()){
                    showItem();
                }
            };

    };

    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.main_purchase_yes:
            if(BillingHelper.isBillingSupported()){
                BillingHelper.requestPurchase(mContext, "android.test.purchased"); 
                // android.test.purchased or android.test.canceled or android.test.refunded or com.blundell.item.passport
            } else {
                Log.i(TAG,"Can't purchase on this device");
                purchaseButton.setEnabled(false); // XXX press button before service started will disable when it shouldnt
            }

            break;
        default:
            // nada
            Log.i(TAG,"default. ID: "+v.getId());
            break;
        }

    }

    private void showItem() {
        purchaseableItem.setVisibility(View.VISIBLE);
    }

    @Override
    protected void onPause() {
        Log.i(TAG, "onPause())");
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        BillingHelper.stopService();
        super.onDestroy();
    }
}

BillingHelper.class

package de.stepforward.billing;

import java.util.ArrayList;

import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;

import com.android.vending.billing.IMarketBillingService;

import de.stepforward.billing.BillingSecurity.VerifiedPurchase;
import de.stepforward.billing.C.ResponseCode;

public class BillingHelper {

    private static final String TAG = "BillingService";

    private static IMarketBillingService mService;
    private static Context mContext;
    private static Handler mCompletedHandler;

    protected static VerifiedPurchase latestPurchase;

    protected static void instantiateHelper(Context context, IMarketBillingService service) {
        mService = service;
        mContext = context;
    }

    protected static void setCompletedHandler(Handler handler){
        mCompletedHandler = handler;
    }

    protected static boolean isBillingSupported() {
        if (amIDead()) {
            return false;
        }
        Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED");
        if (mService != null) {
            try {
                Bundle response = mService.sendBillingRequest(request);
                ResponseCode code = ResponseCode.valueOf((Integer) response.get("RESPONSE_CODE"));
                Log.i(TAG, "isBillingSupported response was: " + code.toString());
                if (ResponseCode.RESULT_OK.equals(code)) {
                    return true;
                } else {
                    return false;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "isBillingSupported response was: RemoteException", e);
                return false;
            }
        } else {
            Log.i(TAG, "isBillingSupported response was: BillingService.mService = null");
            return false;
        }
    }

    /**
     * A REQUEST_PURCHASE request also triggers two asynchronous responses (broadcast intents). 
     * First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides error information about the request. (which I ignore)
     * Next, if the request was successful, the Android Market application sends an IN_APP_NOTIFY broadcast intent. 
     * This message contains a notification ID, which you can use to retrieve the transaction details for the REQUEST_PURCHASE
     * @param activityContext
     * @param itemId
     */
    protected static void requestPurchase(Context activityContext, String itemId){
        if (amIDead()) {
            return;
        }
        Log.i(TAG, "requestPurchase()");
        Bundle request = makeRequestBundle("REQUEST_PURCHASE");
        request.putString("ITEM_ID", itemId);
        try {
            Bundle response = mService.sendBillingRequest(request);

            //The RESPONSE_CODE key provides you with the status of the request
            Integer responseCodeIndex   = (Integer) response.get("RESPONSE_CODE");
            //The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI
            PendingIntent pendingIntent = (PendingIntent) response.get("PURCHASE_INTENT");
            //The REQUEST_ID key provides you with a unique request identifier for the request
            Long requestIndentifier     = (Long) response.get("REQUEST_ID");
            Log.i(TAG, "current request is:" + requestIndentifier);
            C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
            Log.i(TAG, "REQUEST_PURCHASE Sync Response code: "+responseCode.toString());

            startBuyPageActivity(pendingIntent, new Intent(), activityContext);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed, internet error maybe", e);
            Log.e(TAG, "Billing supported: "+isBillingSupported());
        }
    }

    /**
     * A GET_PURCHASE_INFORMATION request also triggers two asynchronous responses (broadcast intents). 
     * First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request.  (which I ignore)
     * Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent. 
     * This message contains detailed transaction information. 
     * The transaction information is contained in a signed JSON string (unencrypted). 
     * The message includes the signature so you can verify the integrity of the signed string
     * @param notifyIds
     */
    protected static void getPurchaseInformation(String[] notifyIds){
        if (amIDead()) {
            return;
        }
        Log.i(TAG, "getPurchaseInformation()");
        Bundle request = makeRequestBundle("GET_PURCHASE_INFORMATION");
        // The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate.
        // The Android Market application returns this nonce with the PURCHASE_STATE_CHANGED broadcast intent so you can verify the integrity of the transaction information.
        request.putLong("NONCE", BillingSecurity.generateNonce());
        // The NOTIFY_IDS key contains an array of notification IDs, which you received in the IN_APP_NOTIFY broadcast intent.
        request.putStringArray("NOTIFY_IDS", notifyIds);
        try {
            Bundle response = mService.sendBillingRequest(request);

            //The REQUEST_ID key provides you with a unique request identifier for the request
            Long requestIndentifier     = (Long) response.get("REQUEST_ID");
            Log.i(TAG, "current request is:" + requestIndentifier);
            //The RESPONSE_CODE key provides you with the status of the request
            Integer responseCodeIndex   = (Integer) response.get("RESPONSE_CODE");
            C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
            Log.i(TAG, "GET_PURCHASE_INFORMATION Sync Response code: "+responseCode.toString());

        } catch (RemoteException e) {
            Log.e(TAG, "Failed, internet error maybe", e);
            Log.e(TAG, "Billing supported: "+isBillingSupported());
        }
    }

    /**
     * To acknowledge that you received transaction information you send a
     * CONFIRM_NOTIFICATIONS request.
     * 
     * A CONFIRM_NOTIFICATIONS request triggers a single asynchronous response—a RESPONSE_CODE broadcast intent. 
     * This broadcast intent provides status and error information about the request.
     * 
     * Note: As a best practice, you should not send a CONFIRM_NOTIFICATIONS request for a purchased item until you have delivered the item to the user. 
     * This way, if your application crashes or something else prevents your application from delivering the product,
     * your application will still receive an IN_APP_NOTIFY broadcast intent from Android Market indicating that you need to deliver the product
     * @param notifyIds
     */
    protected static void confirmTransaction(String[] notifyIds) {
        if (amIDead()) {
            return;
        }
        Log.i(TAG, "confirmTransaction()");
        Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS");
        request.putStringArray("NOTIFY_IDS", notifyIds);
        try {
            Bundle response = mService.sendBillingRequest(request);

            //The REQUEST_ID key provides you with a unique request identifier for the request
            Long requestIndentifier     = (Long) response.get("REQUEST_ID");
            Log.i(TAG, "current request is:" + requestIndentifier);

            //The RESPONSE_CODE key provides you with the status of the request
            Integer responseCodeIndex   = (Integer) response.get("RESPONSE_CODE");
            C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);

            Log.i(TAG, "CONFIRM_NOTIFICATIONS Sync Response code: "+responseCode.toString());
        } catch (RemoteException e) {
            Log.e(TAG, "Failed, internet error maybe", e);
            Log.e(TAG, "Billing supported: " + isBillingSupported());
        }
    }

    /**
     * 
     * Can be used for when a user has reinstalled the app to give back prior purchases. 
     * if an item for sale's purchase type is "managed per user account" this means google will have a record ofthis transaction
     * 
     * A RESTORE_TRANSACTIONS request also triggers two asynchronous responses (broadcast intents). 
     * First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request. 
     * Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent. 
     * This message contains the detailed transaction information. The transaction information is contained in a signed JSON string (unencrypted).
     * The message includes the signature so you can verify the integrity of the signed string
     * @param nonce
     */
    protected static void restoreTransactionInformation(Long nonce) {
        if (amIDead()) {
            return;
        }
        Log.i(TAG, "confirmTransaction()");
        Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS");
        // The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate
        request.putLong("NONCE", nonce);
        try {
            Bundle response = mService.sendBillingRequest(request);

            //The REQUEST_ID key provides you with a unique request identifier for the request
            Long requestIndentifier     = (Long) response.get("REQUEST_ID");
            Log.i(TAG, "current request is:" + requestIndentifier);

            //The RESPONSE_CODE key provides you with the status of the request
            Integer responseCodeIndex   = (Integer) response.get("RESPONSE_CODE");
            C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
            Log.i(TAG, "RESTORE_TRANSACTIONS Sync Response code: "+responseCode.toString());
        } catch (RemoteException e) {
            Log.e(TAG, "Failed, internet error maybe", e);
            Log.e(TAG, "Billing supported: " + isBillingSupported());
        }
    }

    private static boolean amIDead() {
        if (mService == null || mContext == null) {
            Log.e(TAG, "BillingHelper not fully instantiated");
            return true;
        } else {
            return false;
        }
    }

    private static Bundle makeRequestBundle(String method) {
        Bundle request = new Bundle();
        request.putString("BILLING_REQUEST", method);
        request.putInt("API_VERSION", 1);
        request.putString("PACKAGE_NAME", mContext.getPackageName());
        return request;
    }

    /**
     * 
     * 
     * You must launch the pending intent from an activity context and not an application context
     * You cannot use the singleTop launch mode to launch the pending intent
     * @param pendingIntent
     * @param intent
     * @param context
     */
    private static void startBuyPageActivity(PendingIntent pendingIntent, Intent intent, Context context){
        //TODO add above 2.0 implementation with reflection, for now just using 1.6 implem

        // This is on Android 1.6. The in-app checkout page activity will be on its
        // own separate activity stack instead of on the activity stack of
        // the application.
        try {
            pendingIntent.send(context, 0, intent);         
        } catch (CanceledException e){
            Log.e(TAG, "startBuyPageActivity CanceledException");
        }
    }

    protected static void verifyPurchase(String signedData, String signature) {
        ArrayList<VerifiedPurchase> purchases = BillingSecurity.verifyPurchase(signedData, signature);
        latestPurchase = purchases.get(0);

        confirmTransaction(new String[]{latestPurchase.notificationId});

        if(mCompletedHandler != null){
            mCompletedHandler.sendEmptyMessage(0);
        } else {
            Log.e(TAG, "verifyPurchase error. Handler not instantiated. Have you called setCompletedHandler()?");
        }
    }

    public static void stopService(){
        mContext.stopService(new Intent(mContext, BillingService.class));
        mService = null;
        mContext = null;
        mCompletedHandler = null;
        Log.i(TAG, "Stopping Service");
    }
}


Thx for your Help in Advance

Best Regards

safari

Appendix


Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.stepforward"
    android:versionCode="6"
    android:versionName="1.3.2" >

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/StepForward.Theme"
        android:debuggable="false"
         >
        <activity
            android:label="@string/app_name"
            android:name=".SplashActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
            <activity android:name=".ChannelActivity" ></activity>
            <activity android:name=".billing.AppMainTest"></activity>
    </application>
            <uses-permission android:name="android.permission.INTERNET" />
            <uses-permission android:name="com.android.vending.BILLING" />


 <!--  In-App-Einkäufe für Donate -->   
    <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>

</manifest>

LogCat (some more information)

12-07 13:58:14.334: I/BillingService(20997): Starting
12-07 13:58:14.364: D/dalvikvm(20997): GC_EXTERNAL_ALLOC freed 15K, 58% free 2919K/6791K, external 414K/926K, paused 22ms
12-07 13:58:23.864: E/BillingService(20997): BillingHelper not fully instantiated
12-07 13:58:23.864: I/BillingService(20997): Can't purchase on this device

解决方案

If you take a look at this method:

  private static boolean amIDead() {
    if (mService == null || mContext == null) {
        Log.e(TAG, "BillingHelper not fully instantiated");
        return true;
    } else {
        return false;
    }
}

That log is printed when your Service is null or your context. Aaaand your service is null when:

 protected static void instantiateHelper(Context context, IMarketBillingService service) {
            mService = service;
            mContext = context;
    }

instantiateHelper is not called aaand

 @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "Market Billing Service Connected.");
            mService = IMarketBillingService.Stub.asInterface(service);
            BillingHelper.instantiateHelper(getBaseContext(), mService);
    }

is called when your Service is connected, and I can see you attempt to connect to the service like this:

 startService(new Intent(mContext, BillingService.class));

SO:

Have you declared the service in your manifest?

 <application ...
     <service android:name=".BillingService" />
     ...
 </application>

EDIT

I was right :-) It's just your service tag is outside your application tag.

See here: http://stackoverflow.com/a/5439157/413127

这篇关于应用内账单 - BillingHelper没有完全实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆