先贴3个库的链接 : kivy, buildozer, p4a(python-for-android)
buildozer 是 kivy 开发小组基于自家的 python-for-android 开发的傻瓜式 android 编译工具,用于将 python 项目编译成为 .apk 文件。
本文记录了整个折腾过程(不完全整理,非线性时间顺序),建议读完再操作,因为前面可能是我踩过的坑 .... (时间是 2019年6月22日21:31:38 )
配置
OS : UBUNTU 18.04 LTS (官方 buildozer 虚拟机 XUBUNTU 由于无法换到可以用的源,被替换了)
python : python3
buildozer : 0.39 ( 通过 812 ISSUE 升级到了 0.40.dev0)
python-for-android: master
kivy: 1.11.0
先按照官方步骤完成 buildozer 虚拟机的安装,buildozer 虚拟机安装的是 xubuntu 系统。
首先建议将 buildozer 使用开发分支重新安装,可以解决一个 'Buildozer' object has no attribute 'translate_target' 的问题。
pip install --upgrade https://github.com/kivy/buildozer/archive/4c2152b.zip
在 xubuntu 系统中切换至 项目目录,执行
buildozer init
一个名为 buildozer.spec 的文本文件会被自动生成,里面的内容将指导整个编译过程
我这里的 main.py 内容很简单,只有一个按钮
from kivy.app import App
from kivy.uix.button import Button
class SimpleApp(App):
def build(self):
return Button(text='joke')
if '__main__' == __name__:
SimpleApp().run()
那么将问题简化为编译一个带有 kivy 的程序为 .apk 文件,不包含其他任何依赖
bin 和 build_area 目录分别是编译结果输出目录和编译中间目录,在 buildozer.spec 文件中分别指定到 bin_dir 和 build_dir
按照 README 中所说,从 https://www.crystax.net/en/download 下载了 ndk 并解压,对 buildozer.spec 做如下修改
# (str) Title of your application
title = SimpleAppTitle
# (str) Package name
package.name = SimpleApp
# (str) Package domain (needed for android/ios packaging)
package.domain = dav.test
...
# Require python3crystax:
requirements = python3crystax,kivy
# Point to the directory where you extracted the crystax-ndk:
android.ndk_path = /home/kivy/Desktop/Projects/android/android-ndk-r17c/
另外,你一定有需求的就是打开调试日志
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
log_level = 2
于是就可以按照示例进行编译了,按如下即可,后面俩(deploy run)是部署和运行,需要连接 android 设备
buildozer android debug
那么发现下载过程非常慢,于是就去 p4a(python-for-android) 克隆库下来,指定到目录,同时指定为 master(master 会包含 python3 编译所遇到的一些已知问题的处理)
# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github)
p4a.source_dir = /home/kivy/Desktop/Projects/python-for-android
# (str) python-for-android branch to use, defaults to master
p4a.branch = master
其他的包怎么办呢????我能有的思路就是自己通过比较快速的方法下载好包,替换到 buildozer 执行过程的目录中。那么问题就变成该怎么完成替换了(下载我已经解决好了 ^_^)
那么首先我们看日志
这里有下载三个包:hostpython3,libffi,openssl,我们以 hostpython3 为例,红色方框标识出下载地址,
我们先想办法下下来(wget,迅雷,用VPS下,之类的,自己想办法咯 ....)
然后我们观察日志,看到一个 directory context,干啥的呢?通过终端切过去看了下已经跑过的部分,发现就是保存目标文件的位置。于是将下载好后复制到下面目录中
于是重新跑起来
buildozer android debug
结果不对,编译过程始终会跑到
同时也会把刚刚复制过去的包给删除后重新下载, WTF ??? 怎么办 ???只好去找找代码,看能不能发现些什么。于是先去 buildozer 代码中搜索了一把,没有对应日志的输出,于是只有开启思考人生模式
...
可能过去了10分钟
...
buildozer 只是简化了p4a 的操作过程,具体干活儿的其实是 p4a, 那么我又去 p4a 的代码里搜索了一把日志,发现了一些东西
从代码中明确的看到,.mark文件就是个空文件,那我们也照猫画虎,创建一个名为 【.mark-<文件名>】 的空文件(用 touch),然后再把自行下载好的文件复制过来,就完成了该文件的替换
事实上,完全可以将 packages 文件夹(如下)备份下来,在清理后拷贝回来重新进行编译。
.buildozer/android/platform/build/packages/
高高兴兴的再次开启编译流程
buildozer android debug
我以为只要下载了 android-sdk 就够了,然而事情并没有那么简单,我发现还缺组件,由于自动下载过程中报了错,我误以为是 p4a 的问题,于是再次开启DIY下载模式。在查找 android-sdk 下载方法过程中,发现原来的 android 命令并不推荐使用了,所以使用了推荐 sdkmanager ,切换到 android-sdk/tools/bin 目录下,使用以下命令安装组件
./sdkmanager "platform-tools" "platforms;android-27"
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
at com.android.repository.api.SchemaModule$SchemaModuleVersion.(SchemaModule.java:156)
at com.android.repository.api.SchemaModule.(SchemaModule.java:75)
at com.android.sdklib.repository.AndroidSdkHandler.(AndroidSdkHandler.java:81)
at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 5 more
所以是为什么呢???先看了下那么多的 java 日志,就看了下我安装的 java,发现是11的版本
dav@ubuntu:~/Desktop/Projects/android/android-sdk/tools/bin$ java --version
openjdk 11.0.3 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu218.04.1)
OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu218.04.1, mixed mode)
于是根据错误日志找到一个相关问题 Failed to install android-sdk: “java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema” ,文中提到将 java 版本回退到8就好了,那我重新安装好8以后确认下
dav@ubuntu:~/Desktop/Projects/android/android-sdk/tools/bin$ java --version
Unrecognized option: --version
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
咦???于是通过 apt 直接查看安装情况【后面发现尴尬的是 java -version 可以执行,version前面一个杠】
dav@ubuntu:~/Desktop/Projects/android/android-sdk/tools/bin$ apt list --installed *jdk*
Listing... Done
openjdk-8-dbg/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-demo/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-doc/bionic-updates,bionic-updates,bionic-security,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 all [installed]
openjdk-8-jdk/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-jdk-headless/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-jre/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-jre-dcevm/bionic,now 8u112-2 amd64 [installed]
openjdk-8-jre-headless/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-jre-zero/bionic-updates,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 amd64 [installed]
openjdk-8-source/bionic-updates,bionic-updates,bionic-security,bionic-security,now 8u212-b03-0ubuntu1.18.04.1 all [installed]
这次懒得自己去下载了,干脆直接继续吧
buildozer android debug
从日志和 android-sdk 目录可以看到 android-sdk 的 build-tools,platforms,platform-tools,licenses,以及 tools 被相应的安装和升级,然而好景不长,这时候遇到了一个惊天错误
[ERROR]: The python3crystax recipe can only be built when using the CrystaX NDK. Exiting.
我 ......... 细心的读者肯定记得,前面官网 MASTER 分支上的 README 分明让我们去下载了 crystaX NDK, 并且在 buildozer.spec 文件中指定了 android.ndk_path,同时还将默认的
requirements = python3,kivy ==> 修改为 ==> requirements = python3crystax,kivy
于是我们再一次来到了 buildozer 的 ISSUE界面, 发现了这么一个问题 The python3crystax recipe can only be built when using the CrystaX NDK. Exiting.
那么 kivy 的老板们亲切的提示了
时间是 2019.2.2 ,那么我们用的 master 应该就是符合他的描述了,再次回到 README.md,却发现了 deprecated 字样。
于是我们将 buildozer.spec 中的 requirements 修改回来,同时注释掉 ndk_path, buildozer 会自动下载 android-ndk (非 crystaX 版本)
# Require python3crystax:
requirements = python3,kivy
# (str) Android NDK directory (if empty, it will be automatically downloaded.)
# android.ndk_path = /home/kivy/Desktop/Projects/android/android-ndk/
再次开启编译过程
buildozer android debug
结果遇到了下面的问题
[INFO]: -> running patch -t -d /home/dav/Desktop/...(and 245 more)
Exception in thread background thread for pid 37261:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/home/dav/.local/share/virtualenvs/build_area-AnJkn9zu/lib/python3.7/site-packages/sh.py", line 1540, in wrap
fn(*args, **kwargs)
File "/home/dav/.local/share/virtualenvs/build_area-AnJkn9zu/lib/python3.7/site-packages/sh.py", line 2459, in background_thread
handle_exit_code(exit_code)
File "/home/dav/.local/share/virtualenvs/build_area-AnJkn9zu/lib/python3.7/site-packages/sh.py", line 2157, in fn
return self.command.handle_command_exit_code(exit_code)
File "/home/dav/.local/share/virtualenvs/build_area-AnJkn9zu/lib/python3.7/site-packages/sh.py", line 815, in handle_command_exit_code
raise exc
sh.ErrorReturnCode_1:
RAN: /usr/bin/patch -t -d /home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/bootstrap_builds/sdl2-python3/jni/SDL2_image -p1 -i /home/dav/Desktop/workspace/python-for-android/pythonforandroid/recipes/sdl2_image/toggle_jpg_png_webp.patch
STDOUT:
can't find file to patch at input line 3
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|--- SDL2_image-2.0.4/Android.mk.orig 2018-10-31 15:58:52.000000000 +0100
|+++ SDL2_image-2.0.4/Android.mk 2019-02-07 23:51:51.740299680 +0100
--------------------------
No file to patch. Skipping patch.
1 out of 1 hunk ignored
STDERR:
看样子最后执行的命令是 patch 命令,于是去研究了一下 patch 究竟是干什么的,做了下面的简单的实验
dav@ubuntu:~/Desktop/workspace/test$ cat a
test
just
finer
dav@ubuntu:~/Desktop/workspace/test$ cat b
tester
just
fine
dav@ubuntu:~/Desktop/workspace/test$ cat c
test
just
finer
dav@ubuntu:~/Desktop/workspace/test$ diff a b > d
dav@ubuntu:~/Desktop/workspace/test$ cat d
1c1
< test
---
> tester
3c3
< finer
---
> fine
dav@ubuntu:~/Desktop/workspace/test$ patch c d
patching file c
dav@ubuntu:~/Desktop/workspace/test$ cat c
tester
just
fine
那么很明确了,应该就是将补丁文件中的更新同步到本地文件的,那么我搜遍了我可以找到的地方,只有一个 ISSUE 最为接近:Problems in patching files during building for android_new,然而作者已经明确表明是因为自己的错误修改造成的,那么我只好去 buildozer 的界面提交了一个新的 ISSUE,问题待解决 ....
【2019/06/25】
THX to AndreMiras,
根据他提供的建议,我们工作目录中 .buildozer 文件夹下的所有文件删除,重新开始编译
rm .buildozer/* -vrf
buildozer --verbose android debug
终于跑过了 patch 的问题。回过头来看,根据描述,应该是因为下载过程中异常中断导致压缩包数据不全,解压后文件丢失,重新下载后解决问题。
继续,我们又遇到了“新朋友”
[INFO]: Building pyjnius for armeabi-v7a
[INFO]: jnius apparently isn't already in site-packages
[INFO]: Cythonizing anything necessary in pyjnius
[INFO]: -> directory context /home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/pyjnius-python3-sdl2/armeabi-v7a__ndk_target_21/pyjnius
[INFO]: -> running python -c import sys; print(sys.path)
[INFO]: Trying first build of pyjnius to get cython files: this is expected to fail
[INFO]: -> running python setup.py build_ext -v
...
RAN: /home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/hostpython3/desktop/hostpython3/native-build/python setup.py build_ext -v
STDOUT:
/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/hostpython3/desktop/hostpython3/Lib/distutils/dist.py:274: UserWarning: Unknown distribution option: 'install_requires'
warnings.warn(msg)
running build_ext
building 'jnius' extension
creating build
creating build/temp.linux-x86_64-3.7
creating build/temp.linux-x86_64-3.7/jnius
arm-linux-androideabi-gcc -DANDROID -fomit-frame-pointer -D__ANDROID_API__=21 -mandroid -isystem /home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi -I/home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi -isysroot /home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot -I/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/python-installs/SimpleApp/include/python3.7 -DNDEBUG -g -fwrapv -O3 -Wall -DANDROID -fomit-frame-pointer -D__ANDROID_API__=21 -mandroid -isystem /home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi -I/home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi -isysroot /home/dav/Desktop/workspace/android/android-ndk-r17c/sysroot -I/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/python-installs/SimpleApp/include/python3.7 -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -mthumb -I/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Include -fPIC -I/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/hostpython3/desktop/hostpython3/Include -I/home/dav/Desktop/workspace/build_area/buildozer_projects/test/.buildozer/android/platform/build/build/other_builds/hostpython3/desktop/hostpython3/native-build -c jnius/jnius.c -o build/temp.linux-x86_64-3.7/jnius/jnius.o
arm-linux-androideabi-gcc: error: jnius/jnius.c: No such file or directory
arm-linux-androideabi-gcc: fatal error: no input files
compilation terminated.
error: command 'arm-linux-androideabi-gcc' failed with exit status 1
STDERR:
最核心的错误描述在于
arm-linux-androideabi-gcc: error: jnius/jnius.c: No such file or directory
那么顺着这个问题找下去,发现一个相关的 ISSUE :jnius/jnius.c: No such file or directory
按照 deusyss 的提示 在系统中安装 cython 安装
sudo apt install cython -y
然后再在 python 内安装
pip install cython
再次开启编译
buildozer --verbose android debug
然后就看到了一条非常养眼的文字
于是,故事结束的好突然,so sad ....
【2019/07/26】
那么我们回头再看一下需要做的东西,其实就只剩下
1. 安装一个适当版本的 buildozer => 0.40.dev0
2. 初始化
buildozer init
3. 修改编译基本参数
# (str) Title of your application
title = SimpleAppTitle
# (str) Package name
package.name = SimpleApp
# (str) Package domain (needed for android/ios packaging)
package.domain = dav.test
...
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
log_level = 2
4. 解决一些依赖包/库问题 (大部分的都已经通过 buildozer 解决了), 如 openJDK
面临最大的挑战就是网络问题,目前我实践下来最好的结果就是直接在外面的 VPS 上面编译,网络会稳定不少