Android Studio - 找不到本机方法 - 在构建中包含.so文件

时间:2014-03-21 20:16:54

标签: android android-ndk android-gradle

我从NDK开始,想用它编译我的第一个hello-world应用程序。

我的应用程序只是一个带有Activity的简单应用程序,而我的MainActivity位于com.example.myapplication2.app

我想在其中使用原生方法,这就是我所做的:

MainActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        TextView  tv = new TextView(this);
        tv.setText(stringFromJNI());
        setContentView(tv);
    }

    public native String  stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }

在我的jni文件夹中:

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := all

您好-jni.c

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

JNIEXPORT jstring JNICALL   Java_com_example_myapplication2_app_MainActivity_stringFromJNI(JNIEnv* env, jobject  thiz)
{
    return (*env)->NewStringUTF(env, "Hello from native code!");
}

在这里,我的朋友:

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }

    sourceSets.main {
        jniLibs.srcDir 'src/main/libs'
        jni.srcDirs = [] //disable automatic ndk-build call
    }

    productFlavors {
        x86 {
            versionCode Integer.parseInt("6" + defaultConfig.versionCode)
            ndk {
                abiFilter "x86"
            }
        }
        mips {
            versionCode Integer.parseInt("4" + defaultConfig.versionCode)
            ndk {
                abiFilter "mips"
            }
        }
        armv7 {
            versionCode Integer.parseInt("2" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi-v7a"
            }
        }
        arm {
            versionCode Integer.parseInt("1" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi"
            }
        }
        fat
    }
}

def getVersionCodeFromManifest() {
    def manifestFile = file(android.sourceSets.main.manifest.srcFile)
    def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
    def matcher = pattern.matcher(manifestFile.getText())
    matcher.find()
    return Integer.parseInt(matcher.group(1))
}

task copyNativeLibs(type: Copy, dependsOn: 'buildNative') {
    // TODO fix deprecated
    dependsOn 'buildNative'
    from(new File('src/main/libs')) { include '**/*.so' }
    into new File(buildDir, 'native-libs')
}

tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }

clean.dependsOn 'cleanCopyNativeLibs'

tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
    pkgTask.jniFolders = new HashSet<File>();
    pkgTask.jniFolders.add(new File(projectDir, 'native-libs'))
}

task buildNative(type: Exec) {
    if (System.env.ANDROID_NDK != null) {
        def ndkBuild = new File(System.env.ANDROID_NDK, 'ndk-build')
        workingDir "src/main/jni"
        commandLine 'cmd', '/c', '%ANDROID_NDK%\\ndk-build'
        //executable ndkBuild
    } else {
        doLast {
            println '##################'
            println 'Skipping NDK build'
            println 'Reason: ANDROID_NDK not set.'
            println '##################'
        }
    }
}

task nativeLibsToJar(
        type: Zip,
        description: 'create a jar archive of the native libs') {
    destinationDir file("$buildDir/native-libs")
    baseName 'native-libs'
    extension 'jar'
    from fileTree(dir: 'libs', include: '**/*.so')
    into 'lib/'
}

dependencies {
    compile 'com.android.support:appcompat-v7:+'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

当我想运行我的项目时,我没有C错误(我之前有过,这就是为什么我确定编译已完成,然后确定)。

但是,当我想运行应用程序时,我有错误:

  

java.lang.UnsatisfiedLinkError:找不到本机方法:com.example.myapplication2.app.MainActivity.stringFromJNI :()Ljava / lang / String;

我检查过的是(Android NDK Native method not found error):

  • com.example.myapplication2.app.MainActivity.stringFromJNI和Java_com_example_myapplication2_app_MainActivity_stringFromJNI匹配
  • extern&#34; C&#34;不存在,因为当我使用它时,我遇到了这个错误:Error: "expected '(' before string constant"

你知道为什么我会遇到这个错误吗?

2 个答案:

答案 0 :(得分:3)

向您的应用启动添加System.loadLibrary("hello-jni")来电。静态构造函数将是一个好地方。

答案 1 :(得分:3)

错误发生在gradle文件中,该文件没有做任何事情。

这是我的最终.gradle文件。

在编译之前,它会在编译之前添加三个任务来执行以下操作:

  1. 运行NDK构建
  2. 将* .so文件从/ src / main / libs复制到/ build / lib
  3. 将/ build / lib文件夹压缩到/libs/lib.jar
  4. 要求:

    1. JDK的bin文件夹必须位于PATH环境变量
    2. 您必须拥有一个ANDROID_NDK环境变量,其中包含NDK的路径(在Android网站上下载的路径)
    3. 然后,它会将所有必需的.so文件包含到您的应用程序中,而您的gradle中不需要任何ndk命令。

      它的优点:适用于所有平台的一个APK 不方便:APK更大,因为它包含所有平台的.so文件。

      申请插件:'android'

      android {
          compileSdkVersion 19
          buildToolsVersion "19.0.3"
      
          defaultConfig {
              minSdkVersion 8
              targetSdkVersion 19
              versionCode 1
              versionName "1.0"
              ndk {
                  moduleName "hello-jni"
              }
          }
          buildTypes {
              release {
                  runProguard false
                  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
              }
          }
      
          sourceSets.main {
              jniLibs.srcDir 'src/main/libs'
              jni.srcDirs = [] //disable automatic ndk-build call
          }
      }
      
      task buildNative(type: Exec) {
          if (System.env.ANDROID_NDK != null) {
              println 'Running NDK build'
              workingDir "src/main"
              commandLine 'cmd', '/c', '%ANDROID_NDK%/ndk-build'
          } else {
              doLast {
                  println '##################'
                  println 'Skipping NDK build'
                  println 'Reason: ANDROID_NDK not set.'
                  println '##################'
              }
          }
      }
      
      task copyNativeLibs(dependsOn:buildNative, type: Copy) {
          println 'Copying *.so files from /src/main/libs to /build/lib'
          from(new File('src/main/libs')) { include '**/*.so' }
          into new File(buildDir, 'lib')
      }
      
      task nativeLibsToJar(dependsOn:copyNativeLibs, type: Exec, description: 'create a jar archive of the native libs') {
          println 'Compressing /build/lib into /libs/lib.jar'
          workingDir "build"
          commandLine 'cmd', '/c', 'jar cf ../libs/lib.jar lib'
      }
      
      tasks.withType(Compile) { compileTask -> compileTask.dependsOn nativeLibsToJar }
      
      clean.dependsOn 'cleanCopyNativeLibs'
      
      tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
          pkgTask.jniFolders = new HashSet<File>();
          pkgTask.jniFolders.add(new File(projectDir, 'native-libs'))
      }
      
      dependencies {
          compile 'com.android.support:appcompat-v7:+'
          compile fileTree(dir: 'libs', include: ['*.jar'])
      }