Skip to content

Commit c0b9f68

Browse files
AppStore app: eliminate thread
1 parent 5936daf commit c0b9f68

File tree

3 files changed

+53
-47
lines changed

3 files changed

+53
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
0.5.2
22
=====
33
- AudioFlinger: optimize WAV volume scaling for speed and immediately set volume
4+
- API: add TaskManager that wraps asyncio
45

56
0.5.1
67
=====

internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import aiohttp
12
import lvgl as lv
23
import json
34
import requests
@@ -6,8 +7,10 @@
67
import time
78
import _thread
89

10+
911
from mpos.apps import Activity, Intent
1012
from mpos.app import App
13+
from mpos import TaskManager
1114
import mpos.ui
1215
from mpos.content.package_manager import PackageManager
1316

@@ -16,6 +19,7 @@ class AppStore(Activity):
1619
apps = []
1720
app_index_url = "https://apps.micropythonos.com/app_index.json"
1821
can_check_network = True
22+
aiohttp_session = None # one session for the whole app is more performant
1923

2024
# Widgets:
2125
main_screen = None
@@ -26,6 +30,7 @@ class AppStore(Activity):
2630
progress_bar = None
2731

2832
def onCreate(self):
33+
self.aiohttp_session = aiohttp.ClientSession()
2934
self.main_screen = lv.obj()
3035
self.please_wait_label = lv.label(self.main_screen)
3136
self.please_wait_label.set_text("Downloading app index...")
@@ -43,38 +48,39 @@ def onResume(self, screen):
4348
if self.can_check_network and not network.WLAN(network.STA_IF).isconnected():
4449
self.please_wait_label.set_text("Error: WiFi is not connected.")
4550
else:
46-
_thread.stack_size(mpos.apps.good_stack_size())
47-
_thread.start_new_thread(self.download_app_index, (self.app_index_url,))
51+
TaskManager.create_task(self.download_app_index(self.app_index_url))
52+
53+
def onDestroy(self, screen):
54+
await self.aiohttp_session.close()
4855

49-
def download_app_index(self, json_url):
56+
async def download_app_index(self, json_url):
57+
response = await self.download_url(json_url)
58+
if not response:
59+
self.please_wait_label.set_text(f"Could not download app index from\n{json_url}")
60+
return
61+
print(f"Got response text: {response[0:20]}")
5062
try:
51-
response = requests.get(json_url, timeout=10)
63+
for app in json.loads(response):
64+
try:
65+
self.apps.append(App(app["name"], app["publisher"], app["short_description"], app["long_description"], app["icon_url"], app["download_url"], app["fullname"], app["version"], app["category"], app["activities"]))
66+
except Exception as e:
67+
print(f"Warning: could not add app from {json_url} to apps list: {e}")
5268
except Exception as e:
53-
print("Download failed:", e)
54-
self.update_ui_threadsafe_if_foreground(self.please_wait_label.set_text, f"App index download \n{json_url}\ngot error: {e}")
69+
self.please_wait_label.set_text(f"ERROR: could not parse reponse.text JSON: {e}")
5570
return
56-
if response and response.status_code == 200:
57-
#print(f"Got response text: {response.text}")
58-
try:
59-
for app in json.loads(response.text):
60-
try:
61-
self.apps.append(App(app["name"], app["publisher"], app["short_description"], app["long_description"], app["icon_url"], app["download_url"], app["fullname"], app["version"], app["category"], app["activities"]))
62-
except Exception as e:
63-
print(f"Warning: could not add app from {json_url} to apps list: {e}")
64-
except Exception as e:
65-
print(f"ERROR: could not parse reponse.text JSON: {e}")
66-
finally:
67-
response.close()
68-
# Remove duplicates based on app.name
69-
seen = set()
70-
self.apps = [app for app in self.apps if not (app.fullname in seen or seen.add(app.fullname))]
71-
# Sort apps by app.name
72-
self.apps.sort(key=lambda x: x.name.lower()) # Use .lower() for case-insensitive sorting
73-
time.sleep_ms(200)
74-
self.update_ui_threadsafe_if_foreground(self.please_wait_label.add_flag, lv.obj.FLAG.HIDDEN)
75-
self.update_ui_threadsafe_if_foreground(self.create_apps_list)
76-
time.sleep(0.1) # give the UI time to display the app list before starting to download
77-
self.download_icons()
71+
print("Remove duplicates based on app.name")
72+
seen = set()
73+
self.apps = [app for app in self.apps if not (app.fullname in seen or seen.add(app.fullname))]
74+
print("Sort apps by app.name")
75+
self.apps.sort(key=lambda x: x.name.lower()) # Use .lower() for case-insensitive sorting
76+
print("Hiding please wait label...")
77+
self.update_ui_threadsafe_if_foreground(self.please_wait_label.add_flag, lv.obj.FLAG.HIDDEN)
78+
print("Creating apps list...")
79+
created_app_list_event = TaskManager.notify_event() # wait for the list to be shown before downloading the icons
80+
self.update_ui_threadsafe_if_foreground(self.create_apps_list, event=created_app_list_event)
81+
await created_app_list_event.wait()
82+
print("awaiting self.download_icons()")
83+
await self.download_icons()
7884

7985
def create_apps_list(self):
8086
print("create_apps_list")
@@ -119,14 +125,15 @@ def create_apps_list(self):
119125
desc_label.set_style_text_font(lv.font_montserrat_12, 0)
120126
desc_label.add_event_cb(lambda e, a=app: self.show_app_detail(a), lv.EVENT.CLICKED, None)
121127
print("create_apps_list app done")
122-
123-
def download_icons(self):
128+
129+
async def download_icons(self):
130+
print("Downloading icons...")
124131
for app in self.apps:
125132
if not self.has_foreground():
126133
print(f"App is stopping, aborting icon downloads.")
127134
break
128-
if not app.icon_data:
129-
app.icon_data = self.download_icon_data(app.icon_url)
135+
#if not app.icon_data:
136+
app.icon_data = await self.download_url(app.icon_url)
130137
if app.icon_data:
131138
print("download_icons has icon_data, showing it...")
132139
image_icon_widget = None
@@ -147,20 +154,16 @@ def show_app_detail(self, app):
147154
intent.putExtra("app", app)
148155
self.startActivity(intent)
149156

150-
@staticmethod
151-
def download_icon_data(url):
152-
print(f"Downloading icon from {url}")
157+
async def download_url(self, url):
158+
print(f"Downloading {url}")
159+
#await TaskManager.sleep(1)
153160
try:
154-
response = requests.get(url, timeout=5)
155-
if response.status_code == 200:
156-
image_data = response.content
157-
print("Downloaded image, size:", len(image_data), "bytes")
158-
return image_data
159-
else:
160-
print("Failed to download image: Status code", response.status_code)
161+
async with self.aiohttp_session.get(url) as response:
162+
if response.status >= 200 and response.status < 400:
163+
return await response.read()
164+
print(f"Done downloading {url}")
161165
except Exception as e:
162-
print(f"Exception during download of icon: {e}")
163-
return None
166+
print(f"download_url got exception {e}")
164167

165168
class AppDetail(Activity):
166169

internal_filesystem/lib/mpos/app/activity.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,12 @@ def task_handler_callback(self, a, b):
7373
self.throttle_async_call_counter = 0
7474

7575
# Execute a function if the Activity is in the foreground
76-
def if_foreground(self, func, *args, **kwargs):
76+
def if_foreground(self, func, *args, event=None, **kwargs):
7777
if self._has_foreground:
7878
#print(f"executing {func} with args {args} and kwargs {kwargs}")
7979
result = func(*args, **kwargs)
80+
if event:
81+
event.set()
8082
return result
8183
else:
8284
#print(f"[if_foreground] Skipped {func} because _has_foreground=False")
@@ -86,11 +88,11 @@ def if_foreground(self, func, *args, **kwargs):
8688
# The call may get throttled, unless important=True is added to it.
8789
# The order of these update_ui calls are not guaranteed, so a UI update might be overwritten by an "earlier" update.
8890
# To avoid this, use lv.timer_create() with .set_repeat_count(1) as examplified in osupdate.py
89-
def update_ui_threadsafe_if_foreground(self, func, *args, important=False, **kwargs):
91+
def update_ui_threadsafe_if_foreground(self, func, *args, important=False, event=None, **kwargs):
9092
self.throttle_async_call_counter += 1
9193
if not important and self.throttle_async_call_counter > 100: # 250 seems to be okay, so 100 is on the safe side
9294
print(f"update_ui_threadsafe_if_foreground called more than 100 times for one UI frame, which can overflow - throttling!")
9395
return None
9496
# lv.async_call() is needed to update the UI from another thread than the main one (as LVGL is not thread safe)
95-
result = lv.async_call(lambda _: self.if_foreground(func, *args, **kwargs),None)
97+
result = lv.async_call(lambda _: self.if_foreground(func, *args, event=event, **kwargs), None)
9698
return result

0 commit comments

Comments
 (0)