本文一个运行于android的python解释器的例子,版本为python2.7,也可以是其它版本,Python共享库采用NDK编译。这里的例子为了说明如何初始化python解释器,运行python脚本,例子中的界面比较简单,一个输入栏用于输入python脚,一个输出栏用于显示运行的结果。Android代码基于java,需要通过java调用python。

  这里Python解释器基于cle开发,提供了java到native的双向调用。首先需要初始化cle,然后加载和初始化python解释器,最后是获取输入的脚本并捕获输出结果。

1.创建工程

  创建一个android,将相关的库文件拷贝到libs目录下:starcore_android_r2.51.jar,将python2.7.zip拷贝到assets目录下,将共享库拷贝到jniLibs/armeabi目录下:libpython2.7.so;libstar_java.so;libstarcore.so;libstarpy.so

python制作安卓动态库 python 安卓库_java

2. 初始化CLE和python

  例子基于CLE开发,首先需要初始化CLE,为了不影响界面刷新,使用独立的线程进行初始化,CLE运行于初始化的线程中。但是从界面线程或者其它线程中调用CLE相关的函数,或者执行python脚本,需要进行加锁操作,此外该线程需要维护CLE的消息循环。

a. 首先将python2.7.zip拷贝到应用运行的/files目录下



File destDir = new File("/data/data/"+getPackageName()+"/files");
        if(!destDir.exists())
            destDir.mkdirs();
        java.io.File python2_7_libFile = new java.io.File("/data/data/"+getPackageName()+"/files/python2.7.zip");
        if( !python2_7_libFile.exists() ){
            try{
                copyFile(this,"python2.7.zip",null);
            }
            catch(Exception e){
            }
        }


  b. 创建线程,初始化python,然后进入CLE的消息循环



/*----init starcore----*/
        StarCoreFactoryPath.StarCoreCoreLibraryPath = this.getApplicationInfo().nativeLibraryDir;
        StarCoreFactoryPath.StarCoreShareLibraryPath = this.getApplicationInfo().nativeLibraryDir;
        StarCoreFactoryPath.StarCoreOperationPath = "/data/data/"+getPackageName()+"/files";
        
        final String LibPath = this.getApplicationInfo().nativeLibraryDir;
        final String PackagePath = "/data/data/"+getPackageName();
        
        new Thread(new Runnable(){
            @Override
            public void run() {
		        starcore= StarCoreFactory.GetFactory();
		        Service=starcore._InitSimple("test","123",0,0);
				starcore._RegMsgCallBack_P(new StarMsgCallBackInterface(){
					public Object Invoke(int ServiceGroupID, int uMes, Object wParam, Object lParam){
						if (uMes == starcore._Getint("MSG_DISPMSG") || uMes == starcore._Getint("MSG_DISPLUAMSG") )
						{
							final String Str = (String)wParam;
							UIHandler.post(new Runnable() {
								public void run() {
									textbox.setText(Str);
								}
							});
						}
						return null;
					}
				});
		        
		        SrvGroup = (StarSrvGroupClass)Service._Get("_ServiceGroup");
		        Service._CheckPassword(false);
		        
		        /*----run python code----*/		
		        SrvGroup._InitRaw("python",Service);
		        StarObjectClass python = Service._ImportRawContext("python","",false,"");
		        python._Call("import", "sys");
	
		        StarObjectClass pythonSys = python._GetObject("sys");
		        StarObjectClass pythonPath = (StarObjectClass)pythonSys._Get("path");
		        pythonPath._Call("insert",0,PackagePath + "/files/python2.7.zip");
		        pythonPath._Call("insert",0,LibPath);
		 	
                //--enter message loop
	            while (true)
	            {
	                while (starcore._SRPDispatch(false) == true) ;
	                starcore._SRPUnLock();
	                try{
	                    Thread.sleep(10);
	                }
	                catch(Exception x)
	                {
	                	
	                }
	                starcore._SRPLock();
	            }
            }
        }).start();

3.捕获python脚本的输出

  为了捕获python脚本的输出结果,需要注册CLE的回调函数,在回调函数中,将输出结果显示到文本框中,由于回调函数运行在初始化CLE的线程中,因此需要使用Handler。



starcore._RegMsgCallBack_P(new StarMsgCallBackInterface(){
    public Object Invoke(int ServiceGroupID, int uMes, Object wParam, Object lParam){
    	if (uMes == starcore._Getint("MSG_DISPMSG") || uMes == starcore._Getint("MSG_DISPLUAMSG") )
    	{
		    final String Str = (String)wParam;
		    UIHandler.post(new Runnable() {
			    public void run() {
				    textbox.setText(Str);
			    }
		    });
	    }
	    return null;
    }
});

4. 执行python脚本


4.1 编译不执行

  可以只编译脚本,不执行,此时检查脚本中的语法错误。编译脚本需要调用CLE的函数_PreCompile。该函数返回一个object[]数组,如果object[0]是true,则成功编译;如果为false, 并且object[1]的长度为0,表示输入脚本不完整;否则object[1]返回编译错误。



compilebtn.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				textbox.setText("");
				String script = editbox.getText().toString();
				if (script.length() == 0)
					return;
				starcore._SRPLock();
				Object[] result = SrvGroup._PreCompile("python", script+"\n");
				starcore._SRPUnLock();
				if ((Boolean) result[0] == true)
					textbox.setText("success");
				else
				{
					if (((String)result[1]).length() == 0)
						textbox.setText("More Input");
					else
						textbox.setText((String)result[1]);
				}
			}
		});

4.2 直接执行脚本

  调用CLE的函数_RunScript执行python脚本。脚本的输出会被之前设置的回调函数捕获,显示到输出窗口中。



runbtn.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				textbox.setText("");
				String script = editbox.getText().toString();
				if (script.length() == 0)
					return;
				starcore._SRPLock();
				Service._RunScript("python", script + "\n", "", "");
				starcore._SRPUnLock();
			}
		});