问题
Edit: Please see my answer below for my solution.
I'm receiving a NullPointerException error when trying to check owned items using getPurchases()
and I'm not sure why as I've followed the documentation. I'm finding that the documentation can be misleading at times, so here I am.
I've set up In-app billing such that it initializes okay and I get a success message. After it is initialized, I want to check if the user has previously purchased item(s) and display a button based on the result. Here is my code so far and below is my LogCat. The error appears at the beginning of the try/catch.
public class MainActivity extends Activity
{
IabHelper mHelper;
IInAppBillingService mService;
private Button buyButton;
AdView adView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adView = (AdView)this.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
buyButton = (Button)findViewById(R.id.buyButton);
String base64EncodedPublicKey = "public_key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
@Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
Log.d("TEST", "In-app Billing setup failed: " + result);
}
else
{
Log.d("TEST", "In-app Billing is set up OK"); //This passes!
}
}
});
bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), mServiceConn, Context.BIND_AUTO_CREATE);
try
{
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null); //This is line 66 referenced in LogCat error
if(ownedItems.getInt("RESPONSE_CODE") == 0)
{
ArrayList ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
for(int i=0; i<purchaseDataList.size(); ++i)
{
String purchaseData = (String) purchaseDataList.get(i);
String signature = (String) signatureList.get(i);
String sku = (String) ownedSkus.get(i);
Log.d("TEST", "Purchased: "+i+ " -> "+sku);
}
}
else
{
Log.d("TEST", "Not Items Owned!");
}
}
catch(RemoteException e)
{
//TODO: Error, unable to get owned items
e.printStackTrace();
Log.d("TEST", "owned items check failed: "+e);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
ServiceConnection mServiceConn = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("TEST", "mService ready to go!"); //This displays if try/catch above is commented out. Is it not waiting for mService to be initialized before running try/catch?
}
@Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
};
@Override
public void onDestroy()
{
super.onDestroy();
if(mServiceConn != null)
{
unbindService(mServiceConn);
}
}
}
And the Logcat errors
01-04 22:39:35.052: E/AndroidRuntime(32375): FATAL EXCEPTION: main
01-04 22:39:35.052: E/AndroidRuntime(32375): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.test.iab/com.test.iab.MainActivity}: java.lang.NullPointerException
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread.access$600(ActivityThread.java:141)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.os.Handler.dispatchMessage(Handler.java:99)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.os.Looper.loop(Looper.java:137)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread.main(ActivityThread.java:5041)
01-04 22:39:35.052: E/AndroidRuntime(32375): at java.lang.reflect.Method.invokeNative(Native Method)
01-04 22:39:35.052: E/AndroidRuntime(32375): at java.lang.reflect.Method.invoke(Method.java:511)
01-04 22:39:35.052: E/AndroidRuntime(32375): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
01-04 22:39:35.052: E/AndroidRuntime(32375): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
01-04 22:39:35.052: E/AndroidRuntime(32375): at dalvik.system.NativeStart.main(Native Method)
01-04 22:39:35.052: E/AndroidRuntime(32375): Caused by: java.lang.NullPointerException
01-04 22:39:35.052: E/AndroidRuntime(32375): at com.test.iab.MainActivity.onCreate(MainActivity.java:66)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.Activity.performCreate(Activity.java:5104)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
01-04 22:39:35.052: E/AndroidRuntime(32375): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
01-04 22:39:35.052: E/AndroidRuntime(32375): ... 11 more
01-04 22:39:35.083: E/GooglePlayServicesUtil(32375): The Google Play services resources were not found. Check your project configuration to ensure that the resources are included.
My guess is that I'm not initializing mService correctly? But I don't understand if I'm not because I believe I did it exactly as the documentation outlines. Thanks for guidance.
回答1:
The reason this was not working was because onServiceConnected()
isn't guaranteed to be called until onCreate()
completes. So I moved the try/catch code into onServiceConnected()
. I'm not sure if this is best practice, but it seems to have fixed my issue. Here is the resulting code for anyone finding themselves with this same error. This is exuding button and purchasing logic. I recommend this tutorial for that code.
public class MainActivity extends Activity
{
IabHelper mHelper;
IInAppBillingService mService;
static final String ITEM_SKU = "android.test.purchased";
private Button buyButton;
AdView adView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adView = (AdView)this.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
buyButton = (Button)findViewById(R.id.buyButton);
bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), mServiceConn, Context.BIND_AUTO_CREATE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
ServiceConnection mServiceConn = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("TEST", "mService ready to go!");
checkOwnedItems();
}
@Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
};
private void checkownedItems()
{
try
{
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
if(ownedItems.getInt("RESPONSE_CODE") == 0)
{
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE");
String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN");
if(purchaseDataList.size() > 0)
{
//Item(s) owned
for(int i=0; i<purchaseDataList.size(); ++i)
{
String purchaseData = purchaseDataList.get(i);
String signature = signatureList.get(i); //Note signatures do not appear to work with android.test.purchased (silly google)
String sku = ownedSkus.get(i);
}
}
else
{
//Item(s) not owned
String base64EncodedPublicKey = "public_key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
@Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
Log.d("TEST", "In-app Billing setup failed: " + result);
}
else
{
Log.d("TEST", "In-app Billing is set up OK");
}
}
});
}
}
else
{
//Error checking owned items
}
}
catch(RemoteException e)
{
e.printStackTrace();
}
}
@Override
public void onDestroy()
{
super.onDestroy();
if(mServiceConn != null)
{
unbindService(mServiceConn);
}
}
}
回答2:
Your code just says
IInAppBillingService mService;
It is null, you need to initialise it.
Put a System.out.println()
in the onServiceConnected
method to see if it is being initialised. If it is not, it means that onServiceConnected
is not being called so the bindService
method might not be working as intended
EDIT: I think i know the problem.
Add this outside the onCreate method as a class variable...
ServiceConnection mServiceConn;
Then change
ServiceConnection mServiceConn = new ServiceConnection()
to
mServiceConn = new ServiceConnection()
来源:https://stackoverflow.com/questions/20930674/getpurchases-nullpointerexception-initializing-mservice