65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C++ 编写 Android GUI: 第 3 部分:调试

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (8投票s)

2012年6月13日

CPOL

4分钟阅读

viewsIcon

48071

downloadIcon

1473

本文介绍了如何使用 NDK-debug 工具调试 C++ 代码,并提供了一些关于 Android C++ 编程的建议。如果应用程序是用 Java 编写的,程序员可以使用 Eclipse 来调试代码、设置断点并逐行跟踪。

引言

本文介绍了如何使用 NDK 调试工具调试 C++ 代码,并提供了一些关于 Android C++ 编程的建议。如果应用程序是用 Java 编写的,程序员可以使用 Eclipse 来调试代码、设置断点并逐行跟踪。那么用 C++ 编写时,我们如何调试呢?目前,对于 Android 2.2 及以上版本,NDK 提供了一个 NDK 调试工具,该工具运行在 Linux 上,可用于调试源代码。

调试环境可能因平台而异。此处提供的步骤基于我的主机环境,即 Windows XP、Android-ndk-r6b,并使用 Cygwin 来为 Android 构建 C++ 代码。Android-ndk-r6b 和 Cygwin 的安装超出了本文的范围。您可以参考其他资料。

要调试的示例代码是前一篇文章中提供的列表视图持有者。假设您已经准备好了环境,并且 C++ 代码可以成功地为 Android 构建。

项目更改

  1. 修改 AndroidManifest.xml 以启用 gdbserver。

    根据 NDK 的文档,为了调试本地代码,AndroidManifest.xml 应添加标志 android:debuggable="true",如下所示。

    <application
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name" 
         android:debuggable="true" >
        <activity
            android:label="@string/app_name"
            android:name=".DebugActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application> 
  2. 使用调试标志“NDK_DEBUG=1”重新编译项目。

  3. 从 eclipse 运行项目。

使用 ndk-debug 进行调试 

切换到 Cygwin 控制台 

  1. 如下所示运行 ndk-debug

    可能会有很多警告。但不必担心,它们不会影响调试过程。

  2. 键入 list 命令,您可以看到源文件。

    • 使用 b 命令设置断点。
    • 使用 c 命令继续运行。
  3. 移动设备响应可能会变慢,请等待设备可以正常操作。

    如果设备长时间没有响应,可以键入 CTRL+C 退出 ndk-debug 并重复以上步骤。

  4. 单击按钮,源代码将在之前设置的断点处中断。

在 Cygwin 控制台中,查看变量值时,可能会看到“值已优化掉”。

在这种情况下,应将 –Oo 标志添加到 Android.mk 文件,重新生成项目并重新启动。

LOCAL_CFLAGS += -Wno-write-strings -O0 -DENV_ANDROID
LOCAL_CPPFLAGS += -Wno-write-strings -O0 -fexceptions -DENV_ANDROID
LOCAL_LDFLAGS += -Wno-write-strings -O0 -DENV_ANDROID 

在模拟器上调试的效率可能较低,响应速度慢。更好的选择是购买一部便宜的手机或掌上设备,并使用真实设备进行调试。在 Android 上调试 C++ 代码不是一件容易的事,无论您是否熟悉 GDB。我认为将成熟的代码从其他平台迁移到 Android 是一个不错的选择,或者在其他平台上使用更方便的工具进行调试,而不是直接在 Android 上进行调试。

在 Win32 上调试

CLE 也支持 Windows 和 Linux,因此我们可以在 Windows 上调试程序逻辑。让我们更深入地讨论一下。要在 Windows 上进行调试,需要做很多工作,不是技术上的,而是设置 Android 类存根的工作。这项工作尚未开始,因此本文仅提供步骤来说明我们如何在 Windows 上调试 Android C++ 代码。您也可以按照本文中的示例自己编写存根。示例也包含在下载包中。C++ 代码的结果是一个共享库,在 Windows 平台下,它只能构建为动态库。因此,第一步是创建一个加载器来初始化环境并加载它。

开始之前,应从 http://www.srplab.com/data/starcore_win32.1.5.1.exe 安装 CLE for Win32。

动态库加载器

加载器初始化 CLE 环境,导入 wrapandroid 服务描述文件 SRPWrapAndroidEngine.xml,并为共享库创建一个服务。加载器可以使用 Java、Python、Lua、C#、C++ 或 CLE 支持的其他语言创建。这里我们使用 C++ 来创建。代码如下。

  1. 初始化 CLE 环境。
    class ClassOfBasicSRPInterface *BasicSRPInterface;
    VS_CORESIMPLECONTEXT Context;
    
    // init CLE environment. The first parameter Context is used to get the return value.
    BasicSRPInterface = VSCore_InitSimpleEx(&Context,0,0,NULL,0,NULL);
    if( BasicSRPInterface == NULL ){
        printf("init starcore fail\n");
        return -1;
    } 
    // import wrapandroid service description file. This file contains id and name of android classe.
    {
        FILE *hFile;
        int FileSize;
        char *xmlBufer;
        hFile = fopen("SRPWrapAndroidEngine.xml","rt");
        fseek(hFile,0,SEEK_END);
        FileSize = ftell(hFile);
        fseek(hFile,0,SEEK_SET);
        xmlBufer = (char *)malloc(FileSize+1);
        FileSize = fread(xmlBufer,1,FileSize,hFile);
        fclose(hFile);
        xmlBufer[FileSize] = 0;
        if( BasicSRPInterface ->ImportServiceFromXmlBuf(xmlBufer,false) == VS_FALSE ){
            printf("parse [SRPWrapAndroidEngine.xml] failed\n");
            return -1;
        }
        free(xmlBufer);
    }
    // create a service for share library.
    BasicSRPInterface -> CreateService( "","wrapandroid", NULL, "123",5,0,0,0,0,0 );
    // obtain the service interface.
    SRPInterface = BasicSRPInterface ->GetSRPInterface("wrapandroid","root","123");
    // set not check password flag for share library.
    SRPInterface ->CheckPassword(VS_FALSE);
  2. 创建根 Activity。

    共享库需要一个预先创建的 Activity。以下代码用于创建它。然后我们为 Activity 的 getCurrent 函数编写存根代码。这样就可以从共享库调用此函数。

    这是 getCurrent 的存根函数

    void *RootActivity;
    static void *ActivityClass_getCurrent(void *Object){
        return RootActivity;
    }

    创建根 Activity 的代码

    //---get ActivityClass defined in wrapandroid service description file.
    void *ActivityClass = SRPInterface -> GetObjectEx(NULL,"ActivityClass");
    //---change to atomic object
    void *AtomicActivityClass = SRPInterface ->ObjectToAtomic(ActivityClass);
    //---create function stub for getCurrent
    void *AtomicActivityClassFunction_getCurrent = SRPInterface ->CreateAtomicFunctionSimple(
          AtomicActivityClass,"getCurrent","VS_OBJPTR getCurrent();",NULL,NULL,VS_FALSE,VS_FALSE);
    //---set the function address
    SRPInterface -> SetAtomicFunction(AtomicActivityClassFunction_getCurrent,(void *)ActivityClass_getCurrent);
     
    //alloc root activity
    RootActivity = SRPInterface ->MallocObjectL(&VSOBJID_ActivityClass,0,NULL);  
  3. 加载共享库。

    加载共享库很简单。我们调用 CLE 接口 DoFile 函数来完成此操作,更像是脚本接口。

    if( SRPInterface ->DoFile("","../libcode.dll",NULL, NULL, VS_FALSE) == VS_FALSE ){
        printf("load library file\n");
        return -1;
    } 

编译时,应将 starlib_vcm.lib 添加到项目中,如下所示。

将头文件添加到项目中

code.cpp 上设置断点,然后运行。

为 Android 类创建存根

我们为 Android 类创建存根是为了调试或其他目的。这将使我们能够在 Windows 平台上调试 Android C++ 代码。我们如何创建存根?很简单,就像这样。

定义函数

static void *ActivityClass_getCurrent(void *Object){
    …
}

创建存根函数

void *AtomicActivityClassFunction_getCurrent = 
  SRPInterface ->CreateAtomicFunctionSimple(AtomicActivityClass,
  "getCurrent","VS_OBJPTR getCurrent();",NULL,NULL,VS_FALSE,VS_FALSE); 

并将地址分配给存根函数

SRPInterface -> SetAtomicFunction(AtomicActivityClassFunction_getCurrent,(void *)ActivityClass_getCurrent);  

如果您为 Android 类的所有函数创建了存根,您的应用程序就可以迁移到 Windows 或其他平台。

© . All rights reserved.