[JAVA] Integrieren Sie vorhandenen C / C ++ - Code in Ihre Android-Anwendung

Über diesen Artikel

Integrieren Sie vorhandenen C / C ++ - Code als native Funktion mit dem Android NDK in Ihre Android-Anwendung. Es ist im Grunde der offizielle USER GUIDE (https://developer.android.com/studio/projects/add-native-code).

Angenommene Situation

Bestehendes Projekt

Stellen Sie sich ein sehr einfaches Projekt wie dieses vor: Main.cpp hat nur die Hauptfunktion. Bei der normalen C / C ++ - Entwicklung wird davon ausgegangen, dass diese Hauptfunktion hauptsächlich als "Anwendung" fungiert. Dieses Mal wird es nicht auf der Android-Seite verwendet. Submodule hat eine Reihe von Funktionen. Dieses Mal habe ich eine Funktion namens "SubModule_funcDouble" vorbereitet, die einfach den Eingabewert verdoppelt. Unser Ziel ist es diesmal, dies als "Bibliothek" aus Java zu bezeichnen.

Projektstruktur


MyProject
├─CMakeLists.txt
├─Main.cpp
└─SubModule
   ├─CMakeLists.txt
   ├─SubModule.cpp
   └─SubModule.h

Top CMake Listen.txt


cmake_minimum_required(VERSION 3.0)
project("C_application") 

add_executable(Main "Main.cpp")

add_subdirectory(SubModule)
target_include_directories(Main PUBLIC ./SubModule)
target_link_libraries(Main SubModule)

Main.cpp


#include <stdio.h>
#include "SubModule.h"

int main()
{
	SubModule_funcDouble(10);
	return 0;
}

SubModule CMakeLists.txt


cmake_minimum_required(VERSION 3.0)
add_library(SubModule STATIC SubModule.cpp SubModule.h)

SubModule.cpp


#include <stdio.h>
#include "SubModule.h"

int SubModule_funcDouble(int a)
{
	printf("SubModule_funcDouble: %d x 2 = %d\n", a, a * 2);
	return a * 2;
}

SubModule.h


#ifndef _SUB_MODULE_H_
#define _SUB_MODULE_H_

#ifdef __cplusplus
extern "C" {
#endif
#if 0
}
#endif

int SubModule_funcDouble(int a);

#ifdef __cplusplus
}
#endif

#endif	/* _SUB_MODULE_H_ */

Erstellen Sie eine Android-Anwendung

NDK-Installation

Öffnen Sie zunächst das entsprechende Projekt in Android Studio und öffnen Sie die Menüleiste-> Extras-> SDK-Manager. Wählen Sie in den SDK-Tools "LLDB", "CMake" und "NDK" aus und klicken Sie zur Installation auf "OK".

01.png

(Sie können dies später nach dem Erstellen des Projekts tun.)

Android-Projekt generieren

Öffnen Sie Android Studio und erstellen Sie ein neues Projekt. Wählen Sie bei der Auswahl einer Vorlage "Native C ++".

03.png

Projektspeicherort

Ich wollte den Quellcode zusammen mit C / C ++ verwalten, also habe ich einen Ordner namens "AndroidApp" an derselben Stelle wie unten gezeigt erstellt und in diesem Ordner gespeichert.

Projektstruktur


MyProject
├─AndroidApp/     <--Speichern Sie hier Ihr Android-Projekt
├─CMakeLists.txt
├─Main.cpp
└─SubModule/
   ├─CMakeLists.txt
   ├─SubModule.cpp
   └─SubModule.h

Hinweis: Andere Methoden

Wenn Sie Ihren C / C ++ - Code und Ihren Android-Code separat verwalten möchten, können Sie sie an einem völlig anderen Ort speichern. Wenn Sie einen neuen C / C ++ - Code erstellen möchten, befindet sich im Android-Projekt ein CPP-Ordner. Ich denke, Sie sollten ihn dort speichern. Ich denke, dies ist wahrscheinlich die erwartete Verwendung. Angenommen, Sie möchten diesmal "vorhandenen" C / C ++ - Code verwenden, und einige Benutzer verwenden C / C ++ - Code als vorhandenes C / C ++ - Projekt, unabhängig von Android Ich habe es so gemacht. (Angenommen, in Android-Apps und C / C ++ - Projekten werden allgemeine Bibliotheksfunktionen verwendet.)

Entscheiden Sie, wie verschiedene Dateien entsprechend der Projektstruktur, den Teammitgliedern und der Betriebsmethode gespeichert werden sollen.

Hinweis: Beim Einbetten von C / C ++ in ein vorhandenes Android-Projekt

02.png

Bearbeiten von C / C ++ - Code

05.png

Mit Blick auf das generierte Projekt wird ein Ordner mit dem Namen "cpp" wie oben gezeigt erstellt und "CMakeLists.txt" und "native-lib.cpp" werden darunter erstellt. Standardmäßig wird eine Bibliotheksvorlage erstellt, die eine Funktion bereitstellt, die nur die Zeichenfolge Hello World zurückgibt.

Wenn Sie danach CMakeLists.txt ändern, sollten Sie zuerst zu Menüleiste-> Erstellen-> Reflesh Linked C ++ - Projekte wechseln.

CMakeLists bearbeiten

Wenn Sie einen neuen C / C ++ - Code erstellen möchten, können Sie diese CMakeLists auch bearbeiten. Dieses Mal importieren wir jedoch nur den vorhandenen C / C ++ - Code, um den Aufwand für Änderungen so gering wie möglich zu halten.

Wie unten gezeigt, werden am Ende nur das add_subdirectory des SubModule des vorhandenen C / C ++ - Codes, das Hinzufügen des Include-Pfads und die Link-Einstellung hinzugefügt. Die Pfadangabe bezieht sich auf diese CMakeLists.txt. Wenn Sie die Anzahl der Dateien in SubModule erhöhen oder in Zukunft ein anderes Modul aufrufen, müssen Sie dies nicht ändern. (Es sei denn, Sie erstellen eine Header-Datei, die die Abhängigkeiten durcheinander bringt.)

native-lib/CMakeLists.txt


~Abkürzung~

add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

~Abkürzung~

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

#↓ Hinzufügen
add_subdirectory(../../../../../SubModule SubModule)
target_include_directories(native-lib PUBLIC ../../../../../SubModule)
target_link_libraries(native-lib SubModule)
#↑ Hinzufügen

Bearbeiten von native-lib.cpp

Es gibt eine ursprüngliche Funktion, die nur die Zeichenfolge Hello world zurückgibt. Sie können den Namen ändern, aber verwenden Sie ihn so wie er ist.

Die Funktion, die ich dieses Mal aufrufen möchte, befindet sich in "SubModule", also habe ich im Grunde genommen die Rolle des Wrappers. Konvertierung der Java- und C / C ++ - Schnittstelle gemäß JNI. Auch diesmal ist es nur int, also ist es einfach, aber wenn sich ein Array oder Objekt in einem Argument oder einem Rückgabewert befindet, werden Sie gebeten, es ebenfalls zu konvertieren.

native-lib.cpp


#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

//↓ Hinzufügen
#include "SubModule.h"
extern "C" JNIEXPORT jint JNICALL
Java_com_example_myapplication_MainActivity_funcDouble(
        JNIEnv *env,
        jobject /* this */,
        jint a
) {
    int ret = SubModule_funcDouble(a);
    return ret;
}
//↑ Hinzufügen

Rufen Sie C / C ++ - Funktionen aus Java-Code auf

Rufen wir abschließend eine C / C ++ - Funktion aus Java-Code auf. Der Ladevorgang für Bibliotheksdateien (loadLibrary) wurde bereits automatisch generiert.

Deklarieren Sie eine native C / C ++ - Funktion. Es scheint, dass das Schreiben unten eine Methode zu sein scheint. Der Funktionsaufruf kann dann als einfacher Java-Code implementiert werden.

MainActivity.java


package com.example.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        //↓ Hinzufügen
        int ret = funcDouble(10);
        Log.i("MyApp", "funcDouble: " + ret);
        //↑ Hinzufügen
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    //↓ Hinzufügen
    public native int funcDouble(int a);
    //↑ Hinzufügen
}

Memo

printf Der printf auf der C / C ++ - Seite wurde nirgendwo ausgegeben und schien ignoriert zu werden. Da es jedoch wahrscheinlich von der Umgebung abhängt, scheint es besser zu sein, mit #ifdef __ANDRDOID__ usw. zwischen printf und __android_log_print wechseln zu können.

fopen In dem unwahrscheinlichen Fall, dass auf der C / C ++ - Seite Code zum Lesen oder Schreiben von Dateien vorhanden ist, stürzt SIGSEGV ohne Berücksichtigung ab. Unterstützung beim Lesen und Schreiben von Dateien

OpenMP Ich könnte es normal verwenden, indem ich CMakeLists.txt unter native-lib Folgendes hinzufüge. Es hat nicht funktioniert, als ich es unter SubModule an CMakeLists.txt angehängt habe. (Build-Fehler aufgetreten)

native-CMakeLists unter lib.Fügen Sie txt Folgendes hinzu


#↓ Hinzufügen
find_package(OpenMP REQUIRED)
if(OpenMP_FOUND)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
#↑ Hinzufügen

add_subdirectory(../../../../../SubModule SubModule)
target_include_directories(native-lib PUBLIC ../../../../../SubModule)
target_link_libraries(native-lib SubModule)

Es gab auch Informationen im Netz, dass es abstürzen würde, wenn es nicht vom UI-Thread aufgerufen würde, aber es scheint, dass es gut funktioniert, selbst wenn es von einem anderen als dem UI-Thread aufgerufen wird. (NDK-Version ist 19.2) Insbesondere habe ich die native Funktion im Rückruf onCameraFrame des OpenCVCameraBridgeViewBase.CvCameraViewListener aufgerufen und versucht, die for-Anweisung in OpenMP auszuführen, aber es funktionierte ohne Absturz. Es wurde auch beschleunigt.

abschließend

Diese Methode setzt einen ziemlich gezielten Anwendungsfall voraus. Es kann einen besseren Weg geben. Wenn es irgendwelche Verbesserungen gibt, wäre ich dankbar, wenn Sie mich in den Kommentaren wissen lassen könnten.

Recommended Posts

Integrieren Sie vorhandenen C / C ++ - Code in Ihre Android-Anwendung
Integrieren Sie Elasticsearch in Ihre Rails-App
Integrieren Sie Docker in Ihr Ruby on Rails-Portfolio!