应用内账单 - BillingHelper没有完全实例化 [英] In-App-Billing - BillingHelper no fully instantiated
问题描述
我尝试做
您好,大家好,我试图创造一些在-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:机器人=http://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;
< /用途>
<使用-权限的Android:名称=android.permission.INTERNET对/>
<使用-权限的Android:名称=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屋!