-
Notifications
You must be signed in to change notification settings - Fork 1.9k
WIP: workmanager and bound services support #2464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 29 commits
e45a154
cb244b5
015ed03
6a81bfb
70dc848
b6ceca9
681cfeb
e452095
efc150c
146c781
c0c50de
6e2b736
5b68bdc
4687ed1
7f9c00d
e394f38
ecea202
09ab20d
8f2d569
19c00dd
430a7e7
da130e8
6283064
a012719
0a97b5b
35ed8b1
ea36543
f6fc1db
12a0f37
6ec1ac2
2d20e16
0195537
5b6f5aa
550a2ce
20221aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,7 +71,7 @@ int file_exists(const char *filename) { | |
} | ||
|
||
/* int main(int argc, char **argv) { */ | ||
int main(int argc, char *argv[]) { | ||
int main_(int argc, char *argv[], int call_exit) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happened to the main? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. main only gets called from within the JNICALL functions, and my editor complained about the signature. so i renamed it, maybe start_python or so woud better fit. |
||
|
||
char *env_argument = NULL; | ||
char *env_entrypoint = NULL; | ||
|
@@ -333,12 +333,14 @@ int main(int argc, char *argv[]) { | |
|
||
https://github.com/kivy/kivy/pull/6107#issue-246120816 | ||
*/ | ||
char terminatecmd[256]; | ||
snprintf( | ||
terminatecmd, sizeof(terminatecmd), | ||
"import sys; sys.exit(%d)\n", ret | ||
); | ||
PyRun_SimpleString(terminatecmd); | ||
if (call_exit) { | ||
char terminatecmd[256]; | ||
snprintf( | ||
terminatecmd, sizeof(terminatecmd), | ||
"import sys; sys.exit(%d)\n", ret | ||
); | ||
PyRun_SimpleString(terminatecmd); | ||
} | ||
|
||
/* This should never actually be reached, but we'll leave the clean-up | ||
* here just to be safe. | ||
|
@@ -356,7 +358,91 @@ int main(int argc, char *argv[]) { | |
return ret; | ||
} | ||
|
||
JNIEXPORT void JNICALL Java_org_kivy_android_PythonService_nativeStart( | ||
JNIEXPORT int JNICALL Java_org_kivy_android_PythonService_nativeStart( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only thing that changes between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
JNIEnv *env, | ||
jobject thiz, | ||
jstring j_android_private, | ||
jstring j_android_argument, | ||
jstring j_service_entrypoint, | ||
jstring j_python_name, | ||
jstring j_python_home, | ||
jstring j_python_path, | ||
jstring j_arg) { | ||
jboolean iscopy; | ||
const char *android_private = | ||
(*env)->GetStringUTFChars(env, j_android_private, &iscopy); | ||
const char *android_argument = | ||
(*env)->GetStringUTFChars(env, j_android_argument, &iscopy); | ||
const char *service_entrypoint = | ||
(*env)->GetStringUTFChars(env, j_service_entrypoint, &iscopy); | ||
const char *python_name = | ||
(*env)->GetStringUTFChars(env, j_python_name, &iscopy); | ||
const char *python_home = | ||
(*env)->GetStringUTFChars(env, j_python_home, &iscopy); | ||
const char *python_path = | ||
(*env)->GetStringUTFChars(env, j_python_path, &iscopy); | ||
const char *arg = (*env)->GetStringUTFChars(env, j_arg, &iscopy); | ||
|
||
setenv("ANDROID_PRIVATE", android_private, 1); | ||
setenv("ANDROID_ARGUMENT", android_argument, 1); | ||
setenv("ANDROID_APP_PATH", android_argument, 1); | ||
setenv("ANDROID_ENTRYPOINT", service_entrypoint, 1); | ||
setenv("PYTHONOPTIMIZE", "2", 1); | ||
setenv("PYTHON_NAME", python_name, 1); | ||
setenv("PYTHONHOME", python_home, 1); | ||
setenv("PYTHONPATH", python_path, 1); | ||
setenv("PYTHON_SERVICE_ARGUMENT", arg, 1); | ||
setenv("P4A_BOOTSTRAP", bootstrap_name, 1); | ||
|
||
char *argv[] = {"."}; | ||
/* ANDROID_ARGUMENT points to service subdir, | ||
* so main() will run main.py from this dir | ||
*/ | ||
return main_(1, argv, 1); | ||
} | ||
|
||
#if defined(BOOTSTRAP_NAME_SERVICELIBRARY) | ||
// JNIEXPORT void JNICALL Java_org_kivy_android_PythonWorker_nativeStart( | ||
// JNIEnv *env, | ||
// jobject thiz, | ||
// jstring j_android_private, | ||
// jstring j_android_argument, | ||
// jstring j_worker_entrypoint, | ||
// jstring j_python_name, | ||
// jstring j_python_home, | ||
// jstring j_python_path) { | ||
// jboolean iscopy; | ||
// const char *android_private = | ||
// (*env)->GetStringUTFChars(env, j_android_private, &iscopy); | ||
// const char *android_argument = | ||
// (*env)->GetStringUTFChars(env, j_android_argument, &iscopy); | ||
// const char *worker_entrypoint = | ||
// (*env)->GetStringUTFChars(env, j_worker_entrypoint, &iscopy); | ||
// const char *python_name = | ||
// (*env)->GetStringUTFChars(env, j_python_name, &iscopy); | ||
// const char *python_home = | ||
// (*env)->GetStringUTFChars(env, j_python_home, &iscopy); | ||
// const char *python_path = | ||
// (*env)->GetStringUTFChars(env, j_python_path, &iscopy); | ||
|
||
// setenv("ANDROID_PRIVATE", android_private, 1); | ||
// setenv("ANDROID_ARGUMENT", android_argument, 1); | ||
// setenv("ANDROID_APP_PATH", android_argument, 1); | ||
// setenv("ANDROID_ENTRYPOINT", worker_entrypoint, 1); | ||
// setenv("PYTHONOPTIMIZE", "2", 1); | ||
// setenv("PYTHON_NAME", python_name, 1); | ||
// setenv("PYTHONHOME", python_home, 1); | ||
// setenv("PYTHONPATH", python_path, 1); | ||
// setenv("P4A_BOOTSTRAP", bootstrap_name, 1); | ||
|
||
// char *argv[] = {"."}; | ||
// /* ANDROID_ARGUMENT points to service subdir, | ||
// * so main() will run main.py from this dir | ||
// */ | ||
// main_(1, argv, 0); | ||
// } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably because it's still a WIP pull request, but I would not keep any commented out code in the final pull request There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, i played around with running the python thread within the calling process (which is not the case for services). As long as no concurrent python gets started this works fine, but crashes otherwise. So this approach cannot be used when starting workers from e.g. kivy. It is required to use RemoteWorkerService/RemoteWorker in this case, which again uses a dedicated process. The commented function is a relict and will be removed during cleanup. |
||
|
||
JNIEXPORT int JNICALL Java_org_kivy_android_PythonWorker_nativeStart( | ||
JNIEnv *env, | ||
jobject thiz, | ||
jstring j_android_private, | ||
|
@@ -396,9 +482,54 @@ JNIEXPORT void JNICALL Java_org_kivy_android_PythonService_nativeStart( | |
/* ANDROID_ARGUMENT points to service subdir, | ||
* so main() will run main.py from this dir | ||
*/ | ||
main(1, argv); | ||
// main_(1, argv, 1); | ||
return main_(1, argv, 0); | ||
} | ||
|
||
JNIEXPORT int JNICALL Java_org_kivy_android_PythonBoundService_nativeStart( | ||
JNIEnv *env, | ||
jobject thiz, | ||
jstring j_android_private, | ||
jstring j_android_argument, | ||
jstring j_service_entrypoint, | ||
jstring j_python_name, | ||
jstring j_python_home, | ||
jstring j_python_path, | ||
jstring j_arg) { | ||
jboolean iscopy; | ||
const char *android_private = | ||
(*env)->GetStringUTFChars(env, j_android_private, &iscopy); | ||
const char *android_argument = | ||
(*env)->GetStringUTFChars(env, j_android_argument, &iscopy); | ||
const char *service_entrypoint = | ||
(*env)->GetStringUTFChars(env, j_service_entrypoint, &iscopy); | ||
const char *python_name = | ||
(*env)->GetStringUTFChars(env, j_python_name, &iscopy); | ||
const char *python_home = | ||
(*env)->GetStringUTFChars(env, j_python_home, &iscopy); | ||
const char *python_path = | ||
(*env)->GetStringUTFChars(env, j_python_path, &iscopy); | ||
const char *arg = (*env)->GetStringUTFChars(env, j_arg, &iscopy); | ||
|
||
setenv("ANDROID_PRIVATE", android_private, 1); | ||
setenv("ANDROID_ARGUMENT", android_argument, 1); | ||
setenv("ANDROID_APP_PATH", android_argument, 1); | ||
setenv("ANDROID_ENTRYPOINT", service_entrypoint, 1); | ||
setenv("PYTHONOPTIMIZE", "2", 1); | ||
setenv("PYTHON_NAME", python_name, 1); | ||
setenv("PYTHONHOME", python_home, 1); | ||
setenv("PYTHONPATH", python_path, 1); | ||
setenv("PYTHON_SERVICE_ARGUMENT", arg, 1); | ||
setenv("P4A_BOOTSTRAP", bootstrap_name, 1); | ||
|
||
char *argv[] = {"."}; | ||
/* ANDROID_ARGUMENT points to service subdir, | ||
* so main() will run main.py from this dir | ||
*/ | ||
return main_(1, argv, 1); | ||
} | ||
#endif | ||
|
||
#if defined(BOOTSTRAP_NAME_WEBVIEW) || defined(BOOTSTRAP_NAME_SERVICEONLY) | ||
// Webview and service_only uses some more functions: | ||
|
||
|
@@ -419,7 +550,7 @@ void Java_org_kivy_android_PythonActivity_nativeSetenv( | |
} | ||
|
||
|
||
void Java_org_kivy_android_PythonActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) | ||
int Java_org_kivy_android_PythonActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) | ||
{ | ||
/* This nativeInit follows SDL2 */ | ||
|
||
|
@@ -435,7 +566,7 @@ void Java_org_kivy_android_PythonActivity_nativeInit(JNIEnv* env, jclass cls, jo | |
argv[1] = NULL; | ||
/* status = SDL_main(1, argv); */ | ||
|
||
main(1, argv); | ||
return main_(1, argv, 1); | ||
|
||
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */ | ||
/* exit(status); */ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package org.kivy.android; | ||
|
||
import android.app.Service; | ||
import android.content.Context; | ||
import android.os.Process; | ||
import android.util.Log; | ||
|
||
import java.io.File; | ||
|
||
import org.kivy.android.PythonUtil; | ||
|
||
|
||
public abstract class PythonBoundService extends Service implements Runnable { | ||
private static final String TAG = "python bound service"; | ||
|
||
// Thread for Python code | ||
private Thread pythonThread = null; | ||
|
||
// Application root directory | ||
private String appRoot; | ||
|
||
// Python environment variables | ||
private String androidPrivate; | ||
private String androidArgument; | ||
private String pythonName; | ||
private String pythonHome; | ||
private String pythonPath; | ||
private String workerEntrypoint; | ||
|
||
// Argument to pass to Python code, | ||
private String pythonServiceArgument; | ||
|
||
public void setPythonName(String value) { | ||
Log.d(TAG, "setPythonName()"); | ||
|
||
pythonName = value; | ||
} | ||
|
||
public void setWorkerEntrypoint(String value) { | ||
Log.d(TAG, "setWorkerEntrypoint()"); | ||
|
||
workerEntrypoint = value; | ||
} | ||
|
||
public void startPythonThread() { | ||
Log.d(TAG, "startPythonThread()"); | ||
|
||
pythonThread = new Thread(this); | ||
pythonThread.start(); | ||
} | ||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
|
||
Log.d(TAG, "onCreate()"); | ||
|
||
Context context = getApplicationContext(); | ||
appRoot = PythonUtil.getAppRoot(context); | ||
|
||
androidPrivate = appRoot; | ||
androidArgument = appRoot; | ||
pythonHome = appRoot; | ||
pythonPath = appRoot + ":" + appRoot + "/lib"; | ||
|
||
pythonServiceArgument = ""; | ||
|
||
File appRootFile = new File(appRoot); | ||
|
||
Log.d(TAG, "unpack Data"); | ||
|
||
PythonUtil.unpackData(context, "private", appRootFile, false); | ||
|
||
Log.d(TAG, "data unpacked"); | ||
} | ||
|
||
@Override | ||
public void onDestroy() { | ||
super.onDestroy(); | ||
|
||
Log.d(TAG, "onDestroy()"); | ||
|
||
pythonThread = null; | ||
Process.killProcess(Process.myPid()); | ||
} | ||
|
||
@Override | ||
public void run() { | ||
Log.d(TAG, "run()"); | ||
|
||
File appRootFile = new File(appRoot); | ||
|
||
PythonUtil.loadLibraries( | ||
appRootFile, | ||
new File(getApplicationContext().getApplicationInfo().nativeLibraryDir) | ||
); | ||
|
||
Log.d(TAG, "Call native start"); | ||
|
||
nativeStart( | ||
androidPrivate, androidArgument, | ||
workerEntrypoint, pythonName, | ||
pythonHome, pythonPath, | ||
pythonServiceArgument | ||
); | ||
|
||
Log.d(TAG, "Python thread terminating"); | ||
} | ||
|
||
// Native part | ||
public static native void nativeStart( | ||
String androidPrivate, String androidArgument, | ||
String workerEntrypoint, String pythonName, | ||
String pythonHome, String pythonPath, | ||
String pythonServiceArgument | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: we could make a one-liner helper function/method that does that, so we have a single source of truth.