跳转到内容

OpenGL 编程/安装/Android NDK

来自维基教科书,开放世界中的开放书籍
在 Galaxy S 上运行的茶壶

我们的 GLUT 教程可以使用提供的简单包装器在 Android 上运行。

注意:包装器将集成到下一个官方 FreeGLUT(版本 3.0)中!

制作过程

[编辑 | 编辑源代码]

要了解 GLUT 包装器在内部的工作原理,请参阅 Android GLUT 包装器

依赖项

[编辑 | 编辑源代码]

首先,您需要一个最小的 Java 开发环境

sudo apt-get install openjdk-7-jdk ant

然后从 Android 开发者 获取 Android NDK r9d,以编译用于 Android 的 C/C++ 代码。

最后,您需要安装 Android API 级别 10,从同一网站获取 Android SDK 并使用android图形工具来安装它。

程序本身可能还需要 GLM 和 FreeType 库 - 请参阅下面的专用部分。

模拟器(缺少)对 OpenGL ES 2.0 的支持

[编辑 | 编辑源代码]

Android 模拟器从 2012 年 4 月开始仅支持 OpenGL ES 2.0,需要特定的模拟器配置和系统映像,并且似乎并非在所有平台上都能正常工作。

还要注意,“API 演示”应用程序提供了一个“OpenGL ES 2.0”示例,如果 2.0 不可用,它会静默且令人困惑地回退到 OpenGL ES 1.0,因此这不是一个很好的测试来查看是否支持 OpenGL ES 2.0。

在 Android 上使用 支持设备 实验 OpenGL 2.0 仍然是最好的选择。

官方文档:https://developer.android.com.cn/tools/devices/emulator.html#accel-graphics

使用 USB 连接

[编辑 | 编辑源代码]

当您通过 USB 连接设备时,可以使用 adb 命令(来自 Android SDK)浏览文件系统、安装应用程序、调试它们等。

$ adb devices
List of devices attached 
4520412B47C0207D	device

使用我们的包装器

[编辑 | 编辑源代码]

在本维基教科书中,示例基于 GLUT 库。

由于 GLUT 尚未移植到 Android,因此我们为 Android 编写了一个简单的 GLUT 兼容包装器(请参阅 代码库)。

注意:包装器仍处于早期阶段,可能会在不久的将来发生变化。

编译教程代码

[编辑 | 编辑源代码]

查看“android_wrapper/”目录。

  • 通过 USB 插入您的设备(智能手机、平板电脑...)。
  • 将 Android 工具添加到您的 PATH,例如
export PATH="$PATH:/usr/src/android-sdk-linux/tools:/usr/src/android-sdk-linux/platform-tools:/usr/src/android-ndk-r9d"
  • jni/ 目录中,使 src 成为您需要编译的 GLUT 代码的符号链接(例如 ln -nfs ../../tut02_clean src)。
  • 使 assets 成为您正在编译的教程的符号链接(例如 ln -s jni/src assets)。
  • 现在您可以键入
make clean; make && make install
  • 您将在设备上获得一个“OpenGL 维基教科书”应用程序,可以立即运行!

要使您的应用程序全屏,请在您的 AndroidManifest.xml 中添加此属性

<application
  ...
  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
>

默认的 Android 键盘没有 F1/F2/F3 等键。

相反,您可以使用 Hacker's Keyboard,这是一种具有更多键的替代输入法

确保您的应用程序在 AndroidManifest.xml 中定义了 android:icon

    <application ...
		 android:icon="@drawable/icon"

创建两个图标

  • res/drawable/icon.png (48x48)
  • res/drawable-hdpi/icon.png (72x72)

现在您的应用程序将在启动器上有一个自定义图标。

浏览标准输出

[编辑 | 编辑源代码]

如果您想查看程序的标准输出(stdout 和 stderr),您需要将它们重定向到系统日志

adb shell root
adb shell stop
adb shell setprop log.redirect-stdio true
adb shell start  # this may restart your Android session

要检查日志文件,您可以使用

  • 命令:adb logcat
  • ddms 实用程序,它具有图形 GUI 来浏览日志
  • Eclipse,它嵌入了一个类似于 DDMS 的 LogCat 查看器

检查 JNI 调用

[编辑 | 编辑源代码]

以下操作将在从 C/C++ 调用 JNI 时开启更多检查

adb shell stop
adb shell setprop dalvik.vm.checkjni true
adb shell start

您将在系统日志中获得额外的跟踪信息,并且 JNI 对其接受的内容将更加严格。

GDB 也可以启用。

注意:在 NDKr7 中,在 Android.mk 中使用“stabs”格式进行调试符号,否则 GDB 将显示错误的源代码行 [1]

LOCAL_CXXFLAGS  := -gstabs+


GDB 需要调试版本,在构建 C++ 代码时添加 NDK_DEBUG=1

ndk-build NDK_DEBUG=1

在启动 gdb 时,确保您的 AndroidManifest.xml 提到了它是可调试的,否则 gdb 的行为会很糟糕(缺乏线程信息、崩溃等)

    <application ...
            android:hasCode="true" android:debuggable="true"

gdb-server 需要几秒钟才能在设备上启动,因此您的程序将在它被调试器暂停之前开始运行。一个解决方法是在您的 android_main 函数中添加一个等待

  sleep(5);

要启动调试会话,请键入

ndk-gdb --start

无法加载原生库

[编辑 | 编辑源代码]

如果您遇到以下错误:

E/AndroidRuntime( 3021): java.lang.RuntimeException: Unable to start activity
ComponentInfo{org.wikibooks.OpenGL/android.app.NativeActivity}: java.lang.IllegalArgumentException:
Unable to load native library: /data/data/org.wikibook.OpenGL/lib/libnative-activity.so

系统无法加载您的 .so 文件,原因是底层问题。

要获取更多信息,您需要创建一个手动加载库的最小 Java 应用程序

  • src/com/example/test_native_activity/Main.java
package com.example.test_native_activity;

import android.app.Activity;
import android.os.Bundle;

public class Main extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.loadLibrary("native-activity");
    }
}
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test_native_activity"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="7" />

    <application android:label="test_native_activity">
        <activity android:name=".Main">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

编译、安装并准备它

android update project --name test_native_activity --path . --target "android-10"
ant debug
ant installd

adb shell
su -c bash
cd /data/data/
cp -a org.wikibooks.OpenGL/lib/libnative-activity.so com.example.test_native_activity/lib/

运行此应用程序时,您将在 Android 日志中获得更精确的错误,例如错误的 STL 实现

E/AndroidRuntime(3009): java.lang.UnsatisfiedLinkError: Cannot load library:  reloc_library[1311]:
2323 cannot locate '_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc'...

或缺少依赖项

E/AndroidRuntime( 3327): java.lang.UnsatisfiedLinkError: Cannot load library:link_image[1962]:
2323 could not load needed library 'libglut.so.3' for 'libnative-activity.so'
(load_library[1104]: Library 'libglut.so.3' not found)


在最糟糕的情况下,库甚至可能无法正确加载。例如,当全局静态变量的 C++ 构造函数在库加载时崩溃时,即使在应用程序启动之前也会发生这种情况。您需要在 C 级重新生成库加载

#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char* argv[]) {
  const char* err = NULL;
  const char* filename = "/data/data/org.wikibooks.OpenGL/lib/libnative-activity.so";

  if (argc == 2)
    filename = argv[1];

  printf("Clearing errors: "); fflush(stdout);
  err = dlerror();
  printf("%s\n", (err == NULL) ? "OK" : err); fflush(stdout);

  printf("Loading library: "); fflush(stdout);
  void* handle = dlopen(filename, RTLD_LAZY);
  err = dlerror();
  printf("%s\n", (err == NULL) ? "OK" : err); fflush(stdout);
  
  if (handle != NULL) {
    printf("Loading symbol: "); fflush(stdout);
    dlsym(handle, "ANativeActivity_onCreate");
    err = dlerror();
    printf("%s\n", (err == NULL) ? "OK" : err); fflush(stdout);
  }
}

然后将其发送到设备并执行它

$ arm-linux-androideabi-gcc test-dlsym.c
$ adb push a.out /
$ adb shell
# /a.out
Clearing errors: OK
Loading library: OK
Loading symbol: OK

您也可以使用 strace 获取更多精度

# strace /a.out


默认情况下,Android 没有 ldd,但您可以使用以下方法模拟它:

arm-linux-androideabi-objdump -x libs/armeabi/libnative-activity.so | grep NEEDED
# or
arm-linux-androideabi-readelf -d libs/armeabi/libnative-activity.so | grep NEEDED

抽象 OpenGL 和 GLES2 之间的差异

[编辑 | 编辑源代码]

当您只使用 GLES2 函数时,您的应用程序几乎可以移植到台式机和移动设备。还有一些问题需要解决

  • GLSL 的 #version 不同
  • GLES2 需要与 OpenGL 2.1 不兼容的精度提示。

有关详细信息和建议的解决方案,请参阅基本教程 02 和 03。

要安装 GLM,您只需要将最新版本解压缩到 jni/glm 中(这样 jni/glm/glm.hpp 就存在)。它是一个仅包含头文件的库,不需要单独编译。

  • http://lonesock.net/soil.html 下载 Simple OpenGL Image Library
  • 应用来自android_wrapper/soil.patch.
  • 的补丁按照.
  • projects/Android/Makefile中的说明进行操作.

NDK 模块已准备好在

src/build/soil/

FreeType

[编辑 | 编辑源代码]

/usr/src/android-ndk-r8c/build/tools/make-standalone-toolchain.sh \
  --platform=android-14 --install-dir=/usr/src/ndk-standalone-14-arm --arch=arm
NDK_STANDALONE=/usr/src/ndk-standalone-14-arm
PATH=$NDK_STANDALONE/bin:$PATH

如果您需要 FreeType(一个渲染字体的库),您需要交叉编译它。注意:Android 系统使用 FreeType,但内部它不将其公开给原生应用程序。

tar xf freetype-2.6.tar.bz2
cd freetype-2.6/
# For simplicity, use bundled zlib, and avoid harfbuzz cyclic dependency
./configure --host=arm-linux-androideabi --prefix=/freetype --without-zlib --with-png=no --with-harfbuzz=no
make -j$(nproc)
make install DESTDIR=$(pwd)

首先,从 NDK 准备交叉编译器

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := freetype
LOCAL_SRC_FILES := lib/libfreetype.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/include/freetype2

include $(PREBUILT_STATIC_LIBRARY)

然后使用它交叉编译 freetype

然后在新的 freetype/ 目录中编写一个 Android.mk 文件

有关详细信息,请参阅 NDK 中的 docs/STANDALONE-TOOLCHAIN.htmldocs/PREBUILTS.htmlCLEAR_VARS 部分没有记录,但对于避免路径混淆是必要的;它用于 native_app_glue NDK 模块。

LOCAL_STATIC_LIBRARIES := freetype

(或者,如果您不打算使用 NDK 构建系统,则可以在 --prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr 中安装它。)

要在您的项目中使用 FreeType,请编辑您的 Android.mk

故障排除

[编辑 | 编辑源代码]

设备标记为脱机

[编辑 | 编辑源代码]您第一次将设备连接到新计算机时,它会要求您确认计算机指纹。在您接受设备屏幕上的确认之前,它会被标记为脱机。如果您没有被要求确认,请确保您的

adb

是最新的。

允许非 root 用户访问 USB[编辑 | 编辑源代码]如今,Android 设备在您的

/lib/udev/rules.d/

  • 中被引用,非特权用户现在可以安全地连接到设备。
  • 如果 adb 在您的新设备上遇到权限问题查看以 root 身份运行 adb 时是否有效(以检查是否为权限问题,或者其他问题)。安装android-tools-adb.
  • 软件包,其中包含

/lib/udev/rules.d/70-android-tools-adb.rules

usb 2-1: New USB device found, idVendor=18d1, idProduct=4e22

如果不起作用,请手动创建一个 udev 规则,如下所示。

# Galaxy S
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0666", GROUP="plugdev" 

首先,通过在插入设备后键入“dmesg”来确定设备的 idVendor;检查是否有 

然后创建一个如下所示的 udev 规则,例如在 /etc/udev/rules.d/51-android.rules 中 

/etc/init.d/udev restart

(这可以通过 idProduct 进一步细化。)

# ll /dev/bus/usb/002/
total 0
crw-rw-r-T 1 root plugdev 189, 140 janv. 19 21:50 013

然后重新启动 udev 守护进程

如果您插入设备,USB 字符设备应该有“plugdev”组
  1. 参考资料

- 在此页面上评论

华夏公益教科书