Unterstützung von C++-Bibliotheken

Der NDK unterstützt mehrere C++-Laufzeitbibliotheken. In diesem Dokument finden Sie Informationen zu diesen Bibliotheken, den damit verbundenen Nachteilen und ihrer Verwendung.

C++-Laufzeitbibliotheken

Tabelle 1 Laufzeiten und Features von NDK C++.

Name Funktionen
libc++ Moderne C++-Unterstützung.
system new und delete. (In r18 verworfen.)
Keine Keine Header, eingeschränkt in C++.

libc++ ist sowohl als statische als auch als gemeinsam genutzte Bibliothek verfügbar.

libc++

libc++ von LLVM ist die C++-Standardbibliothek, die seit Lollipop vom Android-Betriebssystem verwendet wird. Seit NDK r18 ist sie die einzige STL-Bibliothek, die im NDK verfügbar ist.

CMake verwendet standardmäßig die Version von C++ Clang, die standardmäßig verwendet wird (derzeit C++14). Legen Sie daher den standardmäßigen CMAKE_CXX_STANDARD in der Datei CMakeLists.txt auf den entsprechenden Wert fest, um Funktionen in C++17 oder höher verwenden zu können. Weitere Informationen finden Sie in der CMake-Dokumentation zu CMAKE_CXX_STANDARD.

„ndk-build“ lässt die Entscheidung standardmäßig auch „clang“. Nutzer von „ndk-build“ sollten also APP_CPPFLAGS verwenden, um -std=c++17 oder einen beliebigen anderen Wert hinzuzufügen.

Die gemeinsam genutzte Bibliothek für libc++ ist libc++_shared.so und die statische Bibliothek libc++_static.a. In der Regel übernimmt und verpackt das Build-System diese Bibliotheken nach Bedarf für den Nutzer. Informationen zu ungewöhnlichen Fällen oder zur Implementierung Ihres eigenen Build-Systems finden Sie im Handbuch für Build-Systemadministratoren oder im Leitfaden zur Verwendung anderer Build-Systeme.

Das LLVM-Projekt unterliegt der Apache-Lizenz v2.0 mit LLVM-Ausnahmen. Weitere Informationen findest du in der Lizenzdatei.

Infotainmentsystem

Die Systemlaufzeit bezieht sich auf /system/lib/libstdc++.so. Diese Bibliothek ist nicht mit dem voll funktionsfähigen libstdc++ von GNU zu verwechseln. Unter Android ist libstdc++ nur new und delete. Verwenden Sie libc++, um eine voll funktionsfähige C++-Standardbibliothek zu erhalten.

Die C++-Systemlaufzeit unterstützt die grundlegende C++-Laufzeit ABI. Im Wesentlichen stellt diese Bibliothek new und delete bereit. Im Gegensatz zu den anderen im NDK verfügbaren Optionen werden die Ausnahmebehandlung oder RTTI nicht unterstützt.

Abgesehen von den C++-Wrappern für die C-Bibliotheksheader wie <cstdio> wird keine Standardbibliothek unterstützt. Für STL-Dateien sollten Sie eine der anderen Optionen auf dieser Seite verwenden.

Keine

Es ist auch möglich, keine STL festzulegen. Es gibt in diesem Fall keine Verknüpfungs- oder Lizenzierungsanforderungen. Es sind keine C++-Standardheader verfügbar.

C++-Laufzeit auswählen

CMake

Der Standardwert für CMake ist c++_static.

Sie können c++_shared, c++_static, none oder system mithilfe der Variable ANDROID_STL in der Datei build.gradle auf Modulebene angeben. Weitere Informationen finden Sie in der Dokumentation zu ANDROID_STL in CMake.

NDK-Build

Der Standardwert für „ndk-build“ ist none.

Sie können c++_shared, c++_static, none oder system mithilfe der Variable APP_STL in der Datei Application.mk angeben. Beispiele:

APP_STL := c++_shared

Mit „ndk-build“ können Sie nur eine Laufzeit für Ihre Anwendung auswählen, dies ist nur in Application.mk möglich.

Umgangssprache verwenden

Wenn Sie Clang direkt in Ihrem eigenen Build-System verwenden, verwendet clang++ standardmäßig c++_shared. Wenn Sie die statische Variante verwenden möchten, fügen Sie den Verknüpfungs-Flags -static-libstdc++ hinzu. Obwohl die Option aus historischen Gründen den Namen "libstdc++" verwendet, gilt dies auch für libc++.

Wichtige Hinweise

Statische Laufzeiten

Wenn sich der gesamte native Code Ihrer Anwendung in einer einzigen gemeinsam genutzten Bibliothek befindet, empfehlen wir die Verwendung der statischen Laufzeit. Auf diese Weise kann die Verknüpfung so viel nicht verwendeten Code wie möglich einbetten und bereinigen, was zu einer optimierten und kleinstmöglichen Anwendung führt. Außerdem werden PackageManager- und Dynamic Linker-Fehler in alten Android-Versionen vermieden, die die Verarbeitung mehrerer gemeinsam genutzter Bibliotheken erschweren und zu Fehlern führen.

In C++ ist es jedoch nicht sicher, mehr als eine Kopie derselben Funktion oder desselben Objekts in einem einzigen Programm zu definieren. Dies ist ein Aspekt der One Definition Rule (One-Definition-Regel), die im C++-Standard vorhanden ist.

Wenn Sie eine statische Laufzeit (und im Allgemeinen statische Bibliotheken) verwenden, kann es leicht passieren, dass diese Regel versehentlich außer Kraft gesetzt wird. Die folgende Anwendung verstößt beispielsweise gegen diese Regel:

# Application.mk
APP_STL := c++_static
# Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.cpp
LOCAL_SHARED_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)

In diesem Fall ist der STL einschließlich der globalen Daten und statischen Konstruktoren in beiden Bibliotheken vorhanden. Das Laufzeitverhalten dieser Anwendung ist nicht definiert und in der Praxis kommt es sehr häufig zu Abstürzen. Andere mögliche Probleme sind:

  • Arbeitsspeicher, der in einer Bibliothek zugewiesen und in der anderen freigegeben wird, wodurch Speicherlecks oder Heap-Beschädigungen verursacht werden.
  • In libfoo.so ausgelöste Ausnahmen werden in libbar.so nicht erfasst, wodurch Ihre App abstürzt.
  • Die Zwischenspeicherung von std::cout funktioniert nicht richtig.

Abgesehen von den Verhaltensproblemen wird durch die Verknüpfung der statischen Laufzeit mit mehreren Bibliotheken der Code in jeder gemeinsam genutzten Bibliothek dupliziert, was die Größe Ihrer Anwendung erhöht.

Im Allgemeinen können Sie eine statische Variante der C++-Laufzeit nur verwenden, wenn Ihre Anwendung nur eine einzige gemeinsam genutzte Bibliothek enthält.

Gemeinsame Laufzeiten

Wenn Ihre Anwendung mehrere gemeinsam genutzte Bibliotheken enthält, sollten Sie libc++_shared.so verwenden.

Unter Android ist die vom NDK verwendete libc++ nicht identisch mit der im Betriebssystem. Damit haben NDK-Nutzer auch dann Zugriff auf die neuesten libc++-Funktionen und Fehlerkorrekturen, wenn sie auf ältere Android-Versionen ausgerichtet sind. Der Nachteil: Wenn Sie libc++_shared.so verwenden, müssen Sie es in Ihre App einbinden. Wenn Sie Ihre Anwendung mit Gradle erstellen, erfolgt dies automatisch.

Ältere Android-Versionen enthielten Fehler im PackageManager und in der dynamischen Verknüpfung, die dazu führten, dass die Installation, Aktualisierung und das Laden nativer Bibliotheken unzuverlässig waren. Wenn Ihre App auf eine ältere Android-Version als Android 4.3 (Android API-Level 18) ausgerichtet ist und Sie libc++_shared.so verwenden, müssen Sie die gemeinsam genutzte Bibliothek vor jeder anderen abhängigen Bibliothek laden.

Das Projekt ReLinker bietet Behelfslösungen für alle bekannten Probleme beim Laden nativer Bibliotheken und ist normalerweise eine bessere Wahl, als eigene Problemumgehungen zu entwickeln.

Eine STL pro App

In der Vergangenheit hat der NDK neben libc++ auch GNU libstdc++ und STLport unterstützt. Wenn Ihre Anwendung von vordefinierten Bibliotheken abhängig ist, die auf einem anderen NDK als der zum Erstellen der Anwendung verwendeten NDK erstellt wurden, müssen Sie dafür sorgen, dass dies auf kompatible Weise erfolgt.

Eine Anwendung sollte nicht mehr als eine C++-Laufzeit verwenden. Die verschiedenen STLs sind nicht miteinander kompatibel. Beispielsweise unterscheidet sich das Layout von std::string in libc++ mit dem in gnustl. Code, der für einen STL geschrieben wird, kann keine Objekte verwenden, die gegen eine andere STL geschrieben wurden. Dies ist nur ein Beispiel. Es gibt zahlreiche Inkompatibilitäten.

Diese Regel geht über Ihren Code hinaus. Alle Abhängigkeiten müssen denselben STL verwenden, den Sie ausgewählt haben. Wenn Sie auf eine Closed-Source-Drittanbieterabhängigkeit angewiesen sind, die den STL verwendet und keine Bibliothek pro STL bereitstellt, haben Sie keine Auswahlmöglichkeit für STL. Sie müssen denselben STL-Prozess wie Ihre Abhängigkeit verwenden.

Es ist möglich, dass Sie von zwei gegenseitig inkompatiblen Bibliotheken abhängig sind. In diesem Fall besteht die einzige Lösung darin, eine der Abhängigkeiten zu löschen oder den Maintainer zu bitten, eine Bibliothek bereitzustellen, die mit dem anderen STL erstellt wurde.

C++-Ausnahmen

C++-Ausnahmen werden von libc++ unterstützt, sind aber in ndk-build standardmäßig deaktiviert. Das liegt daran, dass C++-Ausnahmen in der Vergangenheit im NDK nicht verfügbar waren. Bei CMake- und eigenständigen Toolchains sind C++-Ausnahmen standardmäßig aktiviert.

Wenn Sie Ausnahmen für Ihre gesamte Anwendung in „ndk-build“ aktivieren möchten, fügen Sie der Datei Application.mk die folgende Zeile hinzu:

APP_CPPFLAGS := -fexceptions

Um Ausnahmen für ein einzelnes ndk-build-Modul zu aktivieren, fügen Sie dem jeweiligen Modul in seiner Android.mk die folgende Zeile hinzu:

LOCAL_CPP_FEATURES := exceptions

Alternativ können Sie auch Folgendes verwenden:

LOCAL_CPPFLAGS := -fexceptions

RTT

Wie in Ausnahmefällen wird RTTI von libc++ unterstützt, ist aber in ndk-build standardmäßig deaktiviert. Bei CMake- und eigenständigen Toolchains ist RTTI standardmäßig aktiviert.

Um RTTI für Ihre gesamte Anwendung in ndk-build zu aktivieren, fügen Sie der Datei Application.mk die folgende Zeile hinzu:

APP_CPPFLAGS := -frtti

Um RTTI für ein einzelnes ndk-build-Modul zu aktivieren, fügen Sie dem jeweiligen Modul in Android.mk die folgende Zeile hinzu:

LOCAL_CPP_FEATURES := rtti

Alternativ können Sie auch Folgendes verwenden:

LOCAL_CPPFLAGS := -frtti