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

Writing an Android GUI using C++: Introduction

4.68/5 (13 votes)
14 Mar 2013CPOL2 min read 97.5K  
Using CLE and the Wrapandroid project, programmers can call Android classes through interfaces of CLE more easily.

Introduction

For some reason, we might want to write Android applications using native code. In this case, the NDK will be used. But using only NDK is not enough. Because Android only exports Java interface to programmers, so applications can not call Android classes directly. How do we solve this problem? Using CLE and the Wrapandroid project, programmers can call Android classes through interfaces of CLE more easily.

The article is an introduction to write Android GUI applications using CLE and Wrapandroid. CLE is a middleware for programming using multiple languages, which supports Java, Python, C/C++, Lua, etc., and can be extended to support other languages. Objects, for example, instances of classes, will be maintained in the kernel. And then, CLE provides a common interface to multiple languages. We can call an object’s function, get or set object’s attributes, capture object’s events, etc.

Wrapandroid wraps android classes with CLE objects, which enables programmers to use android classes in their applications. In normal case, the steps is illustrated below:

  1. Create an object of an Android class using MallocObjectL of the CLE interface.
  2. Call object’s functions using ScriptCall method.
  3. Override object’s function using CreateOvlFunction method
  4. Hook object’s event using RegEventFunction method

Step 1: Prepare environment

  1. CLE may be installed from the network by the application automatically, you only need to include starcore_android_r6.jar in the project. The file is in starcore_devfiles_r6.zip, which can be download from http://code.google.com/p/cle-for-android.
  2. Wrapandroid has lib files: wrapandroid.jar, which can be download from here.

Step 2: Begin programming 

We will be using Eclipse and NDK to develop the application. How to install these, please refer to other related articles. Android version should be above 2.2.

  1. Open Eclipse, create a new Android project named “introduction”.
  2. CLE may be installed from the network when the application is started; in this case, the following permissions should be added:
  3. XML
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  4. Copy the Java libraries starcore_android_r6.jar and wrapandroid.jar to the project directory and add them into the project:
  5. Edit IntroductionActivity.java, change as follows loading the native share library.
  6. Java
    import com.srplab.wrapandroid.*;
    public class IntroductionActivity 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/"+getPackageName()+"/lib/libCode.so");
        }
    }
  7. CLE may also be included in the project, in this case, you should copy the share libraries of CLE to the directory of the project:
  8. And change the download flag to false:

    Java
    public class IntroductionActivity extends WrapAndroidActivity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            DownloadFromNetFlag = false;
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            StarActivity._Call("DoFile","",
              "/data/data/"+getPackageName()+"/lib/libCode.so");
        }
    }
  9. Edit layout: main.xml.
  10. XML
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/widget73"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
        <TextView
            android:id="@+id/widget45"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello" /> 
        <Button
            android:id="@+id/widget74"
            android:layout_width="220dp"
            android:layout_height="48dp"
            android:text="thank for your use"
            android:typeface="serif"
            android:textStyle="bold"
            android:textColor="#ffff0000"
            android:layout_x="284dp"
            android:layout_y="220dp"
            android:textSize="16dp"
        />        
    </LinearLayout>
  11. Create a jni directory under the project, copy header files of Wrapandroid and CLE into the jni directory. Create new files named code.cpp and Android.mk, and edit Android.mk as follows:
  12.  LOCAL_PATH := $(call my-dir)
    <pre>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 SRPWrapAndroidEngine_UUIDDef.cpp
    LOCAL_SRC_FILES := ${MODULE_CXXSRCS}
    LOCAL_LDLIBS := cle_files/libs/armeabi/libstarlib.a
    LOCAL_MODULE  := Code
    include $(BUILD_SHARED_LIBRARY)  
    #------------------------
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES := cle_files/so/armeabi/libstarcore.so
    LOCAL_MODULE  := starcore
    include $(PREBUILT_SHARED_LIBRARY)  
    #------------------------
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES := cle_files/so/armeabi/libstar_java.so
    LOCAL_MODULE  := star_java
    include $(PREBUILT_SHARED_LIBRARY)

Step 3. Code.cpp

Write native code in Android, we should compile the source file into a share library. The share library exposes two functions:

C++
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
void StarCoreService_Term(class ClassOfStarCore *starcore)

The first function will be called when the library is loaded, which can be used to do some initializing. StarCoreService_Term will be called before the share library is unloaded. 

The code for code.cpp is listed below. We will see it in detail:

C++
//--header file of wrap android
<pre>#include "SRPWrapAndroidEngine_VSClass.h"
//--service interface, other function calls will be done through this interface.
static class ClassOfSRPInterface *SRPInterface;
//--Activity object of android, which is created by previous java code.
static void *StarActivity;
//--button event function. Each event has the same prototype.
static VS_INT32 MyButton_onClick(VS_ULONG FunctionChoice,void *EventPara)
{
    //--when the event is triggered, we show some information using android Toast.
    //--Create instance of toast.
    void *toast = SRPInterface->MallocObjectL(&VSOBJID_ToastClass,0,NULL);
    //--call toast function "makeText", (si) is input parameter type and return type. please refer to CLE documents.
    SRPInterface -> ScriptCall(toast,NULL,"makeText","(si)","Button is click", 0);
    //--call toast function "show"
    SRPInterface -> ScriptCall(toast,NULL,"show","()");
    return 0;
}
static VS_INT32 MyButton1_onClick(VS_ULONG FunctionChoice,void *EventPara)
{
    void *toast = SRPInterface->MallocObjectL(&VSOBJID_ToastClass,0,NULL);
    SRPInterface -> ScriptCall(toast,NULL,"makeText","(si)","Button is click", 0);
    SRPInterface -> ScriptCall(toast,NULL,"show","()");
    return 0;
}
//--share library init function.
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
{
    class ClassOfBasicSRPInterface *BasicSRPInterface;
    
    //--get basic interface of CLE
    BasicSRPInterface = starcore ->GetBasicInterface();    
    //---get current service interface, the service is create by java code.
    SRPInterface = BasicSRPInterface ->GetSRPInterface(
      BasicSRPInterface->QueryActiveService(NULL),"","");
    
    void *ActivityClass;
    //---get wrapped acvitity
    ActivityClass = SRPInterface -> GetObjectEx(NULL,"ActivityClass");
    //--call getCurrent function to get current activity,
    // which is created by java code. ()O means the return type is CLE object;
    StarActivity = (void *)SRPInterface -> ScriptCall(ActivityClass,NULL,"getCurrent","()O");
    //--show some infotmation.
    SRPInterface -> Print("Get Main Activity = %s", SRPInterface -> GetName(StarActivity));    
    //--call activity function getResource to obtained resource id of above layout.
    // Input parameter is a string and output is an integer, so here we use (s)I tag.
    int widget45 = SRPInterface -> ScriptCall(StarActivity,NULL,
      "getResource","(s)i","id/widget45");
   //--Call findViewById function of activity, which is a textview. For this function, we should provide
   // class name as input parameter, which is little different from android function.
   // Input parameter is string and integer and return type is CLE object. So, here uses tag (si)o.
    void *MyText = (void *)SRPInterface -> ScriptCall(StarActivity,NULL,
      "findViewById","(si)o","TextViewClass",widget45);
    //--Call setText of textview object.
    SRPInterface -> ScriptCall(MyText,NULL,"setText",
      "(s)","TextViewClass","from layout");
    int widget74 = SRPInterface -> ScriptCall(StarActivity,NULL,
      "getResource","(s)i","id/widget74");
    //--Call findViewById function of activity to get button object.
    void *MyButton = (void *)SRPInterface -> ScriptCall(StarActivity,NULL,
      "findViewById","(si)o","ButtonClass",widget74);
    //--Call setText function of button object.
    SRPInterface -> ScriptCall(MyButton,NULL,"setText","(s)","click me");
    //--Call setOnClickListener function of button object.
    SRPInterface -> ScriptCall(MyButton,NULL,"setOnClickListener","()");
    //--register event function of button object.
    SRPInterface -> RegEventFunction(MyButton,&VSOUTEVENTID_ViewClass_onClick,MyButton,(void *)MyButton_onClick,0);
    
    int widget73 = SRPInterface -> ScriptCall(StarActivity,NULL,
      "getResource","(s)i","id/widget73");
    //--Call findViewById function of activity to get LinearLayout.
    void *MyLinearLayout = (void *)SRPInterface -> ScriptCall(
      StarActivity,NULL,"findViewById","(si)o","LinearLayoutClass",widget73);
    //--Dynamically create a button, which as a child of linear out.
    void *MyDynaButton = SRPInterface->MallocObject(MyLinearLayout,
      VSATTRINDEX_VIEWGROUPCLASS_VIEWQUEUE,&VSOBJID_ButtonClass,0,NULL);
    SRPInterface -> ScriptCall(MyDynaButton,NULL,"setText","(s)","created dynamically");
    SRPInterface -> ScriptCall(MyDynaButton,NULL,"setOnClickListener","()");
    SRPInterface -> RegEventFunction(MyDynaButton,
      &VSOUTEVENTID_ViewClass_onClick,MyDynaButton,(void *)MyButton1_onClick,0);
    //--set layout parameters of button created.
    SRPInterface -> ScriptCall(MyDynaButton,NULL,"setLinearLayoutParams","(ii)",100,50);
    return VS_TRUE;
}
void StarCoreService_Term(class ClassOfStarCore *starcore)
{
    SRPInterface -> Release();
    return;
}

Step 4. Compile to share library

Result:

The example can be download from: http://wrapandroid-for-multilanguage.googlecode.com/svn/wiki/examples/c_introduction.zip.

License

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