Android_开启NDK开发入门

715 阅读2分钟

初学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文件。