Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Let Android support multiple programming languages

5.00/5 (2 votes)
13 Apr 2012CPOL3 min read 16.3K  
Can we write programs in Android with other languages? The answer is yes.

Introduction 

Java is used as the main language on Android to write applications. Although NDK is provided, it is difficult to write native GUI applications. Can we write programs with other languages? The answer is yes. We can wrap Android Java classes to expose to special languages, and then uses that language to develop applications. The work should be done once instead of doing it for each language again and again. Using the middleware named CLE (Common Language Extension), we can achieve this. Using CLE, all the work needed to be done to encapsulate Android classes into CLE objects is done, then multiple languages are supported through the CLE interface.

Using a figure to illustrate, as shown below:

 

Wrapping an Android class into a CLE object, there are four steps 

1. Create CLE service and object description.

2. Implement Object’s OnCreate and OnDestroy event. In the procedure of the OnCreate event, we create and record a corresponding instance of the Android class, and in the OnDestroy event, we free the instance.
3. In the handler of the OnCreate event, after creating the Android instance, we add the event listeners to it. When the listener is triggered, we generate the CLE event, and the event will be routed by CLE to the application layer.
4. Write the interface functions for each function. We first get the recorded Android instance and then call the corresponding functions of the instance.

A simple example

Based on the support of CLE, the work is relatively simple. In this process, we can use Ecllipse and the Java language to write and test. After finishing, the result can be called with lua, Python, or native C++.

Here we use a simple example to explain the above procedure in detail. In this example, we encapsulate a button class.

Wrapping the Android Button class 

1. Create a CLE service and object description, we can do this with srpdebug tools.

XML
<!--?xml version="1.0" encoding="utf-8" ?-->
<?xml version="1.0" encoding="utf-8" ?>
<service ID="0d0af7b9-32f3-4708-ab91-c32f57e15dca" Password="123" Name="WrapAndroidService">
    <sysrootitem>
        <BasicServiceItem ID="c15c5dfa-253f-4198-b1c3-34473487fb90" NameID="cb0338e8-e22f-48c0-931e-01b97502e374">
            <object>
                <AndroidBaseClass ID="e51b7626-160e-485e-b4ee-1f37686736dd">
                    <attribute>
                        <AndroidRefCount Type="VS_INT32" SyncFlag="1" />
                    </attribute>
                    <function>
                        <incAndroidRef ID="b4b43eb0-ec45-44aa-8af7-93b3f09a847c" />
                        <decAndroidRef ID="d7f5f007-b86f-4fd9-bc8f-86e1e5285e3b" />
                        <getActivity ID="e183d80f-1544-447a-928a-83702f475e0d">
                            <output Type="VS_OBJPTR" />
                        </getActivity>
                    </function>
                </AndroidBaseClass>
                <ViewClass ID="2f669713-d0e6-484b-9473-5c50afd8bbd2" Class="AndroidBaseClass" />
                <ActivityClass ID="16371687-dc7e-4177-a7cc-76fd118c9c3c" Class="AndroidBaseClass">
                    <attribute>
                        <ViewGroupQueue Type="void *" Class="AbsoluteLayoutClass" />
                    </attribute>
                    <function>
                        <runScript ID="1d7668e4-b7e2-4c85-99cd-3cdb11ce5438">
                            <input Name="scriptInterface" Type="VS_CHAR *" />
                            <input Name="script" Type="VS_CHAR *" />
                            <output Type="VS_BOOL" />
                        </runScript>
                        <DoFile ID="542be0a5-bd21-4765-b004-5e336587367c">
                            <input Name="scriptInterface" Type="VS_CHAR *" />
                            <input Name="path" Type="VS_CHAR *" />
                            <output Type="VS_BOOL" />
                        </DoFile>
                        <DoAssetsFile ID="e894f038-1776-47c3-a6ec-802e5b86403c">
                            <input Name="scriptInterface" Type="VS_CHAR *" />
                            <input Name="path" Type="VS_CHAR *" />
                            <output Type="VS_BOOL" />
                        </DoAssetsFile>
                        <pushActivity ID="b8b252ec-e3e7-4194-a557-4f98a5e43c22">
                            <input Name="activity" Type="VS_OBJPTR" />
                        </pushActivity>
                        <popActivity ID="e024762e-cd83-4032-917b-9984bd91d725">
                            <output Type="VS_OBJPTR" />
                        </popActivity>
                        <getCurrent ID="cfcabd6e-028d-45b4-9e34-ebe7dc4c56b6">
                            <output Type="VS_OBJPTR" />
                        </getCurrent>
                    </function>
                </ActivityClass>
                <AbsoluteLayoutClass ID="155920e1-fdaa-489f-b4e9-4e3ac4cf5d7e" Class="ViewClass">
                    <attribute>
                        <ViewQueue Type="void *" Class="ViewClass" />
                    </attribute>
                </AbsoluteLayoutClass>
                <ButtonClass ID="f12ab1a2-2694-4165-8b4f-889b8f9aa872" Class="ViewClass">
                    <function>
                        <setText ID="1c6e8c3b-42db-41b9-988d-b7fb540b93be">
                            <input Name="text" Type="VS_CHAR *" />
                        </setText>
                    </function>
                    <outevent>
                        <onClick ID="328adf46-b65a-49d4-a10a-bd308552de05" />
                    </outevent>
                </ButtonClass>
            </object>
        </BasicServiceItem>
    </sysrootitem>
</service>  

2. OnCreate event of the button

Java
/* _OnCreate Event */
public Object[] _OnCreate(Hashtable Ev){
    StarObjectClass self = (StarObjectClass)Ev.get("_DesObject");
    StarObjectClass parent = (StarObjectClass)self._Get("_Parent");
    StarObjectClass activity = (StarObjectClass)self._Call("getActivity");   
    /* create android button instance */
    StarCLEButton button = new StarCLEButton((Activity)WrapAndroidClass.GetAndroidObject(activity,"AndroidObject"),self);  
    /* record android button instance */
    WrapAndroidClass.SetAndroidObject(self,"AndroidObject",(Object)button);
    /* set android button instance’s parent */
    if( parent != null ){
        if( activity == parent ){
            Activity android_activity = (Activity)WrapAndroidClass.GetAndroidObject(parent,"AndroidObject");
            android_activity.setContentView(button);
        }else{
            ViewGroup android_viewgroup = (ViewGroup)WrapAndroidClass.GetAndroidObject(parent,"AndroidObject");
            android_viewgroup.addView(button);
        }
        self._LockGC();
        self._Call("decAndroidRef");  // release with parent
    }
    /* Add event listener */
    button.setOnClickListener(new View.OnClickListener(){ 
        public void onClick(View v) {
            StarObjectClass self = ((BasicAndroidInterface)v).getBasicAndroid().getStarObject();
            /* trigger CLE object’s event onClick */
            self._ProcessEvent("onClick");
        } 
    });         
    return null;
}   

3. Set Text function of Button

Java
public void setText(StarObjectClass self,String Text){
    /*get recorded android instance*/
    TextView textview = (TextView)WrapAndroidClass.GetAndroidObject(self,"AndroidObject");
    if( textview != null )
        /*call android instance’s function setText*/
        textview.setText(Text);
}

4. Export to jar files: “wrapandroid.jar”

Finish the above steps, we can obtain jar files wrapandroid.jar and service description files WrapAndroidService.xml. And then we can write programs with other languages using Eclipse.

Programming with lua 

a. Create Android project named “myluaexample”

b. Add Java build library 

Copy “wrapandroid.jar” and “starcore_android_r5.jar” to your project directory. Add jar library to your project. 

c. Copy “WrapAndroidService.xml” to assets directory of your project

d. Add Permissions

XML
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

e. Create the code.lua file 

Create the code.lua file in the asserts directory, input text

SrvGroup = libstarcore._GetSrvGroup()
Service = SrvGroup:_GetService("","")
--get activity
StarActivity = Service.ActivityClass.getCurrent();
--create AbsoluteLayout        
MyLayout = Service.AbsoluteLayoutClass:_New(StarActivity);
--create Button
MyButton = Service.ButtonClass:_New(MyLayout);
MyButton:setText("Hello");
function MyButton:onClick(Event)
    self:setText("Is Clicked");
end

f. Edit “MyluaexampleActivity” as follows:

Java
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.main);
        StarActivity._Call("DoAssetsFile", "lua", "code.lua"); 
} 

Programming with Python 

a. Install SL4A

b. Create Android project named “mypythonexample”

c. Create code.py file 

Create the code.py file in the asserts directory, input text

SrvGroup = libstarpy._GetSrvGroup()
Service = SrvGroup._GetService("","")
#--get activity
StarActivity = Service.ActivityClass.getCurrent();
#--create AbsoluteLayout        
MyLayout = Service.AbsoluteLayoutClass._New(StarActivity);
#--create Button
MyButton = Service.ButtonClass._New(MyLayout);
MyButton.setText("Hello");
def MyButton_onClick(self,Event) :
    self.setText("Is Clicked");
MyButton.onClick = MyButton_onClick 

d. Edit “MypythonexampleActivity”, as follows:

Java
public class MypythonexampleActivity extends WrapAndroidActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        StarActivity._Call("DoAssetsFile", "python", "code.py");
    }
}

Other steps are same as the lua example.

Programming with native C 

a. Create Android project named “mycexample”

b. Create header files 

Create the jni directory, enter jni, and create header files from XML file, using command:

star2h ..\assets\WrapAndroidService.xml 

c. Create the Code.cpp source file

C++
#include "WrapAndroidService_VSDHeader.h"
static class ClassOfSRPInterface *SRPInterface;
static void *StarActivity;
static VS_INT32 MyButton_onClick(VS_ULONG FunctionChoice,void *EventPara)
{
    VS_EVENTPARAM *EventParam;
    EventParam = (VS_EVENTPARAM *)EventPara;
    SRPInterface -> ScriptCall(EventParam->SrcObject,NULL,"setText","(s)","Is Clicked");
    return 0;
}
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
{
    class ClassOfBasicSRPInterface *BasicSRPInterface;
    //--init star core
    BasicSRPInterface = starcore ->GetBasicInterface();
    SRPInterface = BasicSRPInterface ->GetSRPInterface(BasicSRPInterface->QueryActiveService(NULL),"","");
    void *ActivityClass;
    ActivityClass = SRPInterface -> GetObjectEx(NULL,"ActivityClass");
    StarActivity = (void *)SRPInterface -> ScriptCall(ActivityClass,NULL,"getCurrent","()O");
    SRPInterface -> Print("Get Main Activity = %s", SRPInterface -> GetName(StarActivity));
    //--create AbsoluteLayout
    void *MyLayout = SRPInterface->MallocObject(StarActivity,VSATTRINDEX_ACTIVITYCLASS_VIEWGROUPQUEUE,&VSOBJID_AbsoluteLayoutClass,0,NULL);
    //--create Button
    void *MyButton = SRPInterface->MallocObject(MyLayout,VSATTRINDEX_ABSOLUTELAYOUTCLASS_VIEWQUEUE,&VSOBJID_ButtonClass,0,NULL);
    SRPInterface -> ScriptCall(MyButton,NULL,"setText","(s)","Hello");
    SRPInterface -> RegEventFunction(MyButton,&VSOUTEVENTID_ButtonClass_onClick,MyButton,(void *)MyButton_onClick,0);
    return VS_TRUE;
}
void StarCoreService_Term(class ClassOfStarCore *starcore)
{
    SRPInterface -> Release();
    return;
}  

d. Create Android.mk.

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Here we give our module name and sourcefile(s)
LOCAL_CFLAGS += -Wno-write-strings -DENV_ANDROID
LOCAL_CPPFLAGS += -Wno-write-strings -fexceptions -DENV_ANDROID
LOCAL_LDFLAGS += -Wno-write-strings -DENV_ANDROID
LOCAL_C_INCLUDES += cle_files/include
#--------source file
MODULE_CXXSRCS := Code.cpp WrapAndroidService_UUIDDef.cpp
LOCAL_SRC_FILES := ${MODULE_CXXSRCS}
LOCAL_LDLIBS := cle_files/libs/armeabi/libstarlib.a
LOCAL_MODULE := Code
include $(BUILD_SHARED_LIBRARY)  

e. Edit “MycexampleActivity” as follows:

Java
public class MycexampleActivity extends WrapAndroidActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        StarActivity._Call("DoFile","","/data/data/com.srplab.mycexample/lib/libCode.so");
    }
} 

The example can be download from:
http://www.srplab.com/android/wrapandroid.rar. An Open Source project http://code.google.com/p/wrapandroid-for-multilanguage is in progress. Anyone who has interest can join the project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)