I'm getting exception reports of DeadObjectException from some devices when requesting prices for in app purchases. The exception occurs when I call getSkuDetails on my billing service connection.
I don't find the documentation on this particularly clear.
The object you are calling has died, because its hosting process no longer exists.
Am I understanding this correctly if I assume that the billing service has been killed by Android for some reason?
Why?
Is there a way to stop this from happening?
Should I stop this from happening?
How should I deal with the exception?
Is there a way I can reproduce this scenario for testing?
I have two methods
void bindToBillingService() {
AndroidLauncher.getActivity().bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
}
public void unbindBillingService() {
if (getBillingServiceConnection() != null) {
AndroidLauncher.getActivity().unbindService(mServiceConn);
}
}
Should I be doing something like this?
try {
// Do something billing related
} catch (DeadObjectException e) {
unbindBillingService();
bindToBillingService();
// Wait for a connection and then try again
}
Related
I'm facing a issue in an app I'm developing that someone may be able to help me with.
I'm working on an app for payment machines that use Android (7.1), with it you are able to pay with credit card. The company that developed these machines offer a java sdk. Since this app is using React Native, I wrote a Native Module to connect with it. The SDK is really simple.
After the app is authenticated (done just once), All I have to do is call the doAsyncPayment method with the right parameters, and once its done, it will call the onSuccess or onError method of the listener.
When either of these methods is called, I call the callback parameter I got from the React Native side. In theory it's supposed to work, and it does, most of the time. Every once in a while, something happens that these callbacks are not called, so the client stays on the loading payment screen forever. No error is reported on sentry.
The only thing I'm thinking is that somehow the bridge is losing it? Maybe because these method runs asyncronously in the native side (another thread?), the connection is maybe lost?
I haven't been able to reproduce it, because it happens rarely and at random (I suppose).
I don't know much of Native Android, so any help is appreciated.
Here is a resumed piece of code of what's happening:
Native method being called:
#ReactMethod
public void startPayment(...other params,
Callback onErrorCallback, Callback onSuccessCallback) {
setPlugPag();
PlugPagPaymentData paymentData = new PlugPagPaymentData(paymentType, amountCents,
installmentType, installments, userReference, printReceipt);
PlugPagInitializationResult initResult = plugPag.initializeAndActivatePinpad(new
PlugPagActivationData(activationCode));
if(initResult.getResult() == PlugPag.RET_OK) {
plugPag.doAsyncPayment(paymentData, new PlugPagPaymentListener() {
PlugPagPrintResult printResult;
#Override
public void onSuccess(#NonNull PlugPagTransactionResult plugPagTransactionResult) {
if(printResult != null) {
WritableMap printResultMap = Arguments.createMap();
printResultMap.putInt("result", printResult.getResult());
printResultMap.putString("message", printResult.getMessage());
printResultMap.putString("errorCode", printResult.getErrorCode());
onSuccessCallback.invoke(TransactionResult.toRNWritableMap(plugPagTransactionResult), printResultMap);
}else {
onSuccessCallback.invoke(TransactionResult.toRNWritableMap(plugPagTransactionResult));
}
}
#Override
public void onError(#NonNull PlugPagTransactionResult plugPagTransactionResult) {
onErrorCallback.invoke(TransactionResult.toRNWritableMap(plugPagTransactionResult));
}
#Override
public void onPaymentProgress(#NonNull PlugPagEventData plugPagEventData) {
WritableMap eventDataMap = Arguments.createMap();
eventDataMap.putString("customMessage", plugPagEventData.getCustomMessage());
eventDataMap.putInt("eventCode", plugPagEventData.getEventCode());
sendEvent(Constants.EVENT_PLUG_PAG_PAYMENT_PROGRESS_CHANGED, eventDataMap);
}
#Override
public void onPrinterSuccess(#NonNull PlugPagPrintResult plugPagPrintResult) {
}
#Override
public void onPrinterError(#NonNull PlugPagPrintResult plugPagPrintResult) {
}
});
}else {
onErrorCallback.invoke(String.valueOf(initResult.getResult()));
}
}
on React native side, I just call:
startPayment(...other params, errorCallback, successCallback);
Maybe because the way the listener is instantiated?
Thanks in advance.
I am showing Google's GDPR consent form and I am noticing a lot of these reports:
Fatal Exception: android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy#38734f2 is not valid; is your activity running?
com.my.project.MainActivity$4.onConsentFormLoaded
As context I use MainActivity.this:
private void displayConsentForm() {
consentForm = new ConsentForm.Builder(MainActivity.this, GeneralUtils.getAppsPrivacyPolicy())
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
consentForm.show(); // crashing here for some users
}
#Override
public void onConsentFormOpened() { }
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
if(userPrefersAdFree) {
ConsentInformation.getInstance(MainActivity.this)
.setConsentStatus(NON_PERSONALIZED);
} else {
ConsentInformation.getInstance(MainActivity.this)
.setConsentStatus(consentStatus);
}
initAds();
}
#Override
public void onConsentFormError(String errorDescription) {
Log.e("Error",errorDescription);
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.withAdFreeOption()
.build();
consentForm.load();
}
Here is additional Firebase crash report:
Why is this happening and how to prevent it? I am not sure what additional check to put before consentForm.show() and I can not reproduce the issue. Maybe it would suffice if I put this check before showing the form:
if(!MainActivity.this.isFinishing() && !MainActivity.this.isDestroyed())
?
The easiest way around this would be to just put a try-catch block around consentForm.show() and catch the BadTokenException.
It's not really clean, but it's likely that this is happening when the Activity finishes (maybe the user closes the app from Recents right as the Dialog is loading).
If this were my project, I'd first try adding that if statement you have (although you don't need the MainActivity.this. part; you can just call isFinishing() and isDestroyed() directly). Since you're referencing an Activity Context, this should take care of it.
However, if it still crashes, you should first look into reproducing it. Try getting to just before displayConsentForm() is called, then closing the app from Recents. Play around with the timing and you'll probably reproduce the crash. If not, then just add the try-catch. The Activity isn't displayed, since it's throwing that error, so the user isn't actually in the app.
I have a custom Chromecast receiver that I launch from an Android app when the user selects their Chromecast device from the Cast button. I find that I often get a timeout on the initial connection, but the second time it works fine. Is the issue most likely my web server not responding fast enough, or are there other factors that might cause the timeout?
I'm getting the CastStatusCodes.TIMEOUT in onApplicationConnectionFailed().
My code to launch
(EDITED to include launchApplication)
Builder builder = new GoogleApiClient.Builder(mContext);
builder.addApi(Cast.API, apiOptionsBuilder.build());
builder.addConnectionCallbacks(this);
builder.addOnConnectionFailedListener(this);
mApiClient = builder.build();
if (mApiClient == null) return;
mApiClient.connect();
...
Cast.CastApi.launchApplication(mApiClient, mApplicationId)
.setResultCallback(new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(ApplicationConnectionResult result) {
if (result.getStatus().isSuccess()) {
onApplicationConnected(
result.getApplicationMetadata(),
result.getApplicationStatus(),
result.getSessionId(),
result.getWasLaunched());
} else {
onApplicationConnectionFailed(result.getStatus().getStatusCode());
}
}
});
The code you have posted is prior to loading the application so if you are getting a timeout in your onApplicationConnectionFailed, then it is further down in your code and not the part that you have posted here. If it is the loading of your application that fails, you need to check on your network and web server, etc.
I've got the following crash in GameHelper.java:
[main] java.lang.NullPointerException at
com.google.android.gms.common.ConnectionResult.startResolutionForResult(Unknown
Source) at
com.google.example.games.basegameutils.GameHelper.resolveConnectionResult(GameHelper.java:752)
at
com.google.example.games.basegameutils.GameHelper.onConnectionFailed(GameHelper.java:729)
The only reason I think that could happen is if mActivity == null at GameHelper.java:752:
mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE);
mActivity gets null on onStop()
Is it possible that GameHelper.java has bug and can crash if onConnectionFailed() happens after onStop() is called?
Thanks.
EDITED:
It happened after update to the latest Play API (rev 15) together with the updated GameHelper.java.
EDIT:
This now has been fixed in latest GameHelper version:
https://github.com/playgameservices/android-samples/commit/e7b3758c136b5b434f1dfd9ca8c03f75aad70f09
OLD ANSWER:
For me it happens on the start of the app, when Google Play Services asks me to sign in and if I click cancel, this same error happens.
So when leaving from your own activity to sign in activity, it dispatches onStop event, and fails to connect because of the user initiated process, which is resolvable, thus the error happens.
So my quick hack was changing:
catch (SendIntentException e)
to simply
catch (Exception e)
So it would also catch Null pointer exception
Of course in this case the signup process might not proceed, so I initate relogin on another action and it seems to work for now.
More thorough hack could be trying to resolve the result on activity start, for that we define pending resolution variable:
// Are we expecting the result of a resolution flow?
boolean mExpectingResolution = false;
boolean mPendingResolution = false;
Then on the error line we check if activity is not null
if(mActivity != null)
mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE);
else
mPendingResolution = true;
And on start we check and try to resolve it:
if(mPendingResolution && mConnectionResult != null)
try {
mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE);
} catch (SendIntentException e) {
e.printStackTrace();
}
This should help until the official resolution from lib supporters :)
Today is 16th september 2014 and I still facing this problem.
I don't know why anyone else did not answer about to comment GameHelper line.
In onStop method there is a line to set mActivity variable as null.
I commented this line (like below) and my app is working properly.
/** Call this method from your Activity's onStop(). */
public void onStop() {
debugLog("onStop");
assertConfigured("onStop");
if (mGoogleApiClient.isConnected()) {
debugLog("Disconnecting client due to onStop");
mGoogleApiClient.disconnect();
} else {
debugLog("Client already disconnected when we got onStop.");
}
mConnecting = false;
mExpectingResolution = false;
// let go of the Activity reference
//mActivity = null; //COMMENT THIS LINE!!!!
//COMMENT ABOVE LINE
}
Is there any problem doing that:?
Ok, I understood, that when you bind to a remote Service, it won't bind until you return from the callback. I need to bind to a service and execute some method from it immediately. Is there any way? My code looks like:
try {
Parser parser = FrameworkBridge.getFrameworkBridge(this).getParser();
if (parser != null) {
for (Article a : parser.getArticlesList("http://www.bt.dk/mecommobile/latest/news_article/%s/%s?output_type=xml", 1379, 20)) {
listAdapter.add(a);
}
}
} catch (RemoteException e) {
Log.d(TAG, "Service communication failure");
} catch (FrameworkNotInstalledException e) {
Log.d(TAG, "No framework installed. Install it!");
}
Here FrameworkBridge.getFrameworkBridge(this).getParser() performs all service connection routine and returns remote interface. The problem is -- when I'm in the code above, the connection is not performed yet, therefore parser is null. How can I make it connect before exiting the code?
onServiceConnected(..) will tell you when the service is connected and the remote interface is established. Don't try to call any methods in the service until this is triggered.
mContext.bindService( new Intent("name of the class"), this, Context.BIND_AUTO_CREATE);
the above method call on the copy of the context you store should automatically bind the service when you need it. I am not 100% sure, but this should help you out somewhat.