Android stuido创建第一个Hello Jni项目,使用的是Mac
下载NDK,配置Tool
Android stuido sdk下载ndk包
配置环境变量
终端输入 vim ~/.bash_profile ,配置NDK和path
终端输入ndk-build,显示如下没找到directory为配置安装成功
导入 ndk包,project structure
Android stuido 配置ExternalTools,左下角新增如下3个选项
(1)jni create
Program:javah
Arguments:-v -jni -d $ModuleFileDir$/src/main/jni $FileClass$
Working directory : $SourcepathEntry$
(2)ndk-build
Program:/Users/你自己的用户/Library/Android/sdk/ndk/21.1.6352462/ndk-build
Working directory : $ProjectFileDir$/app/src/main
(3)ndk-build-clean 与ndk-build一致,增加了clean
静态项目配置
1.app的gradle增加如下,hello为so文件库名称,
jniLibs.srcDir很重要,没有无法加载
android {
defaultConfig {
ndk {
moduleName "hello"
}
}
buildTypes {
sourceSets{
main {
jni.srcDirs = []
jniLibs.srcDir "src/main/libs"
}
}
}
}
2.根目录gradle.properties
android.useDeprecatedNdk=true
3.创建Hellojni.class
方法名getStringFromC()
以后生成so文件module名hello
package com.test.buttontest.ndkdemo;
/**
* @author
* @time 2020-04-21.
*/
public class Hellojni {
public static native String getStringFromC();
static {
System.loadLibrary("hello");
}
public Hellojni(){}
}
Native编译
1.main下新建jni空文件夹
2.在刚才Hellojni,点击右键->External Tools->jni create
会自动生成上图红色com_tayh_buttontest_ndkdemo_Hellojni.h文件,为头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_tayh_buttontest_ndkdemo_Hellojni */
#ifndef _Included_com_tayh_buttontest_ndkdemo_Hellojni
#define _Included_com_tayh_buttontest_ndkdemo_Hellojni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_tayh_buttontest_ndkdemo_Hellojni
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
3.编写hello.c C语言文件,从头文件拷方法名进行修改
#include <stdio.h>
#include <stdlib.h>
#include "com_tayh_buttontest_ndkdemo_Hellojni.h"
JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
(JNIEnv * env, jclass jclass){
return (*env)-> NewStringUTF(env,"helloword");
}
4.新增Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的库的名称
LOCAL_MODULE := hello
#库编译需要的源码文件和依赖库
LOCAL_SRC_FILES := hello.c
#告诉Ndk编译成动态库
include $(BUILD_SHARED_LIBRARY)
新增Application.mk
APP_PLATFORM := android-19
APP_ABI := all
5.在Hellojni.class,点击右键->External Tools->ndk build,
会在main目录下自动生成obj和libs文件夹,里面生成了so文件
Java上层调用
NdkOneActivity.class
package com.test.buttontest.ndkdemo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.tayh.buttontest.R;
/**
* @author
* @time 2020-04-21.
*/
public class NdkOneActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ndk_one);
textView = findViewById(R.id.tv_show);
try {
String str = Hellojni.getStringFromC();
textView.setText(str);
}catch (Exception e){
e.printStackTrace();
}
}
}
增加Log
1.Android.mk
LOCAL_LDLIBS := -lm -llog
- 可以定义个方法,并用C实现
public static native void callLogFromJni();
3.C中添加android/log.h,并实现LOGI
#include <android/log.h>
#define LOG_TAG "TAG"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_callLogFromJni
(JNIEnv * env, jclass jclass){
__android_log_print(ANDROID_LOG_INFO,LOG_TAG,"from jni log");
}
JNIEXPORT jstring JNICALL Java_com_tayh_buttontest_ndkdemo_Hellojni_getStringFromC
(JNIEnv * env, jclass jclass){
LOGI("helloword");
return (*env)-> NewStringUTF(env,"helloword");
}
4.build.,gradle可以配置也可以不配置
android {
ndk {
moduleName "hello"
ldLibs 'log'
// ldLibs.addAll([ 'log']); 这个写法会报空指针
}
}
遇到问题
1.Android NDK: ERROR:jni/Android.mk:native: LOCAL_SRC_FILES should point to a file ending with “.so”
原因:Android.mk文件写错,用的rebuilt
#告诉Ndk编译成动态库
#include $(PREBUILT_SHARED_LIBRARY)
include $(BUILD_SHARED_LIBRARY)
2.No rule to make target ‘native’, needed by ‘ndk-app-local’
原因:build.gradle和Android.mk 库名都要写对
3.undefined reference to `__android_log_print’
原因:Log包导入错误,Android.mk 没有导入log
4.NDK ERROR: Cannot invoke method addAll() on null object
原因:log的gradle导入空指针ldLibs.addAll
动态项目配置
1.jni目录直接创建active.cpp get_random_num返回一个随机数
#ifndef ACTIVE_CPP
#define ACTIVE_CPP
// jni头文件
#include <jni.h>
#include <cassert>
#include <cstdlib>
#include <iostream>
using namespace std;
//native 方法实现
jint get_random_num(){
return rand();
}
/*需要注册的函数列表,放在JNINativeMethod 类型的数组中,
以后如果需要增加函数,只需在这里添加就行了
参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明)
3.C/C++中对应函数的函数名(地址)
*/
static JNINativeMethod getMethods[] = {
{"getRandomNum","()I",(void*)get_random_num}
};
//此函数通过调用RegisterNatives方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* getMethods,int methodsNum){
jclass clazz;
//找到声明native方法的类
clazz = env->FindClass(className);
if(clazz == NULL){
return JNI_FALSE;
}
//注册函数 参数:java类 所要注册的函数数组 注册函数的个数
if(env->RegisterNatives(clazz,getMethods,methodsNum) < 0){
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env){
//指定类的路径,通过FindClass 方法来找到对应的类
const char* className = "com/tayh/buttontest/ndkdemo/ActiveJni";
return registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));
}
//回调函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv* env = NULL;
//获取JNIEnv
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
//注册函数 registerNatives ->registerNativeMethods ->env->RegisterNatives
if(!registerNatives(env)){
return -1;
}
//返回jni 的版本
return JNI_VERSION_1_6;
}
#endif
2.Android.mk ,ndk-build直接创建so文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#生成的库的名称
LOCAL_MODULE := active
#库编译需要的源码文件和依赖库
LOCAL_SRC_FILES :=active.cpp
LOCAL_LDLIBS := -lm -llog
include $(BUILD_SHARED_LIBRARY)
3.ActiveJni.getRandomNum() 即可直接调用
public class ActiveJni {
static {
System.loadLibrary("active");
}
public static native int getRandomNum();
}