使用AS生成.so文件以及调用(mac环境)

时间:2024-04-03 16:12:03

哪些应用比较适合使用NDK开发?

  • 在平台之间移植其应用
  • 重复使用现有库或者提供自己的库供重复使用
  • 在某些情况下提高性能,特别是像游戏这种计算密集型应用。

其中第二条就是因为可以将代码生成.so文件,放在其他项目中使用。

一、使用Android Studio生成.so文件的步骤:

1、首先电脑中要安装并配置NDK环境。

下载ndk并解压  https://developer.android.google.cn/ndk/downloads/revision_history

打开终端,输入open -e .bash_profile

在. bash_profile文件中编辑:

         export ANDROID_NDK_ROOT=/Users/lxy/Library/Android/android-ndk-r16b(解压出下载的ndk文件路径)

         export PATH=$PATH:$ANDROID_NDK_ROOT

2、创建一个GetNum.java文件,用于加载JNI库并定义Native方法。

package com.lxy.sky.obtainsofile;

public class GetNum {

    static {
        System.loadLibrary("GetNum");
    }

    public static native int getNum(int num);
}

3、将该文件进行编译产生GetNum.class文件。

在终端中定位到GetNum类所在的文件夹,然后进行编译,编译后会发现该文件夹中多了一个GetNum.class文件

lxydeMacBook-Pro:java lxy$ cd /Users/lxy/Documents/ObtainSoFile/app/src/main/java/com/lxy/sky/obtainsofile
lxydeMacBook-Pro:obtainsofile lxy$ javac GetNum.java

4、利用javah生成GetNum.class文件对应的native头文件,并将其移至jni文件夹下,改名为GetNum.h

lxydeMacBook-Pro:obtainsofile lxy$ cd /Users/lxy/Documents/ObtainSoFile/app/src/main/java
lxydeMacBook-Pro:java lxy$ javah com.lxy.sky.obtainsofile.GetNum

产生的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lxy_sky_obtainsofile_GetNum */

#ifndef _Included_com_lxy_sky_obtainsofile_GetNum
#define _Included_com_lxy_sky_obtainsofile_GetNum
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lxy_sky_obtainsofile_GetNum
 * Method:    getNum
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_com_lxy_sky_obtainsofile_GetNum_getNum
  (JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif

5、重新创建一个C文件,GetNum.c,并实现头文件中的方法。

GetNum.c文件中如下编写:

//
// Created by lxy on 2019/2/12.
//

#include <jni.h>
jint JNICALL Java_com_lxy_sky_obtainsofile_GetNum_getNum
  (JNIEnv* env, jclass jc, jint num){

  return 2*num;
  }

6、在jni文件夹下创建两个文件,Android.mk和Application.mk。

在jni文件夹下创建Android.mk,其中添加如下内容:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := GetNum
LOCAL_SRC_FILES := GetNum.c
include $(BUILD_SHARED_LIBRARY)

在jni文件夹下创建Android.mk,其中添加如下内容:

APP_PLATFORM := android-14
APP_ABI :=all

这两个文件创建完后编写会出现红色波浪线,可以使用mark as plain text,就不会报错了

7、使用ndk-build命令生成.so文件。

在终端跳转到jni目录下,输入ndk-build就会在jni同级目录出现libs和obj两个文件夹,里面就是.so文件

lxydeMacBook-Pro:java lxy$ cd /Users/lxy/Documents/ObtainSoFile/app/src/main/jni
lxydeMacBook-Pro:jni lxy$ ndk-build

然后就会出现以下效果,就说明好了。

[arm64-v8a] Compile        : GetNum <= GetNum.c
[arm64-v8a] SharedLibrary  : libGetNum.so
[arm64-v8a] Install        : libGetNum.so => libs/arm64-v8a/libGetNum.so
[x86_64] Compile        : GetNum <= GetNum.c
[x86_64] SharedLibrary  : libGetNum.so
[x86_64] Install        : libGetNum.so => libs/x86_64/libGetNum.so
[armeabi-v7a] Compile thumb  : GetNum <= GetNum.c
[armeabi-v7a] SharedLibrary  : libGetNum.so
[armeabi-v7a] Install        : libGetNum.so => libs/armeabi-v7a/libGetNum.so
[x86] Compile        : GetNum <= GetNum.c
[x86] SharedLibrary  : libGetNum.so
[x86] Install        : libGetNum.so => libs/x86/libGetNum.so

到这里.so文件就生成了。

二、下面就是这些.so文件是如何使用,在本地如何使用,在其他工程时如何使用的?

1、首先是在本工程中使用,此时jni文件夹下的所有文件都是可以删除掉的,有没有都是可以使用的了。

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("GetNum");
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(this, "输出的数字是:"+GetNum.getNum(4), Toast.LENGTH_SHORT).show();
    }
}

2、其他工程的话需要新建一个包,要和刚才的包名相同,com.lxy.sky.obtainsofile。如果你有一个本地(native)方法,就会自动根据路径去查找C语言方法,比如以上的native getnum()方法,就会自动查询Java_com_lxy_sky_obtainsofile_GetNum_getNum,如果没有就会报错,所以想要试用该方法,就要创建一个相同的包名,这个路径是和该方法所在的包有关的,类名和方法名也要相同。

使用AS生成.so文件以及调用(mac环境)

要将.so文件粘贴到libs和jnilibs(可以没有)目录下,类及其内容如上图即可,在build. gradle中添加如下代码

sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}

现在就可以在你需要的地方调用了:

GetNum jniUtils=new GetNum();
Toast.makeText(this, "输出的数字是:"+ jniUtils.getNum(4), Toast.LENGTH_SHORT).show();

这样就全部完成了。

三、过程当中遇到的异常

1、java.lang.UnsatisfiedLinkError   so库找不到

java.lang.UnsatisfiedLinkError: No implementation found for int com.lxy.sky.obtainsofile.GetNum.getNum(int)
 (tried Java_com_lxy_sky_obtainsofile_GetNum_getNum and Java_com_lxy_sky_obtainsofile_GetNum_getNum__I)

这有两种可能,一个是没有加载so库

System.loadLibrary("GetNum");

另一种可能就是创建的包名不对。

2、Android NDK: Could not find application project directory !

在使用终端使用ndk-build命令的时候报的错误,这是因为没有事先定位到当前工程的jni目录所导致的