AndroidPIT-Lizensierung fehlerhaft?

  • Antworten:16
AlMiSoft
  • Forum-Beiträge: 55

30.07.2011, 00:17:56 via Website

Ich bin mir nicht sicher, ob ich etwas falsch implementiert habe, aber mein App stürzt manchmal an einer Stelle im Code der AndroidPIT-Lizensierung ab!

java.lang.IllegalArgumentException: Service not registered: Thread[Thread-48,5,main]
at android.app.LoadedApk.forgetServiceDispatcher(LoadedApk.java:934)
at android.app.ContextImpl.unbindService(ContextImpl.java:947)
at android.content.ContextWrapper.unbindService(ContextWrapper.java:352)
at de.androidpit.licensing.AndroidPitLicenseChecker$CheckLicenseThread.run(AndroidPitLicenseChecker.java:231)

Das dürfte m.E. nicht passieren.

Alexander Miehlke

Hier der Code, der die AndroidPIT-Lizensierung aufruft (im Grund alles Standard, nichts rebellisches):

LicenceCheckerCallbackAndroidPIT.java:

1public class AndroidPitLicenseCheckerCallback implements IAndroidPitLicenseCheckerCallback {
2
3 private Activity context;
4
5 public AndroidPitLicenseCheckerCallback(Activity context) {
6 super();
7 this.context = context;
8 }
9
10 public void allow() {
11 if (context.isFinishing()) return;
12 }
13
14 public void dontAllow() {
15 if (context.isFinishing()) return;
16
17 new AlertDialog.Builder(context)
18 .setTitle(R.string.app_name)
19 .setCancelable(false)
20 .setMessage(R.string.licenceerror)
21 .setNegativeButton(R.string.close,
22 new DialogInterface.OnClickListener() {
23 @Override
24 public void onClick(DialogInterface dialog, int which) {
25 context.finish();
26 }
27 }).create().show();
28 }
29
30 @Override
31 public void applicationError(AndroidPitLicenseCheckCode errorCode) {
32 if (context.isFinishing()) return;
33 }
34}

Und hier meine Main-Activity mit der Methode onCreate:

1android_id = Secure.getString(this.getContentResolver(), Secure.ANDROID_ID);
2mObsfuscator = new AESObfuscator(SALT, getPackageName(), android_id);
3licenseCheckerCallbackAndroidPIT = new LicenceCheckerCallbackAndroidPIT(this);
4new AndroidPitLicenseChecker(
5 this,
6 getPackageName(),
7 ANDROIDPIT_PUBLIC_KEY,
8 new ServerManagedPolicy(this, mObsfuscator),
9 GOOGLE_PUBLIC_KEY
10).checkAccess(licenseCheckerCallbackAndroidPIT);

— geändert am 31.07.2011, 12:22:06

Antworten
AlMiSoft
  • Forum-Beiträge: 55

31.07.2011, 01:54:31 via Website

Und da ist wohl noch ein Fehler, der sporadisch auftritt

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.app.Dialog.<init>(Dialog.java:101)
at android.app.AlertDialog.<init>(AlertDialog.java:63)
at android.app.AlertDialog.<init>(AlertDialog.java:59)
at android.app.AlertDialog$Builder.create(AlertDialog.java:786)
at de.almisoft.boxtogo.main.AndroidPitLicenseCheckerCallback.dontAllow(AndroidPitLicenseCheckerCallback.java:59)
at de.androidpit.licensing.AndroidPitLicenseChecker$CheckLicenseThread.checkLicense(AndroidPitLicenseChecker.java:315)
at de.androidpit.licensing.AndroidPitLicenseChecker$CheckLicenseThread.run(AndroidPitLicenseChecker.java:219)

Also stürzt meine App bei der Methode dontAllow() ab, in der ein Dialogfenster angezeigt werden soll (welches dann beim Klick auf OK die App beendet).

Alexander Miehlke

— geändert am 31.07.2011, 12:22:24

Antworten
Gelöschter Account
  • Forum-Beiträge: 5.136

31.07.2011, 02:16:56 via Website

Ich hab das jetzt mal an die Entwicklungsabteilung weitergeleitet Alexander, die Antwort wird aber evtl. bis Montag brauchen .. Weekend eben :)
Auch unsere Jungs brauchen mal Pause ..

Edit: Letztere Fehlermeldung ist allerdings hausgemacht wenn ich mir die Message ansehe .. irgendwo wird der Looper nicht aufgerufen.

— geändert am 31.07.2011, 02:20:29

lg Voss

Antworten
Gelöschter Account
  • Forum-Beiträge: 5.136

31.07.2011, 12:51:42 via Website

Nur mal eine Zwischenfrage? Tritt dieser Fehler mit dem "Service not registered" auf einem echten Device auf, oder auf dem Emulator?

lg Voss

Antworten
AlMiSoft
  • Forum-Beiträge: 55

31.07.2011, 12:55:25 via Website

Jörg V.
Nur mal eine Zwischenfrage? Tritt dieser Fehler mit dem "Service not registered" auf einem echten Device auf, oder auf dem Emulator?

Beide Fehler traten auf echten Geräten auf.
"Service not registered" war ein HTC Sensation mit Android 2.3.3

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

31.07.2011, 15:21:26 via Website

AlMiSoft
Und da ist wohl noch ein Fehler, der sporadisch auftritt

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

Das hatte ich auch schonmal und zwar wenn ich beim Testen die App sehr schnell beendet hab und die Lizenzprüfung etwas länger gedauert hat.
Dann feuert der Callback, wenn die Activity schon beendet wurde und die "zickt" dann natürlich :)
Das ist aber ein Fehlerfall auf den du garnicht mehr reagieren kannst, weil die App nicht mehr aktiv ist. Das einfachste ist es ihn einfach abzufangen, evtl. zu loggen und dann zu ignorieren, damit der User keine unerklärlichen Force-Closes bekommt.
Evtl. ist das auch das Problem bei der zuerst genannten Fehlermeldung.

Was mir noch aufgefallen ist:

1public void allow() {
2 if (context.isFinishing()) return;
3}

Das als einzige Anweisung in einer Methode ist überflüssig, denn es erfolgt sowieso ein impliziter return.
Die Methode kannst Du auch einfach leer lassen, wenn du da sonst nix machst.

— geändert am 31.07.2011, 15:22:28

Antworten
Gelöschter Account
  • Forum-Beiträge: 5.136

31.07.2011, 15:27:44 via Website

AlMiSoft
Jörg V.
Nur mal eine Zwischenfrage? Tritt dieser Fehler mit dem "Service not registered" auf einem echten Device auf, oder auf dem Emulator?

Beide Fehler traten auf echten Geräten auf.
"Service not registered" war ein HTC Sensation mit Android 2.3.3

Und das AppCenter war auf dem Gerät installiert?

lg Voss

Antworten
AlMiSoft
  • Forum-Beiträge: 55

31.07.2011, 17:13:45 via Website

Jörg V.

Und das AppCenter war auf dem Gerät installiert?
Das kann ich nicht sagen, es waren ein Kunden-Gerät.

Antworten
AlMiSoft
  • Forum-Beiträge: 55

31.07.2011, 17:17:45 via Website

Rafael K.
1. Das einfachste ist es ihn einfach abzufangen
2. Die Methode kannst Du auch einfach leer lassen, wenn du da sonst nix machst.
zu 1. Ich habe das inzwischen auch abgefangen, aber natürlich sollte dies der Autor tun.
zu 2. Im eigentlichen Quelltext passiert da noch was, was ich aber hier im Forum rausgelöscht habe...

Antworten
AlMiSoft
  • Forum-Beiträge: 55

31.07.2011, 17:52:07 via Website

Rafael K.
Der Autor kann das garnicht abfangen, denn der eigentliche Fehler wird durch die Logik deiner dontAllow Methode verursacht, die auf der bereits geschlossenen Activity Methoden aufruft.
Guck dir mal den StackTrace an.

Du beziehst dich doch auf den zweiten Fehler "Can't create handler inside thread that has not called Looper.prepare()", nicht wahr?
Ich hatte den Fehler auch einmal bei mir gesehen, da war meine App aber gerade am starten, die Activity war zu diesen Zeitpunkt vorhanden.

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

31.07.2011, 19:15:09 via Website

AlMiSoft
Rafael K.
Der Autor kann das garnicht abfangen, denn der eigentliche Fehler wird durch die Logik deiner dontAllow Methode verursacht, die auf der bereits geschlossenen Activity Methoden aufruft.
Guck dir mal den StackTrace an.

Du beziehst dich doch auf den zweiten Fehler "Can't create handler inside thread that has not called Looper.prepare()", nicht wahr?
Ich hatte den Fehler auch einmal bei mir gesehen, da war meine App aber gerade am starten, die Activity war zu diesen Zeitpunkt vorhanden.
Genau den meinte ich.
Kann es sein, dass du beim Testen deine App mehrfach gestartet und beendet hast?
Dann könnte der Fehler auch von einer toten Instanz gekommen sein, deren Lizenzprüfung dann irgendwann fehlschlug.

Wie gesagt, ich kenne den Fehler nur von beendeten Instanzen.

Ansonsten einfach mal nach der Fehlermeldung googlen und den Text in Anführungszeichen setzen.
Da findet man fast immer Leute, die den gleichen Fehler auch schon hatten und oft auch eine Lösung.

— geändert am 31.07.2011, 19:15:30

Antworten
Sven Woltmann
  • Admin
  • Staff
  • Forum-Beiträge: 1.922

01.08.2011, 11:19:23 via Website

Hallo zusammen,

ich habe hier eine neue Version der Library hochgeladen, die ein paar Probleme behebt:
http://files.androidpit.info/pub/androidpit-licensing-lib.zip

  • Wenn der übergebene Context keine Activity ist, werden Exceptions beim Versuch den Login-Dialog anzuzeigen (was nur auf einer Activity möglich ist, nicht auf sonstigen Contexts) abgefangen und ein ERROR_NOT_AUTHENTICATED an den Listener geschickt.
  • Fehler "ERROR_COMMUNICATING_WITH_APP_CENTER" durch exaktere Meldungen ersetzt.
  • BadTokenExceptions werden abgefangen und ein ERROR_NOT_AUTHENTICATED an den Callback-Listener gesendet.
  • Neue Methode AndroidPitLicenseChecker.setAutoDestroyAfterCheck(). Wenn diese Methode mit "true" aufgerufen wird, wird nach dem Aufruf des Callback-Listeners automatisch die onDestroy-Methode des LicenseCheckers aufgerufen und damit mContext (sowie weitere Members) auf null gesetzt. Eine evtl. verbleibende Referenz auf den Checker dürfte dann nur noch ein paar Byte belegen.
Bitte damit mal testen.

Viele Grüße,
Sven

Svens Java-Entwickler-Blog: https://www.happycoders.eu

Antworten
AlMiSoft
  • Forum-Beiträge: 55

01.08.2011, 17:05:10 via Website

Hallo Sven,

vielen Dank für die schnelle Reaktion.
Werden damit beide Probleme behoben?

Alexander Miehlke

Antworten
Stefan Hansel Solutions UG (haftungsbeschränkt)
  • Forum-Beiträge: 3

05.08.2011, 22:23:57 via Website

Hab mir gerade mal den neuen Code angeschaut - und an der Stelle wo die Exception #1 auftritt (die übrigens auch in meinen App Logs die häufigste Exception ist :( )
------------
java.lang.IllegalArgumentException: Service not registered: Thread[Thread-1092,5,main]
at android.app.ActivityThread$PackageInfo.forgetServiceDispatcher(ActivityThread.java:1074)
at android.app.ContextImpl.unbindService(ContextImpl.java:895)
at android.content.ContextWrapper.unbindService(ContextWrapper.java:352)
at de.androidpit.licensing.AndroidPitLicenseChecker$CheckLicenseThread.run(SourceFile:231)
-------------
glaube ich wird es eher schlimmer.

Durch die 'finished' Variable wird nun 'unbindService()' z.B. so gut wie immer aufgerufen - auch wenn weiter oben 'bindService' false geliefert hat, weil AndroidPit auf dem Gerät gar nicht installiert ist.

Andersrum kann checkLicense() false liefern - und dann wird auf einmal für den Service nicht mehr unbind() aufgerufen.

Antworten
Stefan Hansel Solutions UG (haftungsbeschränkt)
  • Forum-Beiträge: 3

05.08.2011, 22:47:32 via Website

Mehr Infos aus den Crashlogs.
Passiert mit verschiedenen Android Version von 2.2.1 über 2.3.4 bis 3.1.
Auch verschiedene Geräte betroffen: HTC Desire HD, Samung und Sony Ericson treten auf.

In höheren Android Versionen ändert sich der Stacktrace leicht
-----
java.lang.IllegalArgumentException: Service not registered: Thread[Thread-15,5,main]
at android.app.LoadedApk.forgetServiceDispatcher(LoadedApk.java:925)
at android.app.ContextImpl.unbindService(ContextImpl.java:943)
at android.content.ContextWrapper.unbindService(ContextWrapper.java:352)
at de.androidpit.licensing.AndroidPitLicenseChecker$CheckLicenseThread.run(SourceFile:231)
-----

Antworten
Stefan Hansel Solutions UG (haftungsbeschränkt)
  • Forum-Beiträge: 3

05.08.2011, 23:50:32 via Website

Ich kann das Problem jetzt nachstellen.

Im Grunde gibt es ein Timingproblem dadurch, da ihr einen extra CheckLicenseThread für den Lizenzcheck verwendet.
Während dieser CheckLicenseThread läuft, kann die Hauptanwendung (inkl. Context) schon wieder parallel geschlossen werden.
Wenn der Context zugemacht wird, wird das Service-Binding auch automatisch mitentfernt.

Der LizenzThread ist dann irgendwann später halt auch fertig und läuft nochmal selbst über 'unbindService', was dann besagte Exception schmeißt.

Zum Nachstellen einfach einen Breakpoint auf die 'unbindService()' Zeile setzen.
- App starten,
- den Debugger im Breakpoint stehen lassen
- App schließen (onDestroy wird aufgerufen)
- Debugger weiterlaufen lassen, so dass das unbind dann durchgeführt wird => Exception, weil Context+Binding schon längst weg ist.

Lösungsvorschlag: genauso machen, wie es in der Google Doku im SampleCode vorgegeben ist (http://developer.android.com/reference/android/app/Service.html).

Dort wird über eine Variable gemerkt, ob der Service gebunden ist.
Unbind kann über zwei Wege gehen: entweder explizit durch Aufruf von doUnbindService() ... oder indirekt im 'onDestroy', was die Variable auch zurücksetzt.

Persönlich würde ich auch vorschlagen, den bind/unbindService aus dem LizenzcheckerThread rauszulassen und im Hauptthread durchzuführen. Dass die App dauerhaft mit dem AndroidPit-Lizenz-Service verbunden ist, sollte eigentlich keine Probleme machen.
Falls der unbindService im CheckLicenseThread bleibt, wird der Code böse häßlich, wenn ihr den Aufruf von 'onDestroy()' und den expliziten unbindService() im extra CheckLicenseThread synchronisieren müsst. Da sieht dann in drei Monaten keiner mehr durch.

Daniele Pecora

Antworten