-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async consumption / transaction finalization #199
Comments
I think you are right that the current handling could be improved. My remarks:
|
You are making an interesting point.
I think the goal is to make it easy for developers. It should be us that move things off the main thread as needed. |
|
We have to keep in mind, that users may assume (and it is actually convenient in most of the cases), that |
Updating the UI on the render thread is easy enough on libGDX (even if the calling thread is different). We just have to make it clear in the documentation. |
@alex-dorokhov Maybe my message(s) are coming over a little hard? This isn't a criticism of your solution. Your solution is perfectly fine. The same goes with the existing solution which is simply a different way of going about things. It's a matter of taste and question of if we really want to change everything up for all the existing users? Based on gdx-pay-example's MyFancyInAppShop.java, a developer has to assume the |
@noblemaster, it's my fail, I thought it is called in the main thread in the current implementation. About your second point:
I also like the solution by @MrStahlfelge, and I would apply it as it was originally suggested by him. Using a library should be both straightforward in async and sync style, so returning a boolean is better. Throwing an exception looks a bit "hacky" (IMHO). The last sentence makes me think that you suggest to keep only the sync way of doing the things:
First of all, what you suggest with the exception, is currently implemented in the library. The purchase is not consumed if "handlePurchase" throws an exception. But the original issue is not only about blocking the calling thread, but also about convenience of implementation. Especially, when the code inside "handlePurchase" calls external async code. Let me show you an example, where the "async" implementation is easier than the "sync" one. To simplify, I'm using the interface suggested by @MrStahlfelge. Example. Using of Gdx.net.sendHttpRequest to send transaction info to the server. The async way: class AsyncExample implements PurchaseObserver {
PurchaseManager purchaseManager;
@Override
public boolean handlePurchase(final Transaction transaction) {
Gdx.net.sendHttpRequest(new Net.HttpRequest("https://server.com/purchase"), new Net.HttpResponseListener() {
@Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
if (httpResponse.getStatus().getStatusCode() == HttpStatus.SC_ACCEPTED) {
purchaseManager.purchaseHandled(transaction);
} else {
//TODO handle failure
}
}
@Override
public void failed(Throwable t) {
//TODO handle failure
}
@Override
public void cancelled() {
}
});
return false;
}
} The sync way: class SyncExample implements PurchaseObserver {
PurchaseManager purchaseManager;
@Override
public boolean handlePurchase(final Transaction transaction) {
final AtomicBoolean result = new AtomicBoolean(false);
synchronized (result) {
Gdx.net.sendHttpRequest(new Net.HttpRequest("https://server.com/purchase"), new Net.HttpResponseListener() {
@Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
if (httpResponse.getStatus().getStatusCode() == HttpStatus.SC_ACCEPTED) {
synchronized (result) {
result.set(true);
result.notify();
}
} else {
synchronized (result) {
result.set(false);
result.notify();
}
}
}
@Override
public void failed(Throwable t) {
//TODO handle failure
}
@Override
public void cancelled() {
}
});
try {
result.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result.get();
}
} As you can see, the first solution is much easier to implement, because the user is not forced to use locks to convert async code (in this case Gdx.net, based on callbacks) to sync (gdx-pay). In my personal case things go even harder, because it is not only required to send request to the server and wait for response, but additionally update the database (which happens in separate DB-thread). |
I agree, a I also agree that the async way is what we should do. They It's a little odd to always |
In short
I believe it makes sense to add a new method to PurchaseManager, which has to be called by the application to mark the transaction as handled (instead of marking it automatically).
Motivation
Currently a PurchaseManager handles successful purchase in the following way:
The step 2 is performed in the same thread as the step 1. In particular it also means, that if the step 1 is failed (application code), then the transaction is not marked as completed (which is the right behaviour).
The problem appears, when PurchaseObserver.handlePurchase is written in asynchronous way: to apply transaction the application needs to perform some long running operation (e.g. call to a database). In this case the only option is too trigger async operation and block the thread, which has 2 downsides:
Proposal
The alternative to step 5 would be to add an option to PurchaseManagerConfig or an argument to PurchaseManager.install, something like: "boolean automaticTransactionFinalisation".
What do you think?
The text was updated successfully, but these errors were encountered: