Windy's little blog

一切生活中的杂七杂八, and I like CTF.

Kivy/KivyMD开发Andoird程序的一些踩坑记录

之前用python做了个命令行的爬虫,在安卓上运行时还得打开termux,用上虚拟键盘,很不方便,于是一直想改为Android版本的。近期刚发现Kivy/KivyMD可以基于Python进行跨平台开发,国庆期间进行了尝试。在此记录一些踩坑经历,供有相同需求的同学参考。

一、解决中文乱码问题

Kivy和KivyMD原生不支持中文,采用的默认字体是英文字体,直接运行起来就会显示方框。

Kivy中可以通过设置LabelBase修改字体,但是每一个控件都要在代码中增加font_style的定义,非常不方便。而KivyMD的控件就没有提供定义LabelBase改字体的功能。

而且,即使在本机开发环境中修改了字体设置,在打包生成apk文件安装后,在手机上运行时仍然是乱码。

下面是使用buildozer打包时,彻底解决中文乱码问题的方法。

第一步,修改kivy字体设置

buildozer打包过程中,会首先下载kivy,并保存在.buildozer/android/platform/build-armeabi-v7a/packages/kivy/2.0.0.zip

解压该文件,修改config.py:

     elif version == 16:
         Config.setdefault('kivy', 'default_font', [
             'Roboto',
             'data/fonts/msyh.ttc'])

这里我用的是msyh.ttc(微软雅黑)。Roboto是kivy内部定义的字体类型名称,不用修改,这样的话其它文件引用Roboto时,相当于引用了中文字体,这样改动的地方最少。

然后要将新字体的文件复制到kivy/data/fonts/目录下。重新打包生成2.0.0.zip文件,替换原文件。

第二步,修改kivyMD的字体设置

运行一次buildozer android debug后,会将kivyMD以及其它需要的包下载到.buildozer/android/platform/build-armeabi-v7a/build/python-installs/yourapp/这个目录下。

(刚才的 kivy的2.0.0.zip压缩包也会被解压到这个目录下,如果第一步没改,也可以在这里修改)

首先将新增的msyh.ttc字体文件复制到kivymd/fonts/目录下,然后修改font_definitions.py.

 fonts = [
     {
         "name": "Roboto",
         "fn_regular": fonts_path + "msyh.ttc",
         "fn_bold": fonts_path + "msyh.ttc",
         "fn_italic": fonts_path + "msyh.ttc",
         "fn_bolditalic": fonts_path + "msyh.ttc",
     },
     {
         "name": "RobotoThin",
         "fn_regular": fonts_path + "msyh.ttc",
         "fn_italic": fonts_path + "msyh.ttc",
     },
     {
         "name": "RobotoLight",
         "fn_regular": fonts_path + "msyh.ttc",
         "fn_italic": fonts_path + "msyh.ttc",
     },
     {
         "name": "RobotoMedium",
         "fn_regular": fonts_path + "msyh.ttc",
         "fn_italic": fonts_path + "msyh.ttc",
     },
     {
         "name": "RobotoBlack",
         "fn_regular": fonts_path + "msyh.ttc",
         "fn_italic": fonts_path + "msyh.ttc",
     },
     {
         "name": "Icons",
         "fn_regular": fonts_path + "materialdesignicons-webfont.ttf",
     },
 ]

这里,同样不修改Boboto的字体类型名称,这样不用修改其它诸多py文件。我是都改为了msyh.ttc,你也可以按需增加light、Black等字体。

第三步,重新编译打包

删除所有的pyc文件,删除/home/kali/Documents/kivy_exp/.buildozer/android/platform/build-armeabi-v7a/dists/yourapp__armeabi-v7a下所有上一次打包时生成的文件,重新用buildozer打包。

中文乱码问题即可完美解决。

二、安卓调试问题

直接运行adb logcat,会显示大量老旧和无关调试信息,造成定位我们程序Bug的难度增加。

这里,可以先清空log文件。

 adb logcat -c

然后通过搜索‘Python for android ended’这句话,快速找到出错点。

比如下面的log文件就显示出,程序崩溃是因为没有加载docutils模块。

 10-06 23:20:08.444  4181  6396 I python  :    File "/home/xxx/Documents/kivy_exp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/yourapp/kivy/lang/builder.py", line 663, in _apply_rule                                 
 10-06 23:20:08.444  4181  6396 I python  :    File "/home/xxx/Documents/kivy_exp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/yourapp/kivy/lang/builder.py", line 621, in _apply_rule                                
 10-06 23:20:08.444  4181  6396 I python  :    File "/home/xxx/Documents/kivy_exp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/yourapp/kivy/factory.py", line 154, in __getattr__                                      
 10-06 23:20:08.445  4181  6396 I python  :    File "/home/xxx/Documents/kivy_exp/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/yourapp/kivy/uix/rst.py", line 81, in <module>                                          
 10-06 23:20:08.445  4181  6396 I python  :  ModuleNotFoundError: No module named 'docutils'    
 10-06 23:20:08.445  4181  6396 I python  : Python for android ended.            

三、buildozer.spec的设置问题

buildozer.spec的设置很重要,往往决定了打包apk过程能否顺利,以及apk安装到手机后能否正常运行。以下几个设置需要关注。

一是依赖模块要全

比如我的程序的依赖模块如下

 requirements = python3,kivy,https://github.com/kivymd/KivyMD/archive/master.zip,pygments,sdl2_ttf,pillow,requests,urllib3,datetime,beautifulsoup4,baidu-aip,chardet,idna,docutils,android,pyjnius

KivyMD默认版本过低,必须用github上的。

pygments,sdl2_ttf,pillow,chardet,idna,docutils是编译kivy必须的。

android是请求安卓权限必须的(特别是在安卓10和11上)。

二是安卓权限要定义,并在程序运行中动态请求

安卓10以后,对权限的管理大为改变,我初期调试时多次因为权限设置问题通不过。

buildozer.spec中要进行定义:

 android.permissions = WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,INTERNET,WAKE_LOCK
 android.wakelock = True

然后代码中要进行相应申请:

 from kivy.utils import platform
 if platform == "android":
     from android.permissions import request_permissions, Permission
     request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE, Permission.INTERNET])    
     from android.storage import primary_external_storage_path
     appwd=primary_external_storage_path()+'/Download/yourapp'

四、用pyinstaller打包成单个EXE的问题

pyinstaller打包成单个exe时,主要要解决子目录资源的包含路径问题,否则经常容易出错。

在我的项目中,包含两个子目录:kv目录下有多个kv文件,assets目录下是图标和图片。

一是最终的spec文件如下:

 # -*- mode: python ; coding: utf-8 -*-
 
 from PyInstaller.building.build_main import *
 import sys
 import os
 
 from kivy_deps import sdl2, glew
 from kivymd import hooks_path as kivymd_hooks_path
 
 a = Analysis(['main.py'],
              pathex=['os.getcwd()'],
              binaries=[],
              datas=[("assets\\*","assets"),
                  ("kv\\*","kv"),              
                   ],
              hiddenimports=[],
              hookspath=[kivymd_hooks_path],
              hooksconfig={},
              runtime_hooks=[],
              excludes=[],
              win_no_prefer_redirects=False,
              win_private_assemblies=False,
              cipher=None,
              noarchive=False)
             
 pyz = PYZ(a.pure, a.zipped_data,
              cipher=None)
 
 exe = EXE(
     pyz,
     a.scripts,
     a.binaries,
     a.zipfiles,
     a.datas,
     *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
     debug=False,
     strip=False,
     upx=True,
     name="gjcinfo",
     console=False,
     icon='assets\\logo.ico',
 )

这时如果直接打包成exe,运行时仍然会显示找不到kv文件的错误。这是因为exe运行解压时,会生成临时目录_MEIPASS,打包进去的kv文件都被展开到临时目录下。这就要在main.py中进行修改:

 from kivy.resources import resource_add_path
 if __name__ == "__main__":
     if hasattr(sys, '_MEIPASS'):
         resource_add_path(os.path.join(sys._MEIPASS))
     MainApp().run()

修改完后运行pyinstaller.exe xxxx.spec,成功。


五、在安卓上使用webview的问题

网上关于此问题最好的示代代码如下:

https://insideconspiracy.blogspot.com/2020/03/inline-webview-within-your-kivy-app.html


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Powered By Z-BlogPHP 1.7.1