当前位置: 首页 > 知识库问答 >
问题:

使用JavaCPP在Java Android应用程序中运行C++代码

周峻
2023-03-14
dependencies {
    implementation 'org.bytedeco:javacpp:1.5.4'
}

以便在我的应用程序中使用它。我正在测试一个原生C++Android Studio模板项目。

我已经在GitHub上上传了完整的项目供参考:https://GitHub.com/jacobkrieg10/javacppnewexample

还有,下面是我认为需要注意的项目中的相关文件:

nativelibrary.java:

package com.example.javacplusplus;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="NativeLibrary.h")
@Namespace("NativeLibrary")
public class NativeLibrary {
    public static class NativeClass extends Pointer {
        static { Loader.load(); }
        public NativeClass() { allocate(); }
        private native void allocate();

        // to call the getter and setter functions
        public native @StdString String get_property();
        public native void set_property(String property);

        // to access the member variable directly
        public native @StdString String property();
        public native void property(String property);
    }
}
#ifndef NATIVELIBRARY_H
#define NATIVELIBRARY_H

#include <string>

namespace NativeLibrary {
    class NativeClass {
    public:
        const std::string& get_property();
        void set_property(const std::string& property);
        std::string property;
    };
}

#endif // NATIVELIBRARY_H
#include "NativeLibrary.h"

namespace NativeLibrary {

const std::string& NativeClass::get_property() { return property; }
void NativeClass::set_property(const std::string& property) { this->property = property; }

} // namespace NativeLibrary

cmakelists.txt:

cmake_minimum_required(VERSION 3.4.1)

add_library(
        native-lib

        SHARED

        NativeLibrary.cpp
        native-lib.cpp)

find_library(
        log-lib

        log)

target_link_libraries(
        native-lib

        ${log-lib})

注意,Native-lib.cpp只是模板中使用的C++文件,我现在忽略它。

build.gradle(对应于app模块):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"
    defaultConfig {
        applicationId "com.example.javacplusplus"
        minSdkVersion 28
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'org.bytedeco:javacpp:1.5.4'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.3.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
package com.example.javacplusplus;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
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);

        // Pointer objects allocated in Java get deallocated once they become unreachable,
        // but C++ destructors can still be called in a timely fashion with Pointer.deallocate()
        NativeLibrary.NativeClass l = new NativeLibrary.NativeClass();
        l.set_property("Hello World!");

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

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}
    --------- beginning of crash
2020-11-16 00:57:38.557 13506-13506/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.javacppapp, PID: 13506
    java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/base.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_resources_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/base.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_dependencies_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_resources_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_0_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_1_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_2_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_3_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_4_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_5_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_6_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_7_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_8_apk.apk!/lib/x86, /data/app/com.example.javacppapp-8V-wRac6X4RLpYvvYEmtGQ==/split_lib_slice_9_apk.apk!/lib/x86, /system/lib]]] couldn't find "libjniNativeLibrary.so"
        at java.lang.Runtime.loadLibrary0(Runtime.java:1012)
        at java.lang.System.loadLibrary(System.java:1669)
        at org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:1683)
        at org.bytedeco.javacpp.Loader.load(Loader.java:1300)
        at org.bytedeco.javacpp.Loader.load(Loader.java:1123)
        at com.example.javacppapp.NativeLibrary$NativeClass.<clinit>(NativeLibrary.java:10)
        at com.example.javacppapp.MainActivity.onCreate(MainActivity.java:22)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
2020-11-16 00:57:38.557 13506-13506/? E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="NativeLibrary.h")
@Namespace("NativeLibrary")
public class NativeLibrary {
    public static class NativeClass extends Pointer {
        static { Loader.load(); }
        public NativeClass() { allocate(); }
        private native void allocate();

        // to call the getter and setter functions 
        public native @StdString String get_property();
        public native void set_property(String property);

        // to access the member variable directly
        public native @StdString String property();
        public native void property(String property);
    }

    public static void main(String[] args) {
        // Pointer objects allocated in Java get deallocated once they become unreachable,
        // but C++ destructors can still be called in a timely fashion with Pointer.deallocate()
        NativeClass l = new NativeClass();
        l.set_property("Hello World!");
        System.out.println(l.property());
    }
}
$ javac -cp javacpp.jar NativeLibrary.java
$ java -jar javacpp.jar NativeLibrary
$ java -cp javacpp.jar NativeLibrary
$ Hello World!

有人能帮我吗?有人知道为什么找不到libjninativelibrary.so吗?是否需要添加一些内容才能完成设置?也许libjninativelibrary.so需要以某种方式生成?

我已经将构建插件平台插件添加到项目的build.gradle文件中;这就是现在的样子:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.1"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

plugins {
    id 'java-library'
    id 'java-gradle-plugin'
    id 'org.bytedeco.gradle-javacpp-build' version "$javacppVersion"
    id 'org.bytedeco.gradle-javacpp-platform' version "$javacppVersion"
}

ext {
    javacppPlatform = 'android-arm64' // or any other platform, defaults to Loader.getPlatform()
}

dependencies {
    implementation gradleApi()
    api "org.bytedeco:javacpp:$javacppVersion"
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

// Note: Had to comment this as I was getting the error > Cannot add task 'clean' as a task with that name already exists.
//task clean(type: Delete) {
//    delete rootProject.buildDir
//}

tasks.withType(org.bytedeco.gradle.javacpp.BuildTask) {
    // set here default values for all build tasks below, typically just includePath and linkPath,
    // but also properties to set the path to the NDK and its compiler in the case of Android
}

javacppBuildCommand {
    // typically set here the buildCommand to the script that fills up includePath and linkPath
}

javacppBuildParser {
    // typically set here the classOrPackageNames to class names implementing InfoMap
}

javacppBuildCompiler {
    // typically set here boolean flags like copyLibs
}

项目同步时没有错误,但当我运行它时,我得到:

> Task :javacppBuildParser FAILED
Execution failed for task ':javacppBuildParser'.
> app/build/intermediates/javac/debug/classes/com/example/myjavacppapp/BuildConfig (wrong name: com/example/myjavacppapp/BuildConfig)
android {
    applicationVariants.all { variant ->
        variant.javaCompiler.doLast {
            println 'javacpp ' + variant.name
            javaexec {
                main 'org.bytedeco.javacpp.tools.Builder'
                classpath '/home/jacob/Work/other/sandbox/java/javacpp3/app/libs/javacpp.jar'
                args '-cp', variant.javaCompiler.destinationDir,
                        '-properties', 'android-arm',
                        '-Dplatform.root=/home/jacob/.android/sdk/ndk/21.1.6352462',
                        '-Dplatform.compiler=/home/jacob/.android/sdk/ndk/21.1.6352462/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++',
                        '-Dplatform.includepath=/home/jacob/.android/sdk/ndk/21.1.6352462/sources/cxx-stl/gnu-libstdc++/include:src/main/cpp',
                        '-Dplatform.linkpath=/home/jacob/.android/sdk/ndk/21.1.6352462/sources/cxx-stl/llvm-libc++/libs/arm64-v8a',
                        '-d', 'libs/armeabi'
            }
            println 'javacpp done'
        }
    }

    sourceSets.main {
        jniLibs.srcDir 'libs'
        jni.srcDirs = [] // disable automatic ndk-build call
    }
}
> Task :app:compileDebugJavaWithJavac FAILED
javacpp debug
Exception in thread "main" java.lang.NoClassDefFoundError: androidx/appcompat/app/AppCompatActivity
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at org.bytedeco.javacpp.tools.UserClassLoader.findClass(UserClassLoader.java:72)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.bytedeco.javacpp.tools.ClassScanner.addClass(ClassScanner.java:61)
    at org.bytedeco.javacpp.tools.ClassScanner.addMatchingFile(ClassScanner.java:75)
    at org.bytedeco.javacpp.tools.ClassScanner.addMatchingDir(ClassScanner.java:87)
    at org.bytedeco.javacpp.tools.ClassScanner.addMatchingDir(ClassScanner.java:85)
    at org.bytedeco.javacpp.tools.ClassScanner.addMatchingDir(ClassScanner.java:85)
    at org.bytedeco.javacpp.tools.ClassScanner.addMatchingDir(ClassScanner.java:85)
    at org.bytedeco.javacpp.tools.ClassScanner.addPackage(ClassScanner.java:99)
    at org.bytedeco.javacpp.tools.Builder.classesOrPackages(Builder.java:672)
    at org.bytedeco.javacpp.tools.Builder.main(Builder.java:962)
Caused by: java.lang.ClassNotFoundException: androidx.appcompat.app.AppCompatActivity
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at org.bytedeco.javacpp.tools.UserClassLoader.findClass(UserClassLoader.java:72)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 23 more

FAILURE: Build failed with an exception.

* Where:
Build file '/home/jacob/Work/other/sandbox/java/javacpp3/app/build.gradle' line: 48

* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Process 'command '/home/jacob/.android/studio/jre/bin/java'' finished with non-zero exit value 1

知道我该怎么解决吗?

共有1个答案

梁丘亦
2023-03-14

我在这里发布了一个完整的工作示例:
https://github.com/bytedeco/sample-projects/tree/master/gradle-javacpp-android

作为参考,我们可以通过以下步骤复制此项目:

  1. 按照https://developer.android.com/studio/projects/add-native-code
  2. 的说明
  3. app/build.gradle文件中添加如下内容(在以通常的方式应用org.bytedeco.gradle-javacpp-build插件之后),以及
android.applicationVariants.all { variant ->
    def variantName = variant.name.capitalize() // either "Debug" or "Release"
    def javaCompile = project.tasks.getByName("compile${variantName}JavaWithJavac")
    def generateJson = project.tasks.getByName("generateJsonModel$variantName")

    // Compiles NativeLibraryConfig.java
    task "javacppCompileJava$variantName"(type: JavaCompile) {
        include 'com/example/myapplication/NativeLibraryConfig.java'
        source = javaCompile.source
        classpath = javaCompile.classpath
        destinationDir = javaCompile.destinationDir
    }

    // Parses NativeLibrary.h and outputs NativeLibrary.java
    task "javacppBuildParser$variantName"(type: org.bytedeco.gradle.javacpp.BuildTask) {
        dependsOn "javacppCompileJava$variantName"
        classPath = [javaCompile.destinationDir]
        includePath =  ["$projectDir/src/main/cpp/"]
        classOrPackageNames = ['com.example.myapplication.NativeLibraryConfig']
        outputDirectory = file("$projectDir/src/main/java/")
    }

    // Compiles NativeLibrary.java and everything else
    javaCompile.dependsOn "javacppBuildParser$variantName"

    // Generates jnijavacpp.cpp and jniNativeLibrary.cpp
    task "javacppBuildCompiler$variantName"(type: org.bytedeco.gradle.javacpp.BuildTask) {
        dependsOn javaCompile
        classPath = [javaCompile.destinationDir]
        classOrPackageNames = ['com.example.myapplication.NativeLibrary']
        compile = false
        deleteJniFiles = false
        outputDirectory = file("$projectDir/src/main/cpp/")
    }

    // Picks up the C++ files listed in CMakeLists.txt
    generateJson.dependsOn "javacppBuildCompiler$variantName"
}
 类似资料:
  • 问题内容: 我有一个现有的Flask应用程序,并且想找到通往另一个应用程序的路线。更具体地说,第二个应用程序是Plotly Dash应用程序。如何在现有的Flask应用程序中运行Dash应用程序? 我还尝试将路由添加到Dash实例,因为它是Flask应用程序,但出现错误: 问题答案: 从文档: 基本的Flask应用程序可从访问app.server。 你还可以将自己的Flask应用实例传递到Dash

  • 我试图在SpringMVC中运行SpringBoot应用程序,在SpringMVCPOM中添加SpringBoot应用程序依赖项,并扫描SpringBoot包,但我面临以下问题

  • 问题内容: 我想知道如何在Java中运行PHP代码。使用ScriptEngine,我可以运行JavaScript: 为此,我导入了库。我相信要运行PHP,我必须导入一个类似的库,并将上面代码的第三行更改为extension 。不幸的是,我不知道这是哪个库。我已经用Google搜索并找到了答案,并遇到了PHP / Java Bridge库,但是我认为这并不是我要找的,因为它专注于通过PHP运行Jav

  • 问题内容: 我想使用html applet标记在我的Web应用程序中运行简单applet,但是它给出了类似的错误 java.lang.ClassNotFoundException:MyApplet 请给我示例应用程序,如果可能的话..... 问题答案: 问题是applet引擎在您定义的代码库中找不到MyApplet类。 这是因为您在/ WEB-INF / classes目录中进行了分类。该目录受s

  • 问题内容: 因此,我正在使用C#开发一个应用程序以从Web读取一些信息,但这是一个Windows窗体应用程序。我得到一个JSON字符串,我需要从中获取一些信息。我知道有一个用于C#Web应用程序的库,但是如何在Windows窗体应用程序中使用呢? 问题答案: 那里有许多JSON库。您可以考虑以下三个建议: Json.NET ,一个相当流行的JSON(反序列化)库。 ****根据 ServiceSt

  • 作为一个软件开发人员,我需要做哪些步骤来允许我的JavaFX应用程序与JDK11+一起运行?有什么好的广告吗?是否可以通过Gradle获得OpenJDK?