初学Android相关知识,记录自己的学习过程。
进行 NDK 开发的时候,首先需要把 Java 方法声明为 native,然后编写对应的 C/C++ 代码,并编译成为动态链接库,在调用 Java 方法前加载动态链接库即可调用。
一、开发环境
在Mac,需要安装:
JDK 1.8
Android Studio
Mac下的Android Studio安装NDK和cmake:
二、简单的DEMO
这里只做一个简单的Demo,java代码调用c函数返回一个"hello"字符串。c函数是在一个.so库中的。所以要先编译一个.so库。
使用Android Studio创建一个测试项目:
MainActivity.java代码:
package com.example.mytest001;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.R.string;
import android.widget.Toast;
import com.example.mytest001.activity.SecondMainActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, getString(), 5).show();//弹框展示获取到的字符串
}
public native CharSequence getString();//native函数,获取一个字符串
}
因为MainActivity.java在com.example.mytest001这个包下面,所以在终端cd到java这个目录下:
使用javah命令生成一个.h头文件:
% javah -jni com.example.mytest001.MainActivity
如果看不到,可以在Android Studio中refresh一下。
创建一个jni文件夹,修改com_example_mytest001_MainActivity.h名字为JNI_study.h ,并移动到jni文件夹下:
在jni下创建三个文件:
Android.mk,这是编译要用到的参数,会生成一个JNI_study模块,代码如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI_study
LOCAL_SRC_FILES := JNI_study.c
LOCAL_ARM_MODE := arm
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
Application.mk代码如下:
APP_PLATFORM := android-19
APP_ABI := all
JNI_study.c代码如下:
#include <JNI_study.h>
JNIEXPORT jobject JNICALL Java_com_example_mytest001_MainActivity_getString
(JNIEnv *env, jobject obj){
jstring str = (*env)->NewStringUTF(env, "hello");
return str;
}
开始编译.so文件。
终端cd到jni文件夹下,执行编译命令:
% ndk-build
执行完毕,会在libs文件夹下生成多个架构的so文件:
有了so文件,就要在MainActivity.java中引用这个so库了,MainActivity.java代码如下:
package com.example.mytest001;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.R.string;
import android.widget.Toast;
import com.example.mytest001.activity.SecondMainActivity;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("JNI_study");//加载JNI_study这个so库
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, getString(), 5).show();
}
public native CharSequence getString();
}
运行这个测试工程,可在模拟器中看到弹框展示“hello”信息:
三、可能遇到的各种错误
(1)使用ndk-build报错:
% ndk-buildzsh: command not found: ndk-build
解决:
是因为没有配置好ndk的路径,我直接找到ndk-build的全路径,然后直接在终端执行全路径的命令:
jni % /Users/wj1/Library/Android/sdk/ndk/25.1.8937393/ndk-build
以上是我电脑中ndk-build的全路径。
(2)找到ndk-build路径后报错:
% /Users/wj1/Library/Android/sdk/ndk/25.1.8937393/ndk-build
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-19.
Android NDK: WARNING: APP_PLATFORM android-19 is higher than android:minSdkVersion 1 in /Users/wj1/AndroidStudioProjects/MyTest001/app/src/main/AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-19. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
usage: ldflags_to_sanitizers.py [GLOBAL_FLAGS] --module [MODULE_FLAGS] [--module [MODULE_FLAGS]...]
/Users/wj1/Library/Android/sdk/ndk/25.1.8937393/build/core/build-all.mk:84: Android NDK: WARNING: There are no modules to build in this project!
应该是安卓SDK最低版本问题,在build.gradle文件中修改minSDKVersion为19,截图如下:
(3)报错
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-19.
把Application.mk的APP_PLATFORM修改成"android-19":
(4)报错
Android NDK: WARNING: APP_PLATFORM android-19 is higher than android:minSdkVersion 1
解决:
在AndroidManifest.xml中设置users-sdk的minSDKVersion为19:
(5)报错
Android NDK: WARNING: There are no modules to build in this project!
解决:
在Android.mk中缺少一行内容:
include $(BUILD_SHARED_LIBRARY)
(6)报错
make: *** No rule to make target '/JNI_study.c', needed by '/Users/wj1/AndroidStudioProjects/MyTest001/app/src/main/obj/local/armeabi-v7a/objs/JNI_study/JNI_study.o'. Stop.
解决:
因为Android.mk中的路径写成了
LOCAL_PATH := $(cal my-dir)
改成:
LOCAL_PATH := $(call my-dir)
就好了。
如果你也遇到类似提示,可检查下Android.mk中的内容。
(7)运行该项目,app闪退,并提示错误:
java.lang.UnsatisfiedLinkError: dlopen failed: library "xxx.so" not found
解决:
在app的build.gradle文件中添加下面这就话:
// sourceSets用于指定依赖的第三方so库的存放地址 sourceSets { main { jniLibs.srcDirs = ['libs'] } }解决方法:在android节点下添加上述的sourceSets的配置即可。
如果还是报同样的错误,可能是.so对应的路径不对,找到对应libs文件夹,改为:
sourceSets { main { jniLibs.srcDirs = ['src/main/libs’] }}
因为我的项目中so所在的路径是src/main/libs下面,所以jniLibs.srcDirs是"src/main/libs",如果直接写 jniLibs.srcDirs = ['libs'] 就会找不到so文件。