使用 C++ 编写 Android GUI: 入门
使用 CLE 和 Wrapandroid 项目,程序员可以通过 CLE 的接口更轻松地调用 Android 类。
引言
由于某种原因,我们可能希望使用原生代码编写 Android 应用程序。在这种情况下,将使用 NDK。但仅使用 NDK 是不够的。因为 Android 仅向程序员导出 Java 接口,所以应用程序无法直接调用 Android 类。我们如何解决这个问题?使用 CLE 和 Wrapandroid 项目,程序员可以通过 CLE 的接口更轻松地调用 Android 类。
本文介绍了使用 CLE 和 Wrapandroid 编写 Android GUI 应用程序。 CLE 是一个用于使用多种语言编程的中间件,支持 Java、Python、C/C++、Lua 等,并且可以扩展以支持其他语言。例如,类的实例等对象将被维护在内核中。然后,CLE 提供了一个通用的多语言接口。我们可以调用一个对象的函数,获取或设置对象的属性,捕获对象的事件等。
Wrapandroid 使用 CLE 对象包装 Android 类,这使程序员能够在他们的应用程序中使用 Android 类。通常情况下,步骤如下所示
- 使用 CLE 接口的
MallocObjectL
创建一个 Android 类的对象。 - 使用
ScriptCall
方法调用对象的函数。 - 使用
CreateOvlFunction
方法覆盖对象的函数 - 使用
RegEventFunction
方法挂钩对象的事件
步骤 1:准备环境
- CLE 可以由应用程序从网络自动安装,您只需要在项目中包含 starcore_android_r6.jar。该文件位于 starcore_devfiles_r6.zip 中,可以从 http://code.google.com/p/cle-for-android 下载。
- Wrapandroid 有库文件:wrapandroid.jar,可以从 这里 下载。
步骤 2:开始编程
我们将使用 Eclipse 和 NDK 来开发应用程序。如何安装这些,请参考其他相关文章。 Android 版本应高于 2.2。
- 打开 Eclipse,创建一个名为“introduction”的新 Android 项目。
- CLE 可以在应用程序启动时从网络安装;在这种情况下,应添加以下权限
- 将 Java 库 starcore_android_r6.jar 和 wrapandroid.jar 复制到项目目录并将它们添加到项目中
- 编辑 IntroductionActivity.java,更改为以下内容以加载原生共享库。
- CLE 也可以包含在项目中,在这种情况下,您应该将 CLE 的共享库复制到项目目录中
- 编辑布局:main.xml。
- 在项目下创建一个 jni 目录,将 Wrapandroid 和 CLE 的头文件复制到 jni 目录中。创建名为 code.cpp 和 Android.mk 的新文件,并按如下所示编辑 Android.mk
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
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");
}
}
并将下载标志更改为 false
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");
}
}
<?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>
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)
步骤 3. Code.cpp
在 Android 中编写原生代码,我们应该将源文件编译成共享库。共享库公开两个函数
VS_BOOL StarCoreService_Init(class ClassOfStarCore *starcore)
void StarCoreService_Term(class ClassOfStarCore *starcore)
第一个函数将在加载库时被调用,可以用于进行一些初始化。 StarCoreService_Term
将在卸载共享库之前被调用。
code.cpp 的代码如下所示。 我们将详细了解它
//--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;
}
步骤 4. 编译到共享库
结果
示例可以从以下位置下载: http://wrapandroid-for-multilanguage.googlecode.com/svn/wiki/examples/c_introduction.zip。