Android 平台的Python——基础篇(一)
Android 平台的Python——JNI方案(二)
Android 平台的Python——CLE方案实现(三)
Android 平台的Python——第三方库移植
Android 平台的Python——编译Python解释器
更新:本文已过时!!!CLE不再被推荐,接口不友好,且未提供一些需要编译的Python 第三方库,如numpy、pillow等等,推荐更新的方案 Android与Python混合编程
CLE(Common Language Extension)公共语言拓展
官网:http://www.srplab.com/cn/index.html
来自官网的简介:
CLE中间件支持多脚本语言混合编程。c/c++, lua, python, c#, ruby, java等语言可以通过CLE中间件直接使用其它语言中的类,函数,变量或者模块, 使得现有的功能库或者模块可以很容易的应用到其它编程语言中。开发者可以使用熟悉的语言编写功能库,然后在其它语言开发的应用中使用。CLE非常简单,其执行体仅为一个核心共享库和对应于每个脚本语言接口的共享库.
我们建议开发人员使用脚本语言编写主逻辑,并使用平台相关的语言开发GUI或设备特定部分。这样不仅可以保证应用程序的可移植性,而且还利用了平台SDK的优势。使用脚本语言可能会导致安装包的大小增加, 性能略有下降,但随着硬件性能提高和存储增加, 这些将不成为问题
在Android中通过CLE框架,实现与Python交互(以下内容为eclipse环境)
注:本博客之前以eclipse环境为主,后续有读者反馈,Android Studio下无法运行,在Android Studio下
android-support-v4.jar
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.study"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
// jni配置
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
在官网下载最新的CLE for Android 开发包,其中包含示例工程和API文档。解压开发包,拷贝如下文件
text.py
def add(x,y) :
return x+y
calljava.py
import imp #test load path
def log(content):
JavaClass.d("formPython",content)
log("Hello Android,form python")
py_code.py
import time
def get_time():
return time.time()
将py_code.py压缩为py_code.zip文件。将编写的Python源码放入Android 工程的assets目录,其中还要包含一些Python需要的环境及标准库,见下图
public class MainActivity extends Activity {
public StarSrvGroupClass SrvGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final File appFile = getFilesDir(); /*-- /data/data/packageName/files --*/
final String appLib = getApplicationInfo().nativeLibraryDir;
AsyncTask.execute(new Runnable() {
@Override
public void run() {
loadPy(appFile,appLib);
}
});
}
void loadPy(File appFile,String appLib){
//拷贝Python相关环境
File pythonLibFile = new File(appFile, "python3.4.zip");
if (!pythonLibFile.exists()) {
copyFile(this, "python3.4.zip");
copyFile(this, "_struct.cpython-34m.so");
copyFile(this, "binascii.cpython-34m.so");
copyFile(this, "time.cpython-34m.so");
copyFile(this, "zlib.cpython-34m.so");
}
// 拷贝Python 代码
copyFile(this, "calljava.py");
copyFile(this, "test.py");
try {
// 加载Python解释器
System.load(appLib + File.separator + "libpython3.4m.so");
// 除了将代码直接拷贝,还支持将代码压缩为zip包,通过Install方法解压到指定路径
InputStream dataSource = getAssets().open("py_code.zip");
StarCoreFactoryPath.Install(dataSource, appFile.getPath(),true );
} catch (Exception e) {
e.printStackTrace();
}
/*----init starcore----*/
StarCoreFactoryPath.StarCoreCoreLibraryPath = appLib;
StarCoreFactoryPath.StarCoreShareLibraryPath = appLib;
StarCoreFactoryPath.StarCoreOperationPath = appFile.getPath();
StarCoreFactory starcore = StarCoreFactory.GetFactory();
StarServiceClass Service = starcore._InitSimple("test", "123", 0, 0);
SrvGroup = (StarSrvGroupClass) Service._Get("_ServiceGroup");
Service._CheckPassword(false);
/*----run python code----*/
SrvGroup._InitRaw("python34", Service);
StarObjectClass python = Service._ImportRawContext("python", "", false, "");
// 设置Python模块加载路径
python._Call("import", "sys");
StarObjectClass pythonSys = python._GetObject("sys");
StarObjectClass pythonPath = (StarObjectClass) pythonSys._Get("path");
pythonPath._Call("insert", 0, appFile.getPath()+ File.separator +"python3.4.zip");
pythonPath._Call("insert", 0, appLib);
pythonPath._Call("insert", 0, appFile.getPath());
//调用Python代码
Service._DoFile("python", appFile.getPath() + "/py_code.py", "");
long time = python._Calllong("get_time");
Log.d("", "form python time="+time);
Service._DoFile("python", appFile.getPath() + "/test.py", "");
int result = python._Callint("add", 5, 2);
Log.d("", "result="+result);
python._Set("JavaClass", Log.class);
Service._DoFile("python", appFile.getPath() + "/calljava.py", "");
}
private void copyFile(Context c, String Name) {
File outfile = new File(c.getFilesDir(), Name);
BufferedOutputStream outStream = null;
BufferedInputStream inStream = null;
try {
outStream = new BufferedOutputStream(new FileOutputStream(outfile));
inStream = new BufferedInputStream(c.getAssets().open(Name));
byte[] buffer = new byte[1024 * 10];
int readLen = 0;
while ((readLen = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inStream != null) inStream.close();
if (outStream != null) outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上代码即完成了Java与Python的互相调用,其中python._Set("JavaClass", Log.class)
一句,指将一个java类设置给Python,变成Python类,第一个参数指定在Python中的类名,此处随意起了一个类名JavaClass,该类可直接在Python中使用,如上将Android的日志输出类设置给Python使用
关于CLE的相关原理,可进入官网了解,更多详细用法,见CLE文档