pywebview icon indicating copy to clipboard operation
pywebview copied to clipboard

Android support [Decommission Kivy, Reduce startup time]

Open kengoon opened this issue 7 months ago • 28 comments

kengoon avatar May 25 '25 08:05 kengoon

@r0x0r can you help me with the conflicts here. My brain is currently off. (git is one of my weakness). Also I would like to know why you are using Semaphore here and do you know it's thread blocking. We can use a callback here instead. What do you think?

def evaluate_js(js_code, _, parse_json=True):
    def callback(event, result):
        nonlocal js_result
        js_result = json.loads(result) if parse_json and result else result
        lock.release()

    def _evaluate_js():
        callback_wrapper = EventCallbackWrapper(callback)
        value_callback = JavascriptValueCallback()
        value_callback.setCallback(callback_wrapper)
        app.view.webview.evaluateJavascript(js_code, value_callback)

    lock = Semaphore(0)
    js_result = None

    Runnable(_evaluate_js)()
    lock.acquire()
    return js_result
    ```

kengoon avatar May 25 '25 16:05 kengoon

@kengoon. Done. The function of semaphore is to turn an async function into a sync one. window.evaluate_js is a synchronous function, so after Runnable(_evaluate_js)() it needs to lock before returning a result. It is thread blocking, but evaluate_js and other window functions are designed to be run on secondary threads

r0x0r avatar May 25 '25 17:05 r0x0r

@r0x0r seems the conflict is still there image

Also I've not worked with evaluateJavascripts method before. Do you know how long it takes for the result to return? I'm looking at making the UI as smooth as possible to avoid any freezes.

kengoon avatar May 26 '25 06:05 kengoon

The execution time of evaluateJavascript depends on the code you execute. If you are concerned with performance then evaluation of Javascript should be done on a separate thread

r0x0r avatar May 26 '25 07:05 r0x0r

The execution time of evaluateJavascript depends on the code you execute. If you are concerned with performance then evaluation of Javascript should be done on a separate thread

@r0x0r alright makes sense now.

kengoon avatar May 26 '25 07:05 kengoon

@r0x0r can I know why you did this

def run_ui_thread(f, *args, **kwargs):
    Runnable(f)(args, kwargs)

instead of

from android.runnable import run_on_ui_thread

Maybe there's something you know that I don't?

kengoon avatar May 26 '25 22:05 kengoon

@r0x0r can I know where the window is coming from

 self.pywebview_window = window

kengoon avatar May 26 '25 22:05 kengoon

There is no particular reason for a run_ui_thread function as far as I remember. I did not know better.

window object is passed to the target platform create_window in here Each platform instantiates a browser view object in its create_window function

r0x0r avatar May 27 '25 07:05 r0x0r

@r0x0r run_on_ui_thread is used to run python codes on Android UI thread instead of python thread, when it comes to interacting with a UI object (WebView in this case). So since you don't know what that function does, I would say it's safe for me to remove it. We will use the one that's directly imported from android module which is safer and has caching abilities.

kengoon avatar May 27 '25 07:05 kengoon

@r0x0r kivy successfully decommissioned. Startup time is pretty good. I will clean up the code and provide support for Kivy then we are good to go.

Screenshot_20250527-084805.png

kengoon avatar May 27 '25 07:05 kengoon

Also don't worry about the WebView going beyond statusbar. It's a personal customization and won't be part of pywebview directly. Anyone that needs it can take a look at https://github.com/kvdroid/kvdroid

kengoon avatar May 27 '25 07:05 kengoon

I tried building it, but I get this this error. Kivy is still present in android.py too.

05-29 00:14:13.087 31329 31515 I python  : [pywebview] Using Kivy
05-29 00:14:13.087 31329 31515 I python  : DEBUG:pywebview:Using Kivy
05-29 00:14:13.087 31329 31515 I python  : Traceback (most recent call last):
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/main.py", line 23, in <module>
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/webview/__init__.py", line 201, in start
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/webview/guilib.py", line 141, in initialize
05-29 00:14:13.087 31329 31515 I python  : AttributeError: module 'webview.platforms.android' has no attribute 'setup_app'
05-29 00:14:13.087 31329 31515 I python  : Python for android ended.

r0x0r avatar May 29 '25 07:05 r0x0r

I tried building it, but I get this this error. Kivy is still present in android.py too.

05-29 00:14:13.087 31329 31515 I python  : [pywebview] Using Kivy
05-29 00:14:13.087 31329 31515 I python  : DEBUG:pywebview:Using Kivy
05-29 00:14:13.087 31329 31515 I python  : Traceback (most recent call last):
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/main.py", line 23, in <module>
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/webview/__init__.py", line 201, in start
05-29 00:14:13.087 31329 31515 I python  :   File "/Users/roman/Code/pywebview/.buildozer/android/app/webview/guilib.py", line 141, in initialize
05-29 00:14:13.087 31329 31515 I python  : AttributeError: module 'webview.platforms.android' has no attribute 'setup_app'
05-29 00:14:13.087 31329 31515 I python  : Python for android ended.

@r0x0r Don''t build yet, I'm yet to clean up and push commit for the new android module. There's still some work to do at the moment, but I'm having some financial challenges right now. So I'm trying to see how I can stabilize myself before getting back to the PR. Hopefully this weekend I should be back to complete it.

kengoon avatar May 29 '25 07:05 kengoon

@r0x0r also after this PR, I have further ideas on how to reduce the weight of pywebview on Android. We need to remove sdl completely because it doesn't do anything for pywebview. It's only being used by Kivy and other recipes on python-for-android that depends on it. This will make pywebview very light weight on Android. Anyone who wants to use sdl can include it as a requirement on their own. The way to tackle this is by having a bootstrap for pywebview in p4a. This way pywebview will have full control on how it works on Android. No Kivy, No sdl, smaller app size.

kengoon avatar May 29 '25 08:05 kengoon

@r0x0r do you know why I'm having this error

05-30 16:55:20.750  9233  9233 I python  :   File "/data/data/com.parkkly.host/files/app/webview/platforms/android/__init__.py", line 218, in _back_pressed
05-30 16:55:20.750  9233  9233 I python  :     self.pywebview_window.closed.set()
05-30 16:55:20.750  9233  9233 I python  :     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
05-30 16:55:20.750  9233  9233 I python  : AttributeError: 'Window' object has no attribute 'closed'

kengoon avatar May 30 '25 16:05 kengoon

@r0x0r you can review and test. my work is done on this PR. once merged we can discuss how we can remove sdl completely

kengoon avatar May 30 '25 16:05 kengoon

@r0x0r please review.

kengoon avatar Jun 03 '25 06:06 kengoon

@kengoon I tested it quickly and it worked like a charm. Give a few to do proper testing and review.

r0x0r avatar Jun 03 '25 07:06 r0x0r

@r0x0r alright 👍

kengoon avatar Jun 03 '25 07:06 kengoon

A couple of issues.

  • What is your buildozer file? Mine is below
  • After building and starting app, I still get kivy references in the build log Starting: Intent { act=org.kivy.android.PythonActivity cmp=com.pywebview.pywebview/org.kivy.android.PythonActivity } and the app has got a Kivy splash screen
  • JS API seems to be broken. The error in logcat is
 I python  : ===== Python/java method missing ======
 I python  : Python class:<webview.platforms.android.jinterface.pywebview.JsApiCallbackWrapper object at 0x7807a751d0>
 I python  : Java method name:callback
 I python  : Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 I python  : =======================================
 I python  : 
 I python  : Traceback (most recent call last):
 I python  :   File "jnius/jnius_proxy.pxi", line 50, in jnius.jnius.PythonJavaClass.invoke
 I python  :   File "jnius/jnius_proxy.pxi", line 74, in jnius.jnius.PythonJavaClass._invoke
 I python  : NotImplementedError: The method ('callback', ('V', ('Ljava/lang/String;', 'Ljava/lang/String;', 'Ljava/lang/String;'))) is not implemented

Buildozer file (with comments stripped)

[app]
title = pywebview simple browser
package.name = pywebview
package.domain = com.pywebview
source.dir = ./
source.include_exts = py,png,jpg,kv,atlas,html,jar,css,js
source.include_patterns = assets/*,images/*.png,webview/js/*.js
source.exclude_exts = spec,
source.exclude_dirs = bin,build,dist,docs,logo,tests,pywebview.egg-info,android2,examples
source.exclude_patterns = venv*/*/*,images/*/*.jpg
version = 0.1
requirements = python3,bottle,proxy_tools,typing_extensions,cryptography
requirements.source.webview = webview
presplash.filename = %(source.dir)s/logo/logo.png
orientation = portrait,landscape
osx.python_version = 3
fullscreen = 0
android.presplash_color =
android.permissions = android.permission.INTERNET, (name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18)
android.apptheme = @android:style/Theme.Material.NoActionBar
android.add_jars = webview/lib/pywebview-android.jar
android.archs = arm64-v8a, armeabi-v7a
android.allow_backup = True
adb
ios.kivy_ios_url = https://github.com/kivy/kivy-ios
ios.kivy_ios_branch = master
ios.ios_deploy_url = https://github.com/phonegap/ios-deploy
ios.ios_deploy_branch = 1.10.0
ios.codesign.allowed = false
[buildozer]
log_level = 2
warn_on_root = 1

r0x0r avatar Jun 05 '25 11:06 r0x0r

@r0x0r

  • What is your buildozer file? Mine is below

I used an existing .buildozer cache to build, so my requirement file might be quite confusing to you. I did some low level stuffs. Though yours is lgtm

  • After building and starting app, I still get kivy references in the build log Starting: Intent { act=org.kivy.android.PythonActivity cmp=com.pywebview.pywebview/org.kivy.android.PythonActivity } and the app has got a Kivy splash screen

That's not part of kivy framework, it's been used by python for android to run your python, it just happened that they named the package org.kivy...... if you are seeing kivy icon on the splash screen, it means you didn't set your own splash screen properly. The splash screen is needed so that python can start in the background while the user waits without a black screen or something like that.

  • JS API seems to be broken. The error in logcat is
 I python  : ===== Python/java method missing ======
 I python  : Python class:<webview.platforms.android.jinterface.pywebview.JsApiCallbackWrapper object at 0x7807a751d0>
 I python  : Java method name:callback
 I python  : Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 I python  : =======================================
 I python  : 
 I python  : Traceback (most recent call last):
 I python  :   File "jnius/jnius_proxy.pxi", line 50, in jnius.jnius.PythonJavaClass.invoke
 I python  :   File "jnius/jnius_proxy.pxi", line 74, in jnius.jnius.PythonJavaClass._invoke
 I python  : NotImplementedError: The method ('callback', ('V', ('Ljava/lang/String;', 'Ljava/lang/String;', 'Ljava/lang/String;'))) is not implemented

I will have to take a look on this and fix it.

kengoon avatar Jun 05 '25 11:06 kengoon

@r0x0r For the the JS API Error, i think you didn't compile using the latest commit I made. clean your buildozer cache and test again.

kengoon avatar Jun 10 '25 15:06 kengoon

@kengoon I removed .buildozer dir, re-built, but I get the same error. I checked .buildozer/android/app/webview/platforms/android and it has correct files in it.

r0x0r avatar Jun 11 '25 08:06 r0x0r

@r0x0r can you send the copy of the code you ran let me test with it and see. Also what exactly did you do to get that error.

kengoon avatar Jun 11 '25 08:06 kengoon

I am using examples/js_api.py as main.py placed to the project root with the following buildozer.spec

[app]
title = pywebview simple browser
package.name = pywebview
package.domain = com.pywebview
source.dir = ./
source.include_exts = py,png,jpg,kv,atlas,html,jar,css,js
source.include_patterns = assets/*,images/*.png,webview/js/*.js
source.exclude_exts = spec,
source.exclude_dirs = bin,build,dist,docs,logo,tests,pywebview.egg-info,android2,examples
source.exclude_patterns = venv*/*/*,images/*/*.jpg
version = 0.1
requirements = python3,bottle,proxy_tools,typing_extensions,cryptography
requirements.source.webview = webview
presplash.filename = %(source.dir)s/logo/logo.png
orientation = portrait,landscape
osx.python_version = 3
fullscreen = 0
android.presplash_color =
android.permissions = android.permission.INTERNET, (name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18)
android.apptheme = @android:style/Theme.Material.NoActionBar
android.add_jars = webview/lib/pywebview-android.jar
android.archs = arm64-v8a, armeabi-v7a
android.allow_backup = True
adb
ios.kivy_ios_url = https://github.com/kivy/kivy-ios
ios.kivy_ios_branch = master
ios.ios_deploy_url = https://github.com/phonegap/ios-deploy
ios.ios_deploy_branch = 1.10.0
ios.codesign.allowed = false
[buildozer]
log_level = 2
warn_on_root = 1

r0x0r avatar Jun 11 '25 10:06 r0x0r

@r0x0r just fixed it. You can test and again

kengoon avatar Jun 15 '25 18:06 kengoon

@r0x0r

https://github.com/user-attachments/assets/988b73a3-9445-4f74-b95b-aa6e2ae7ded9

kengoon avatar Jun 16 '25 08:06 kengoon

I confirmed that JS API works with the latest commit. I am going to do more testing later. I am writing a separate Android test suite now, which I am going to test on this PR.

r0x0r avatar Jun 17 '25 21:06 r0x0r

I have created a basic Android test suite, but it has revealed a whole bunch of problems with the Android implementation (both the original one and the one in PR). The most pressing problem is that tests cause Android app to crash. I have spent a lot of time to investigate it, but with limited results. It seems that crashes are partly caused by Python's eval used by tests, but there is something wrong with JS API calls as well. My gut feeling is that the problem is caused how threads are invoked. I would appreciate help on this one, if you got time.

I have pushed Android test suite to the 6.0 branch and it is located in tests/android/main.py. Currently only tests not crashing Android app are selected.

r0x0r avatar Jun 24 '25 07:06 r0x0r

@r0x0r did you run the test locally or here on GitHub, I want to access the error logs.

kengoon avatar Jun 24 '25 08:06 kengoon