Android 인앱 결제 : 다른 비동기 작업 (진행 중)으로 인해 비동기 작업을 시작할 수 없습니다.
IabHelper
Google의 자습서에서 권장 하는 유틸리티 클래스를 사용하고 있는데이 오류로 인해 큰 타격을 받고 있습니다. 분명히 IabHelper
동시에 여러 비동기 작업을 실행할 수 없습니다. 재고 조사가 진행되는 동안 구매를 시작하려고 시도하는 것만으로도 성공했습니다.
여기 에 onActivityResult
제안 된대로 이미 기본 클래스에서 구현하려고 시도했지만 오류가 발생하기 전에 해당 메서드에 대한 호출도 얻지 못합니다. 그럼 내가 발견 이 있지만이 찾을 아무 생각이없는 방법을 - 그것은에없는 클래스를.flagEndAsync
IabHelper
이제이 문제를 해결하는 방법을 찾고 있습니다 (전체 she-bang을 다시 구현하지 않음). 내가 생각할 수있는 유일한 해결책 asyncActive
은 비동기 작업이 시작되기 전에 확인 되는 부울 필드를 만들고 다른 작업이 활성화 된 경우에는 수행하지 않는 것입니다. 그러나 그것은 다른 많은 문제를 가지고 있으며 활동 전반에 걸쳐 작동하지 않습니다. 또한 비동기 작업 대기열을 만들고 전혀 실행하지 않는 대신 허용되는 즉시 실행하는 것을 선호합니다.
이 문제에 대한 해결책이 있습니까?
간단하고 까다로운 솔루션
purchaseItem 메서드 를 호출하기 전에 다음 줄을 추가하십시오.
if (billingHelper != null) billingHelper.flagEndAsync();
그래서 당신의 코드는 이렇게 보입니다.
if (billingHelper != null) billingHelper.flagEndAsync();
purchaseItem("android.test.purchased");
참고 : 다른 패키지에서 호출하는 경우 IabHelper에서 공용 flagEndAsync () 메서드를 만드는 것을 잊지 마십시오.
반드시 당신이 IabHelper의 전화 확인 handleActivityResult
활동의에 onActivityResult
, 그리고 NOT 조각 위치의에 onActivityResult
.
다음 코드 조각은 TrivialDrive의 MainActivity 에서 가져온 것입니다 .
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
최신 정보:
- 이제 인앱 결제 버전 3 API가 있습니다 (2013 년 버전은 무엇 이었나요?).
- 코드 샘플이 Github 로 이동되었습니다 . 위의 스 니펫은 현재 샘플을 반영하도록 편집되었지만 논리적으로 이전과 동일합니다.
이것은 크래킹하기 쉽지 않았지만 필요한 해결 방법을 찾았습니다. 최근 구글에 상당히 실망한 그들의 안드로이드 웹 사이트는 엉망이되었고 (유용한 정보를 찾기가 매우 어려웠다) 샘플 코드가 형편 없다. 몇 년 전 Android 개발을 할 때 모든 것이 훨씬 쉬워졌습니다! 이것은 또 다른 예입니다 ...
실제로 IabUtil은 버그가 있으며 자체 비동기 작업을 제대로 호출하지 않습니다. 이 문제를 안정화하는 데 필요한 완전한 해결 방법 :
1) 메서드를 flagEndAsync
공개합니다. 그것은 단지 보이지 않습니다.
2) 모든 리스너 호출 iabHelper.flagEndAsync
을 통해 프로 시저가 올바르게 완료된 것으로 표시되었는지 확인합니다. 모든 청취자에게 필요한 것 같습니다.
3) 발생할 수 있는 try/catch
것을 잡기 위해 통화를 둘러싸고 IllegalStateException
그렇게 처리합니다.
결국 Kintaro와 비슷한 일을하게되었습니다. 그러나 캐치 끝에 mHelper.flagEndAsync ()를 추가했습니다. 사용자는 여전히 건배를 받지만 다음에 구매 버튼을 누르면 비동기 작업이 종료되고 구매 버튼을 다시 사용할 준비가됩니다.
if (mHelper != null) {
try {
mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, "");
}
catch(IllegalStateException ex){
Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show();
mHelper.flagEndAsync();
}
}
파일 flagEndAsync()
내부를 찾아 공개 기능으로 IabHelper.java
만드십시오 .
구매 호출을 시도하기 전에 flagEndAsync()
당신을 위해 IabHelper
.
다음 코드와 같이 somthig를 수행해야합니다.
mHelper.flagEndAsync();
mHelper.launchPurchaseFlow(AboutActivity.this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "payload-string");
다른 SO 스레드를 우연히 발견 할 때까지 같은 문제가 발생했습니다 . 구매를 초기화하는 활동에 포함해야하는 다른 스레드에서 찾은 코드의 수정 된 버전을 포함하고 있습니다.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Pass on the activity result to the helper for handling
// NOTE: handleActivityResult() will update the state of the helper,
// allowing you to make further calls without having it exception on you
if (billingHelper.handleActivityResult(requestCode, resultCode, data)) {
Log.d(TAG, "onActivityResult handled by IABUtil.");
handlePurchaseResult(requestCode, resultCode, data);
return;
}
// What you would normally do
// ...
}
나를 위해 한 간단한 트릭은 IabHelper에서 메서드를 만드는 것입니다.
public Boolean getAsyncInProgress() {
return mAsyncInProgress;
}
그런 다음 코드에서 다음을 확인하십시오.
if (!mHelper.getAsyncInProgress())
//launch purchase
else
Log.d(TAG, "Async in progress already..)
정말 성가신 문제입니다. 다음은 완벽한 코드는 아니지만 사용자 친화적이고 나쁜 평가 및 충돌을 방지하는 빠르고 더러운 솔루션입니다.
if (mHelper != null) {
try {
mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, "");
}
catch(IllegalStateException ex){
Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show();
}
}
이렇게하면 사용자는 한 번 더 탭하면 (최악의 경우 2 번) 결제 팝업이 표시됩니다.
도움이되기를 바랍니다.
액티비티에서 onActivityResult requestCode를 확인하고 구매에 사용한 PURCHASE_REQUEST_CODE와 일치하면 조각에 전달하면됩니다.
FragmentTransaction에서 조각을 추가하거나 교체 할 때 태그를 설정하기 만하면됩니다.
fTransaction.replace(R.id.content_fragment, fragment, fragment.getClass().getName());
그런 다음 활동의 onActivityResult에서
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == PurchaseFragment.PURCHASE_REQUEST_CODE) {
PurchaseFragment fragment = getSuportFragmentManager().findFragmentByTag(PurchaseFragment.class.getNAme());
if(fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
조각으로 코딩하면 IabHelper.java 에이 코드가 있습니다.
void flagStartAsync(String operation) {
if (mAsyncInProgress) {
flagEndAsync();
}
if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" +
operation + ") because another async operation(" + mAsyncOperation + ") is in progress.");
mAsyncOperation = operation;
mAsyncInProgress = true;
logDebug("Starting async operation: " + operation);
}
또는 여기에서 최신 IabHelper.java 파일을 얻을 수 있습니다. https://code.google.com/p/marketbilling/source/browse/
3 월 15 일 버전에서이 문제를 해결했습니다. (변경 사항이없는 다른 파일은 15 일에 커밋되었습니다.)
"android.test.canceled"가 전송 된 SKU 일 때 null 인 텐트 엑스트라로 인해 테스트 중에 발생한 충돌을 수정해야했습니다. 나는 변했다 :
int getResponseCodeFromIntent(Intent i) {
Object o = i.getExtras().get(RESPONSE_CODE);
에:
int getResponseCodeFromIntent(Intent i) {
Object o = i.getExtras() != null ? i.getExtras().get(RESPONSE_CODE) : null;
이 문제가 가끔 발생했으며, 제 경우에는 IabHelper의 onServiceConnected 메서드가 기본 서비스의 연결이 끊겼다가 다시 연결되는 경우 (예 : 간헐적 인 네트워크 연결로 인해) 두 번 이상 호출 될 수 있다는 사실을 추적했습니다.
필자의 경우 특정 작업은 "다른 비동기 작업 (launchPurchaseFlow)이 진행 중이므로 비동기 작업 (인벤토리 새로 고침)을 시작할 수 없음)"이었습니다.
내 앱이 작성되는 방식에서는 queryInventory를 완료 할 때까지 launchPurchaseFlow를 호출 할 수 없으며, onIabSetupFinished 핸들러 함수에서만 queryInventory를 호출합니다.
IabHelper 코드는 onServiceConnected가 호출 될 때마다이 핸들러 함수를 호출하며 이는 두 번 이상 발생할 수 있습니다.
안드로이드 문서 onServiceDisconnected에 대한 말한다 :
서비스 연결이 끊어졌을 때 호출됩니다. 이는 일반적으로 서비스를 호스팅하는 프로세스가 충돌하거나 종료되었을 때 발생합니다. 이렇게해도 ServiceConnection 자체가 제거되지는 않습니다. 서비스에 대한이 바인딩은 활성 상태로 유지되며 다음에 서비스가 실행될 때 onServiceConnected (ComponentName, IBinder)에 대한 호출을 받게됩니다.
문제를 설명합니다.
틀림없이 IabHelper는 onIabSetupFinished 리스너 함수를 두 번 이상 호출해서는 안되지만 다른 한편으로는 이미 수행하고 결과를 얻은 경우이 함수 내에서 queryInventory를 호출하지 않음으로써 내 앱의 문제를 해결하는 것은 사소한 일이었습니다. .
IabHelpr 클래스의 또 다른 주요 문제는 여러 메서드에서 RuntimeExcptions (IllegalStateException)를 던지는 잘못된 선택입니다. 대부분의 경우 자신의 코드에서 RuntimeExeptions를 던지는 것은 확인되지 않은 예외 라는 사실 때문에 바람직하지 않습니다 . 이는 자체 애플리케이션을 방해하는 것과 같습니다. 잡히지 않으면 이러한 예외가 발생하여 앱이 중단됩니다.
이에 대한 해결책은 자체 검사 된 예외 를 구현 하고 IllegalStateException 대신 IabHelper 클래스를 변경하여 예외를 발생 시키는 것입니다. 그러면 컴파일 타임에 코드에서 발생할 수있는 모든 곳에서이 예외를 처리해야합니다.
내 사용자 지정 예외는 다음과 같습니다.
public class MyIllegalStateException extends Exception {
private static final long serialVersionUID = 1L;
//Parameterless Constructor
public MyIllegalStateException() {}
//Constructor that accepts a message
public MyIllegalStateException(String message)
{
super(message);
}
}
IabHelper 클래스를 변경하면 클래스 메서드를 호출하는 코드에서 확인 된 예외를 처리 할 수 있습니다. 예를 들면 :
try {
setUpBilling(targetActivityInstance.allData.getAll());
} catch (MyIllegalStateException ex) {
ex.printStackTrace();
}
나는 같은 문제가 있었고 문제는 onActivityResult 메서드를 구현하지 않았다는 것입니다.
@Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
try
{
if (billingHelper == null)
{
return;
} else if (!billingHelper.handleActivityResult(requestCode, resultCode, data))
{
super.onActivityResult(requestCode, resultCode, data);
}
} catch (Exception exception)
{
super.onActivityResult(requestCode, resultCode, data);
}
}
예, 나도이 문제에 직면했지만이 문제를 해결했지만 다음을 사용하여 해결했습니다.
IabHelper mHelpermHelper = new IabHelper(inappActivity, base64EncodedPublicKey);
mHelper.flagEndAsync();
위의 방법은 모든 플래그를 중지합니다. 나를위한 작업은 확인해야합니다.
이 답변은 @Wouter가 본 문제를 직접 해결합니다.
It is true that onActivityResult()
must be triggered, like many people have said. However, the bug is that Google's code isn't triggering onActivityResult()
in certain circumstances, i.e. when you're pressing your [BUY] button twice when running the debug build of your app.
Additionally, one major problem is that the user may be in a shaky environment (i.e. Bus or subway) and presses your [BUY] button twice... suddenly you've got yourself an exception !
At least Google fixed this embarrassing exception https://github.com/googlesamples/android-play-billing/commit/07b085b32a62c7981e5f3581fd743e30b9adb4ed#diff-b43848e47f8a93bca77e5ce95b1c2d66
Below is what I implemented in the same class where IabHelper
is instantiated (for me, this is in the Application class) :
/**
* invokes the startIntentSenderForResult - which will call your activity's onActivityResult() when it's finished
* NOTE: you need to override onActivityResult() in your activity.
* NOTE2: check IAB code updates at https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main/java/com/example/android/trivialdrivesample/util
* @param activity
* @param sku
*/
protected boolean launchPurchaseWorkflow(Activity activity, String sku)
{
if (mIabIsInitialized)
{
try
{
mHelper.launchPurchaseFlow(
activity,
sku,
Constants.PURCHASE_REQUEST_ID++,// just needs to be a positive number and unique
mPurchaseFinishedListener,
Constants.DEVELOPER_PAYLOAD);
return true;//success
}
catch (IllegalStateException e)
{
mHelper.flagEndAsync();
return launchPurchaseWorkflow(activity, sku);//recursive call
}
}
else
{
return false;//failure - not initialized
}
}
My [BUY] button calls this launchPurchaseWorkflow()
and passes the SKU and the activity the button is in (or if you're in a fragment, the enclosing activity)
NOTE: be sure to make IabHelper.flagEndAsync()
public.
Hopefully, Google will improve this code in the near future; this problem is about 3 years old and it's still an ongoing problem :(
My solution is simple
1.) Make the mAsyncInProgress variable visible outside of IabHelper
public boolean isAsyncInProgress() {
return mAsyncInProgress;
}
2.) Use this in your Activity like:
...
if (mIabHelper.AsyncInProgress()) return;
mIabHelper.queryInventoryAsync(...);
...
A little-modified version of NadtheVlad's answer that works like charm
private void makePurchase() {
if (mHelper != null) {
try {
mHelper.launchPurchaseFlow(getActivity(), ITEM_SKU, 10001, mPurchaseFinishedListener, "");
}
catch(IllegalStateException ex){
mHelper.flagEndAsync();
makePurchase();
}
}
}
The logic is simple, just put the launchPurchaseFlow()
thing in a method and use recursion in the catch block. You still need to make flagEndAsync()
public from the IabHelper
class.
I have same issue, but it resolved! I think you must be not run "launchPurchaseFlow" on UI thread, try to run launchPurchaseFlow on UI thread, it would be working fine!
mActivity.runOnUiThread(new Runnable(){
public void run(){
mHelper.launchPurchaseFlow(mActivity, item, 10001, mPurchaseFinishedListener,username);
}
});
'Development Tip' 카테고리의 다른 글
Visual Studio 2012 설치 실패 : 프로그램 호환성 모드가 켜져 있습니다. (0) | 2020.11.02 |
---|---|
Prim과 Dijkstra의 알고리즘의 차이점은 무엇입니까? (0) | 2020.11.02 |
Outlook에서 줄 바꿈을 인쇄하도록 전자 메일의 문자열 서식을 어떻게 지정합니까? (0) | 2020.11.02 |
여러 버튼의 OnClickListener () android (0) | 2020.11.02 |
관계형 데이터베이스의 널은 괜찮습니까? (0) | 2020.11.02 |