上一篇博客已经讲了Python与C的交互,本篇主要关于在Android项目中嵌入Python解释器,实现Java与Python相互调用。以有好的项目提供了思路,地址:https://github.com/joaoventura/pybridge

我这里说一下简单实现

1、环境准备

配置好crystax ndk环境,并创建一个NDK项目,将crystax 包下面的 libpython3.5m.so拷贝至工程 lib/armeabi目录下

2、为Java 编写 native方法

public class Util {

static {

try {

System.loadLibrary("jni_test");

} catch (Exception e) {

Log.e("jni_test", ""+e);

}

}

public static native int run(String path);

}

3、编写Jni代码

jni_test.c

#include

#include

#include

#define LOG(x) __android_log_write(ANDROID_LOG_WARN, "jni_test", (x))

/* --------------- */

/* Android log */

/* --------------- */

static PyObject *androidlog(PyObject *self, PyObject *args)

{

char *str;

if (!PyArg_ParseTuple(args, "s", &str))

return NULL;

LOG(str);

Py_RETURN_NONE;

}

static PyMethodDef AndroidlogMethods[] = {

{"log", androidlog, METH_VARARGS, "Logs to Android stdout"},

{NULL, NULL, 0, NULL}

};

static struct PyModuleDef AndroidlogModule = {

PyModuleDef_HEAD_INIT,

"androidlog", /* m_name */

"Log for Android", /* m_doc */

-1, /* m_size */

AndroidlogMethods /* m_methods */

};

PyMODINIT_FUNC PyInit_androidlog(void)

{

return PyModule_Create(&AndroidlogModule);

}

void setAndroidLog()

{

// Inject bootstrap code to redirect python stdin/stdout

// to the androidlog module

PyRun_SimpleString(

"import sysn"

"import androidlogn"

"class LogFile(object):n"

" def __init__(self):n"

" self.buffer = ''n"

" def write(self, s):n"

" s = self.buffer + sn"

" lines = s.split("\n")n"

" for l in lines[:-1]:n"

" androidlog.log(l)n"

" self.buffer = lines[-1]n"

" def flush(self):n"

" returnn"

"sys.stdout = sys.stderr = LogFile()n"

);

}

/* --------------------------------------------------------------- */

/* 以上部分代码,为C语言为Python3编写拓展模块的标准模板代码。 */

/* 这套模板写起来有些繁琐,我们之前已经用SWIG自动化实现过拓展模块 */

/* 这部分代码主要功能是将Python的print输出连接到Android的Log输出中 */

/* 与我们要探讨的内容联系不大,无须感到困惑 */

/* ---------------------------------------------------------------- */

/* java对应的native方法 */

JNIEXPORT jint

JNICALL Java_com_example_jnitest_Util_run(JNIEnv *env, jobject obj, jstring path)

{

LOG("Initializing the Python interpreter");

const char *pypath = (*env)->GetStringUTFChars(env, path, NULL);

// Build paths for the Python interpreter

char paths[512];

snprintf(paths, sizeof(paths), "%s:%s/stdlib.zip", pypath, pypath);

// Set Python paths

wchar_t *wchar_paths = Py_DecodeLocale(paths, NULL);

Py_SetPath(wchar_paths);

PyImport_AppendInittab("androidlog", PyInit_androidlog);

Py_Initialize();//初始化Python解析器

if (!Py_IsInitialized())

{

LOG("Initialize failed");

return -1;

}

setAndroidLog();

PyRun_SimpleString("import test");

// Cleanup

(*env)->ReleaseStringUTFChars(env, path, pypath);

PyMem_RawFree(wchar_paths);

Py_Finalize();//释放Python解析器

return 0;

}

LOCAL_PATH := $(call my-dir)

CRYSTAX_PATH := D:/developer/AS_SDK/crystax-ndk-10.3.2

include $(CLEAR_VARS)

LOCAL_MODULE := jni_test

LOCAL_SRC_FILES := jni_test.c

LOCAL_LDLIBS := -llog

LOCAL_SHARED_LIBRARIES := python3.5m

include $(BUILD_SHARED_LIBRARY)

# Include libpython3.5m.so

include $(CLEAR_VARS)

LOCAL_MODULE := python3.5m

LOCAL_SRC_FILES := $(CRYSTAX_PATH)/sources/python/3.5/libs/$(TARGET_ARCH_ABI)/libpython3.5m.so

LOCAL_EXPORT_CFLAGS := -I $(CRYSTAX_PATH)/sources/python/3.5/include/python/

include $(PREBUILT_SHARED_LIBRARY)

4、编写Python代码

def sayHello():

print("Hello Android,from Python ")

sayHello()

5、项目调用

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

AssetExtractor assetExtractor = new AssetExtractor(this);

assetExtractor.removeAssets("python");

assetExtractor.copyAssets("python");

final String pythonPath = assetExtractor.getAssetsDataDir() + "python";

AsyncTask.execute(new Runnable() {

@Override

public void run() {

Log.d("---> ",""+Util.run(pythonPath));

}

});

}

}

AssetExtractor.java 工具类,部分代码

public class AssetExtractor {

private final static String LOGTAG = "AssetExtractor";

private Context mContext;

private AssetManager mAssetManager;

public AssetExtractor(Context context) {

mContext = context;

mAssetManager = context.getAssets();

}

/**

* Copies the assets from the APK to the device.

*

* @param path: the source path

*/

public void copyAssets(String path) {

for (String asset : listAssets(path)) {

copyAssetFile(asset, getAssetsDataDir() + asset);

}

}

/**

* Removes recursively the assets from the device.

*

* @param path: the path to the assets folder

*/

public void removeAssets(String path) {

File file = new File(getAssetsDataDir() + path);

recursiveDelete(file);

}

}

方案一的实现到此结束,这种方案的好处是可以完全自主的控制Python解释器与java的交互,缺点是过于麻烦,且须精通Android的ndk编程以及C语言,否则出问题,不会解决。

与之相比,下一篇方案二的实现则简单得多。

关注个人公众号:编程之路从0到1

f8ceb6e20031b996220c1a1ddf87a336.png

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐