Skip to content

Commit d06eae9

Browse files
authored
Update broadcast.py
To avoid crash on_pause() on_resume() if we do call start() and stop() there jnius.jnius.JavaException: JVM exception occurred: java.lang.IllegalThreadStateException
1 parent be3de2e commit d06eae9

File tree

1 file changed

+76
-40
lines changed

1 file changed

+76
-40
lines changed

pythonforandroid/recipes/android/src/android/broadcast.py

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,108 @@
11
# -------------------------------------------------------------------
2-
# Broadcast receiver bridge
2+
# Broadcast receiver bridge for Kivy + Pyjnius
3+
# Safe start/stop with Android lifecycle integration
4+
# -------------------------------------------------------------------
35

46
from jnius import autoclass, PythonJavaClass, java_method
57
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE, ACTIVITY_CLASS_NAME, SERVICE_CLASS_NAME
68

79

8-
class BroadcastReceiver(object):
9-
10+
class BroadcastReceiver:
1011
class Callback(PythonJavaClass):
1112
__javainterfaces__ = [JNI_NAMESPACE + '/GenericBroadcastReceiverCallback']
1213
__javacontext__ = 'app'
1314

1415
def __init__(self, callback, *args, **kwargs):
1516
self.callback = callback
16-
PythonJavaClass.__init__(self, *args, **kwargs)
17+
super().__init__(*args, **kwargs)
1718

1819
@java_method('(Landroid/content/Context;Landroid/content/Intent;)V')
1920
def onReceive(self, context, intent):
2021
self.callback(context, intent)
2122

2223
def __init__(self, callback, actions=None, categories=None):
23-
super().__init__()
24-
self.callback = callback
25-
2624
if not actions and not categories:
2725
raise Exception('You need to define at least actions or categories')
2826

29-
def _expand_partial_name(partial_name):
30-
if '.' in partial_name:
31-
return partial_name # Its actually a full dotted name
32-
else:
33-
name = 'ACTION_{}'.format(partial_name.upper())
34-
if not hasattr(Intent, name):
35-
raise Exception('The intent {} does not exist'.format(name))
36-
return getattr(Intent, name)
27+
self.callback = callback
28+
self._is_registered = False
29+
self._handlerthread_started = False
3730

38-
# resolve actions/categories first
31+
# Expand intent names
3932
Intent = autoclass('android.content.Intent')
40-
resolved_actions = [_expand_partial_name(x) for x in actions or []]
41-
resolved_categories = [_expand_partial_name(x) for x in categories or []]
42-
43-
# resolve android API
44-
GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver')
45-
IntentFilter = autoclass('android.content.IntentFilter')
46-
HandlerThread = autoclass('android.os.HandlerThread')
4733

48-
# create a thread for handling events from the receiver
49-
self.handlerthread = HandlerThread('handlerthread')
50-
51-
# create a listener
34+
def _expand(partial):
35+
if '.' in partial:
36+
return partial
37+
name = 'ACTION_' + partial.upper()
38+
if not hasattr(Intent, name):
39+
raise Exception(f'Intent {name} does not exist')
40+
return getattr(Intent, name)
41+
42+
self.resolved_actions = [_expand(a) for a in actions or []]
43+
self.resolved_categories = [_expand(c) for c in categories or []]
44+
45+
# Java classes
46+
self.GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver')
47+
self.IntentFilter = autoclass('android.content.IntentFilter')
48+
self.Handler = autoclass('android.os.Handler')
49+
self.HandlerThreadClass = autoclass('android.os.HandlerThread')
50+
51+
# Build filter
52+
self.receiver_filter = self.IntentFilter()
53+
for action in self.resolved_actions:
54+
self.receiver_filter.addAction(action)
55+
for category in self.resolved_categories:
56+
self.receiver_filter.addCategory(category)
57+
58+
# Receiver and callback
5259
self.listener = BroadcastReceiver.Callback(self.callback)
53-
self.receiver = GenericBroadcastReceiver(self.listener)
54-
self.receiver_filter = IntentFilter()
55-
for x in resolved_actions:
56-
self.receiver_filter.addAction(x)
57-
for x in resolved_categories:
58-
self.receiver_filter.addCategory(x)
60+
self.receiver = self.GenericBroadcastReceiver(self.listener)
61+
62+
# Init thread placeholder
63+
self.handlerthread = None
64+
self.handler = None
5965

6066
def start(self):
61-
Handler = autoclass('android.os.Handler')
62-
self.handlerthread.start()
63-
self.handler = Handler(self.handlerthread.getLooper())
64-
self.context.registerReceiver(
65-
self.receiver, self.receiver_filter, None, self.handler)
67+
if self._is_registered:
68+
print("[BroadcastReceiver] Already registered.")
69+
return
70+
71+
if not self._handlerthread_started:
72+
self.handlerthread = self.HandlerThreadClass('BroadcastReceiverThread')
73+
try:
74+
self.handlerthread.start()
75+
self._handlerthread_started = True
76+
except Exception as e:
77+
print(f"[BroadcastReceiver] HandlerThread start failed: {e}")
78+
return
79+
80+
try:
81+
self.handler = self.Handler(self.handlerthread.getLooper())
82+
self.context.registerReceiver(self.receiver, self.receiver_filter, None, self.handler)
83+
self._is_registered = True
84+
print("[BroadcastReceiver] Registered.")
85+
except Exception as e:
86+
print(f"[BroadcastReceiver] registerReceiver failed: {e}")
6687

6788
def stop(self):
68-
self.context.unregisterReceiver(self.receiver)
69-
self.handlerthread.quit()
89+
if self._is_registered:
90+
try:
91+
self.context.unregisterReceiver(self.receiver)
92+
print("[BroadcastReceiver] Unregistered.")
93+
except Exception as e:
94+
print(f"[BroadcastReceiver] unregisterReceiver failed: {e}")
95+
self._is_registered = False
96+
97+
if self._handlerthread_started:
98+
try:
99+
self.handlerthread.quitSafely()
100+
print("[BroadcastReceiver] HandlerThread quit safely.")
101+
except Exception as e:
102+
print(f"[BroadcastReceiver] thread quit failed: {e}")
103+
self._handlerthread_started = False
104+
self.handlerthread = None
105+
self.handler = None
70106

71107
@property
72108
def context(self):

0 commit comments

Comments
 (0)