I've had in app billing v3 implemented in my app for about a week now. I used a lot of android's sample code to simplify the integration. I've been logging a crash fairly often that I can't seem to reproduce:
Exception Type: java.lang.RuntimeException
Reason: Unable to destroy activity {[package].billing.BillingActivity}: java.lang.IllegalArgumentException: Service not registered: [package].billing.util.IabHelper$1#40646a70
It seems to be breaking on this line:
if (mContext != null) mContext.unbindService(mServiceConn);
I'm binding this service in my onCreate method and disposing it in my onDestroy method (which is where this error is logged). Any pointers?
You could replace the line you mentioned:
if (mContext != null) mContext.unbindService(mServiceConn);
by this line
if (mContext != null && mService != null) mContext.unbindService(mServiceConn);
This should do the trick
I checked out the latest version of the sample project and up to today my recommendation is to currently to NOT use IabHelper. It is massively flawed.
To give you an idea:
1.) the async methods of IabHelper start a new thread. If IabHelper.dispose() is called while a thread is running you will always get various exceptions you cannot even handle.
2.) If the connection to the billing service goes down, they set it to null. But apart from that they never check if mService is null before accessing the methods. So it will always crash with NullPointerException in this case.
public void onServiceDisconnected(ComponentName name) {
logDebug("Billing service disconnected.");
mService = null;
and this is just the tip of the ice berg. Seriously I do not understand how somebody can publish this as reference code.
I just encountered the same issue but on android emulator. Billing v3 requires that Google Play app should be launched at least once and since the emulator lack of Google Play app it cannot set up helper and cannot dispose it in onDestroy().
My personal workaround is just skipping that error in try/catch:
#Override
protected void onDestroy() {
super.onDestroy();
if (bHelper != null){
try {
bHelper.dispose();
}catch (IllegalArgumentException ex){
ex.printStackTrace();
}finally{}
}
bHelper = null;
}
Add this in every onDestroy() where you dispose helper. Works fine for me.
The IabHelper class is working in a normal way.
What you need to do is:
when you call startSetup for the helper, you need to pass a callback IabHelper.OnIabSetupFinishedListener which will tell you the result of starting setup. If you get failure in the callback, the service connection with the google play services was not established.
You should handle future calls to IabHelper depending upon the result received in IabHelper.OnIabSetupFinishedListener. You can surely keep a boolean field to know what was the status.
The answer sam provided is actually a trick (in his own words). The helper classes aren't supposed to throw exceptions so that the user of those classes can implement some task in such scenarios.
And of-course, try/catch is best way if you don't want to go in details (whenever anything breaks due to exception, surely first thing which comes in mind is to put that in a try/catch block).
Related
While profiling my Google Cardboard application, I found out a very large memory leak (15Mb!) each time I left the activity with the 3D graphics.
After a long and grievous investigation, I found out that the source of the problem was a Context leak that happened each time I closed my CardboardActivity subclass.
The solution can be found in the accepted answer*
* wow... this is awkward... Note for any kind (and experienced) reviewer: I am writing a question to whom I know the answer already: am I supposed to do something for style, like add some fake suspense ("will our heroes prevail?! Find out in the accepted answer!"), like in a old Batman TV series or something?
After dicing and slicing my CardboardActivity subclass, until nothing else but the base class remained, I had to conclude that the base class itself was leaking the context.
I searched the web and found this post explaining how the activity in question leaked the context by failing to un-register a listener with a private instance of a class.
Upon trying to invoke said method manually (using reflection), I found out that in the current version of the Cardboard SDK (0.5.4 at the time of writing), the field is not present anymore.
Long story short: all sensors are now handled by an undocumented (yet public) SensorConnection class instantiated in CardboardActivity as a sensorConnection field, which is still plagued by the bug detailed in my first link.
This led me to this solution:
get the sensorConnection field in the CardboardActivity by reflection
use it to get the magneticSensor field, again by reflection
invoke the setOnCardboardTheaterListener with null argument, to clear the binding holding the reference to the Context in the Activity onDestroy method.
this boils down to the following code:
private void workAroundLeak() {
try {
// Get the sensor Connection
Class<?> c1 = Class.forName("com.google.vrtoolkit.cardboard.CardboardActivity");
Field sensorsField = c1.getDeclaredField("sensorConnection");
sensorsField.setAccessible(true);
SensorConnection sc = (SensorConnection) sensorsField.get(this);
if(sc == null) return;
// Get the magnetSensor
Class<?> c2 = Class.forName("com.google.vrtoolkit.cardboard.sensors.SensorConnection");
Field magnetField = c2.getDeclaredField("magnetSensor");
magnetField.setAccessible(true);
MagnetSensor ms = (MagnetSensor) magnetField.get(sc);
if(ms == null) return;
ms.setOnCardboardTriggerListener(null);
} catch(Exception e) {}
}
#Override
protected void onDestroy() {
workAroundLeak();
super.onDestroy();
}
which solved the problem entirely.
A word to the wise: since this solution relies on reflection, it might break (without consequences other than doing nothing, likely) as soon as Google will update the SDK (possibly fixing the issue in a clean way).
Hope this helps someone
I'm making an app that uses Network Service Discovery, let's call it a "Wi-fi Chat". And at some point I want to unregister a service created earlier in order to avoid creation of countless copies of it. But the problem is, when I cal;
mNsdManager.unregisterService(mRegistrationListener);
I get "listener is not registered" error. To make sure that I have STILL THE SAME object of that listener I even initialized it in a class that extends Application class and still I get this error. So, the question is: how to unregister a service properly? Thank you in advance.
Also, I looked through "NsdChat" sample application, and it crashes at the same point with the same error!
Well, I kinda found a solution. Much thanks to this Wizard who fixed NsdChat Google example.
The solution is: in tearDown() method, inside which we call unregisterService(RegistrationListener listener) we should do this
public void tearDown() {
if (mRegistrationListener != null) {
try {
mNsdManager.unregisterService(mRegistrationListener);
} finally {
}
mRegistrationListener = null;
}
}
Though I still have no clue how actually this works, so if you have any thoughts regarding this puzzle, please post an answer
My activity's onInit() contains a call to TextToSpeech.setEngineByPackageName():
tts = new TextToSpeech(this, this);
tts.setEngineByPackageName("com.ivona.tts.voicebeta.eng.usa.kendra");
It works on an Android 2.2.2 device, but on an Android 2.3.4 device it produced a NullPointerException, with the following stack trace:
E/TextToSpeech.java - setEngineByPackageName(3423): NullPointerException
W/System.err(3423): java.lang.NullPointerException
W/System.err(3423): at android.os.Parcel.readException(Parcel.java:1328)
W/System.err(3423): at android.os.Parcel.readException(Parcel.java:1276)
W/System.err(3423): at android.speech.tts.ITts$Stub$Proxy.setEngineByPackageName(ITts.java:654)
W/System.err(3423): at android.speech.tts.TextToSpeech.setEngineByPackageName(TextToSpeech.java:1356)
Since I am providing a hard-coded string parameter, I know that the parameter isn't what's causing the NullPointerException.
I also know that setEngineByPackageName() is deprecated but that's only since API 14, so this couldn't be the reason.
Any idea what could be causing this NullPointerException?
EDIT: I wouldn't have been concerned with the "why" if this didn't result in an endless bombardment of:
I/TextToSpeech.java(3652): initTts() successfully bound to service
Followed by calls to onInit() (by the system, not by my code).
My hope is that if I underderstand why this is happening, I can stop the bombardment of onInit()s and recover gracefully from the error.
Is the TTS engine you are referencing installed on the 2.3.4 device? If it is, it might be a platform bug.
EDIT:
Don't remember what results I got when I did this, but calling setEngineByPackageName() when the package doesn't exists is not a good idea. Check if it is installed and don't try to use it if it's not. Something like:
boolean isPackageInstalled(String packageName) {
PackageManager pm = context.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(packageName, 0);
return pi != null;
} catch (NameNotFoundException e) {
return false;
}
}
Attempting to investigate this myself, in case an expert on the subject isn't around:
The NullPointerException stack trace is printed by setEngineByPackageName() itself in the catch handler for this try clause:
try {
result = mITts.setEngineByPackageName(enginePackageName);
if (result == TextToSpeech.SUCCESS){
mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
}
}
Which suggests that either of the following is null:
mITts
mCachedParams
mCachedParams is unlikely to be the null one because it is initialized in the constructor. So this leaves us with mITts:
If I examine TextToSpeech.initTts() I can easily spot 2 points in which mITts can remain null:
onServiceConnected()
onServiceDisconnected()
But why this is happening only on the 2.3.4 device? This is still a mystery.
Possible clue: The method TextToSpeech.initTts() ends with the following comment:
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
// TODO handle case where the binding works (should always work) but
// the plugin fails
Which may explain why I receive a barrage of onInit()s ("initTts() successfully bound to service"): Binding always works, but since the package is not installed, "the plugin fails".
The question now is how to stop this endless loop...
I am using following code:
public class MyBillingService extends Service implements ServiceConnection
{
String TAG = "MyBillingService";
#Override
public void onCreate()
{
super.onCreate();
Log.i(TAG, "onCreate");
try {
boolean bindResult = getBaseContext().bindService(
new Intent(IMarketBillingService.class.getName()), this, Context.BIND_AUTO_CREATE);
if (bindResult) {
Log.i(TAG, "Service bind successful.");
} else {
Log.e(TAG, "Could not bind to the MarketBillingService.");
}
} catch (SecurityException e) {
Log.e(TAG, "Security exception: " + e);
}
}
}
I have also added the IMarketBillingService.aidl but still it show like:
Could not bind to the
MarketBillingService
Can you point out my mistake?
I don't know if this is the cause for your problem, but you are using bindService() on getBaseContext(). Is there a reason that you don't call bindService() on the service instance? That's what the example service does.
The base context seems to be mostly unneeded and the general advice seems to not use it.
See the other question: What's the difference between the various methods to get a Context?
Besides that, you need to have the newest (or at least a recent) version of the Android Market App on your device, although I assume that it updates itself.
There is a way to test if the market app supports In-App Billing, somewhere described in the In-App-Billing reference. I assume that it is worth to do that check.
I was testing on an old Android device which had a clean system install. This didn't have a copy of the Google Market app on it (hat tip #sstn).
So I logged in with a Google account, then launched the Market. That prompted me to agree to terms and the Google Play app appeared in the list of applications.
Then I removed and reinstalled my app and it bound to the service correctly!
I am getting a
NullPointerException at android.app.ActivityThread$PackageInfo$ServiceDispatcher.doConnected(ActivityThread.java:1012)
My application is not even in the stack trace, so I have no idea what is going on.
I am trying to connect to a service when it happens.
How can I fix this problem?
This probably way too old for my response to be of any use, but in case anybody else has this problem, here's what it was for me. I am using a newer version of the SDK, so I'm getting this issue at line 1061.
It was happening to me because I was passing a null ServiceConnection object to the function bindService.
It was helpful to browse the SDK code in my case - although the line numbers don't add up due to version differences, the general code is likely the same (and I knew which method to check):
1097 // If there was an old service, it is not disconnected.
1098 if (old != null) {
1099 mConnection.onServiceDisconnected(name);
1100 }
1101 // If there is a new service, it is now connected.
1102 if (service != null) {
1103 mConnection.onServiceConnected(name, service);
1104 }
mConnection was pretty much the only thing that made sense to be null.