跳轉到內容

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/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. 參考資料

- 在此頁面上評論

華夏公益教科書