Skip to content

Commit b41a9c2

Browse files
dbnicholsonrtibbles
authored andcommitted
Add documentation section on workers
1 parent 4356950 commit b41a9c2

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

doc/source/buildoptions.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ options (this list may not be exhaustive):
108108
included in AndroidManifest.xml.
109109
- ``--service``: A service name and the Python script it should
110110
run. See :ref:`arbitrary_scripts_services`.
111+
- ``--worker``: A worker name and the Python script it should run. See
112+
:ref:`workers` for details.
111113
- ``--add-source``: Add a source directory to the app's Java code.
112114
- ``--no-byte-compile-python``: Skip byte compile for .py files.
113115
- ``--enable-androidx``: Enable AndroidX support library.
@@ -174,6 +176,8 @@ ready.
174176
included in AndroidManifest.xml.
175177
- ``--service``: A service name and the Python script it should
176178
run. See :ref:`arbitrary_scripts_services`.
179+
- ``--worker``: A worker name and the Python script it should run. See
180+
:ref:`workers` for details.
177181
- ``add-source``: Add a source directory to the app's Java code.
178182
- ``--port``: The port on localhost that the WebView will
179183
access. Defaults to 5000.
@@ -195,6 +199,8 @@ systems and frameworks.
195199
- ``--version``: The version number.
196200
- ``--service``: A service name and the Python script it should
197201
run. See :ref:`arbitrary_scripts_services`.
202+
- ``--worker``: A worker name and the Python script it should run. See
203+
:ref:`workers` for details.
198204
- ``--blacklist``: The path to a file containing blacklisted patterns
199205
that will be excluded from the final AAR. Defaults to ``./blacklist.txt``.
200206
- ``--whitelist``: The path to a file containing whitelisted patterns

doc/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Contents
3535
recipes
3636
bootstraps
3737
services
38+
workers
3839
troubleshooting
3940
docker
4041
contribute

doc/source/workers.rst

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
Workers
2+
=======
3+
4+
python-for-android supports worker tasks using `WorkManager
5+
<https://developer.android.com/topic/libraries/architecture/workmanager>`_.
6+
``WorkManager`` tasks are the recommended way to perform both one-time
7+
and recurring work with current Android. Starting with Android 12,
8+
worker tasks will also be required to replace foreground services in
9+
some cases.
10+
11+
Each worker runs tasks in a service declared in the
12+
``AndroidManifest.xml`` file. This is managed by python-for-android with
13+
a service generated from the application package. The worker service is
14+
specified to run in a separate process since python-for-android does not
15+
support running multiple Python interpreters in the same process.
16+
17+
Worker creation
18+
---------------
19+
20+
To create the worker, create a python script with your worker code and
21+
add a ``--worker=myworker:PATH_TO_WORKER_PY`` argument when calling
22+
python-for-android.
23+
24+
The ``myworker`` name before the colon is in the names of the worker and
25+
worker service classes, via which you will interact with it later.
26+
27+
The ``PATH_TO_WORKER_PY`` is the relative path to the worker entry point
28+
(like ``workers/myworker.py``)
29+
30+
You can add multiple ``--worker`` arguments to include multiple workers,
31+
all of which you will later be able to stop and start from your app.
32+
33+
Running workers
34+
---------------
35+
36+
To run the workers (i.e. starting them from within your main app code),
37+
you must use PyJNIus to interact with the Java class python-for-android
38+
creates for each one. First, you need to create a work request using the
39+
``buildInputData`` helper function which configures the work to run in
40+
the appropriate service class::
41+
42+
from jnius import autoclass
43+
44+
worker = autoclass('your.package.domain.package.name.MyworkerWorker')
45+
OneTimeWorkRequestBuilder = autoclass('androidx.work.OneTimeWorkRequest$Builder')
46+
argument = ''
47+
data = worker.buildInputData(argument)
48+
request = OneTimeWorkRequestBuilder(worker._class).setInputData(data).build()
49+
50+
Here, ``your.package.domain.package.name`` refers to the package
51+
identifier of your APK. The identifier is set by the ``--package``
52+
argument to python-for-android. The name of the worker is
53+
``MyworkerWorker``, where ``Myworker`` is the identifier that was
54+
previously passed to the ``--worker`` argument, but with the first
55+
letter upper case. You must also pass the ``argument`` parameter even if
56+
(as here) it is an empty string or `None`. If you do pass it, the
57+
service can make use of this argument.
58+
59+
The argument is made available to your worker via the
60+
'PYTHON_SERVICE_ARGUMENT' environment variable. It is exposed as a
61+
simple string, so if you want to pass in multiple values, we would
62+
recommend using the json module to encode and decode more complex data.
63+
::
64+
65+
from os import environ
66+
argument = environ.get('PYTHON_SERVICE_ARGUMENT', '')
67+
68+
Now the work request needs to be enqueued in the application's
69+
`WorkManager
70+
<https://developer.android.com/reference/androidx/work/WorkManager>`_
71+
instance::
72+
73+
mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
74+
WorkManager = autoclass('androidx.work.WorkManager')
75+
work_manager = WorkManager.getInstance(mActivity)
76+
work_manager.enqueue(request)
77+
78+
Enqueuing a work request is asynchronous and returns an `Operation
79+
<https://developer.android.com/reference/androidx/work/Operation>`_. To
80+
block until the request has been queued, wait for the state to resolve::
81+
82+
operation = work_manager.enqueue(request)
83+
operation.getResult().get()
84+
85+
Once the work request has been queued, information about the request
86+
such as its current state can be requested from ``WorkManager``::
87+
88+
request_id = request.getId()
89+
work_info = work_manager.getWorkInfoById(request_id).get()
90+
state = work_info.getState()
91+
print('Work request state:', state.toString())
92+
if state.isFinished():
93+
print('Work request has completed')
94+
95+
.. note::
96+
97+
The app root directory for Python imports will be in the app root
98+
folder even if the worker file is in a subfolder. If the worker is
99+
in the ``worker/`` folder, it must be imported with ``import
100+
worker.module`` rather than ``import module``.
101+
102+
Worker progress
103+
~~~~~~~~~~~~~~~
104+
105+
A worker can send intermediate progress data for the work request that
106+
can be retrieved in the activity. From the worker script, use the
107+
``setProgressAsync`` method from the worker class instance::
108+
109+
from jnius import autoclass
110+
111+
mWorker = autoclass('your.package.domain.package.name.MyworkerWorker').mWorker
112+
DataBuilder = autoclass('androidx.work.Data$Builder')
113+
114+
data = DataBuilder().putInt('PROGRESS', 50).build()
115+
mWorker.setProgressAsync(data)
116+
117+
The progress can be retrieved in the activity from the work request
118+
information::
119+
120+
request_id = request.getId()
121+
work_info = work_manager.getWorkInfoById(request_id).get()
122+
progress = work_info.getProgress().getInt('PROGRESS', 0)
123+
print('Work request {}% complete'.format(progress))
124+
125+
.. note::
126+
127+
At present, there is no method to return output data for the work
128+
request. The work is either succeeded or failed based on the exit
129+
status of the worker script.

0 commit comments

Comments
 (0)