Android项目目录结构
Example
├── build
├── build.gradle
├── consumer-rules.pro
├── gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libs
├── local.properties
├── proguard-rules.pro
└── src
├── androidTest
│ └── java
│ └── com
├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── config.json
│ │ ├── entity
│ │ └── words
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ └── vad_jni.cpp
│ ├── java
│ │ └── com
│ ├── jniLibs
│ │ ├── arm64-v8a
│ │ └── armeabi-v7a
│ └── res
└── test
└── java
└── com
我们比较关心的目录是libs
,src
。
JNI 示例
动态库放置位置
- 在Android Studio中,项目中依赖的动态库会默认匹配到
src/main/jniLibs
目录 - 原则上,所有的动/静态库都放到
src/main/jniLibs
目录下
build.gradle 配置
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-std=c++14 -frtti -fexceptions"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
...
}
C/CPP源码位置
在 Android Studio中,使用CMake作为编译工具,JNI C/CPP 源码会放到 src/main/cpp
目录或者 src/main/jni
,具体的目录名称要和build.gradle中配置 CMakeLists.txt
的位置相同
Example
└─ src
├─ main
│ ├─ cpp
│ │ ├── CMakeLists.txt
│ │ └── demo_jni.cpp
└───└─ java
└── com
└── example
└── jni
└── Demo.java
CMakeLists.txt 内容(名字不能错)
cmake_minimum_required(VERSION 3.4.1)
add_library(demo_jni SHARED)
include_directories("${PROJECT_SOURCE_DIR}/include")
target_sources(demo_jni PUBLIC vad_jni.cpp)
target_link_libraries(demo_jni -llog)
添加三方动态库的依赖
在CMakeLists文件中添加如下语句:
target_link_libraries(demo_jni ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libxxx.so)
CMAKE_ANDROID_ARCH_ABI
: 当前编译的 ABI。
静态注册示例
demo_jni.cpp 源码
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "demo_jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C" {
void Java_com_example_jni_Demo_demo_1init(JNIEnv *env, jobject clazz)
{
LOGD("jni demo");
return;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM *vm, void *reserved)
{
return;
}
}
Demo.java 源码
- 加载动态库
- 声明 native 方法
package com.example.jni;
public class Demo {
static {
System.loadLibrary("demo_jni");
}
public native void demo_init();
}
动态注册和多个类JNI实例
MainActivity
package com.example.jnidemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
public JNIClassA jniClassA;
public JNIClassB jniClassB;
static {
System.loadLibrary("jni_demo");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jniClassA = new JNIClassA();
jniClassB = new JNIClassB();
jniClassA.printA();
jniClassB.printB();
}
}
JNIClassA
package com.example.jnidemo;
public class JNIClassA {
public native int printA();
}
JNIClassB
package com.example.jnidemo;
public class JNIClassB {
public native int printB();
}
com_example_jnidemo_JNIClassA
Header
#ifndef JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSA_H
#define JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSA_H
#include <jni_demo.h>
#ifdef __cplusplus
extern "C" {
#endif
jint registerJNIClassA(JNIEnv *env);
#ifdef __cplusplus
}
#endif
#endif //JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSA_H
CPP
#include "jni_demo.h"
#ifdef __cplusplus
extern "C" {
#endif
static const char *jclass_ClassB_name = "com/example/jnidemo/JNIClassB";
static jclass jclass_ClassB = NULL;
static jint print_B(JNIEnv *env, jobject clazz)
{
LOGD("%s, %d print_B", __FILE__, __LINE__);
return 0;
}
static JNINativeMethod classB_methods[] = {
{"printB", "()I", (void *)print_B}
};
jint registerJNIClassB(JNIEnv *env)
{
jclass_ClassB = env->FindClass(jclass_ClassB_name);
if (jclass_ClassB == NULL) {
LOGE("can't find %s", jclass_ClassB_name);
return -1;
}
int err = env->RegisterNatives(jclass_ClassB, classB_methods, sizeof(classB_methods)/ sizeof(classB_methods[0]));
if (err != 0) {
LOGE("call RegisterNatives failed, %s", __FILE__);
return -1;
}
return 0;
}
#ifdef __cplusplus
}
#endif
com_example_jnidemo_JNIClassB
Header
#ifndef JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSB_H
#define JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSB_H
#include <jni_demo.h>
#ifdef __cplusplus
extern "C" {
#endif
jint registerJNIClassB(JNIEnv *env);
#ifdef __cplusplus
}
#endif
#endif //JNIDEMO_COM_EXAMPLE_JNIDEMO_JNICLASSB_H
CPP
#include "jni_demo.h"
#ifdef __cplusplus
extern "C" {
#endif
static const char *jclass_ClassB_name = "com/example/jnidemo/JNIClassB";
static jclass jclass_ClassB = NULL;
static jint print_B(JNIEnv *env, jobject clazz)
{
LOGD("%s, %d print_B", __FILE__, __LINE__);
return 0;
}
static JNINativeMethod classB_methods[] = {
{"printB", "()I", (void *)print_B}
};
jint registerJNIClassB(JNIEnv *env)
{
jclass_ClassB = env->FindClass(jclass_ClassB_name);
if (jclass_ClassB == NULL) {
LOGE("can't find %s", jclass_ClassB_name);
return -1;
}
int err = env->RegisterNatives(jclass_ClassB, classB_methods, sizeof(classB_methods)/ sizeof(classB_methods[0]));
if (err != 0) {
LOGE("call RegisterNatives failed, %s", __FILE__);
return -1;
}
return 0;
}
#ifdef __cplusplus
}
#endif
jni_demo
#ifndef JNIDEMO_JNI_DEMO_H
#define JNIDEMO_JNI_DEMO_H
#include <jni.h>
#include <android/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#define LOG_TAG "jni_demo"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif //JNIDEMO_JNI_DEMO_H
CPP
#include "jni_demo.h"
#include <com_example_jnidemo_JNIClassA.h>
#include <com_example_jnidemo_JNIClassB.h>
#ifdef __cplusplus
extern "C" {
#endif
static jint registerNativeMethods(JNIEnv* env) {
jint err = registerJNIClassA(env);
if (err != 0) {
LOGE("failed to registerJNIClassA, err: %d", err);
return err;
}
err = registerJNIClassB(env);
if (err != 0) {
LOGE("failed to registerJNIClassB, err: %d", err);
return err;
}
return 0;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (env == NULL) {
LOGE("JNI_OnLoad error, should never happen.");
return -1;
}
jint err = registerNativeMethods(env);
if (err != 0) {
LOGE("failed to register native methods, err: %d", err);
return err;
}
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
return;
}
#ifdef __cplusplus
}
#endif
build gradle配置
jar/aar 包导入
将 jar 或者 aar 包放入 $PROJECT/app/libs
目录,并确认 $PROJECT/app/build.gradle
文件包含下面的内容:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
}
Instrumented Test
issues
permission denied
问题
plugin: ‘com.android.library’ not found
解决:在 build.gradle 中添加下面内容
allprojects {
repositories {
google()
jcenter()
}
}
buildscript {
ext.kotlin_version = '1.3.72'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
Bad encoded_value method type size 7
add compileOptions { targetCompatibility JavaVersion.VERSION_1_8 }
in build:gradle(:app) under the android section.
android {
compileOptions { targetCompatibility JavaVersion.VERSION_1_8 }
}
gradle: connection reset
- ping
dl.google.com
,确定是否能连接到google网络
Build Output 汉字乱码解决方案
-
AS内自带的全局搜索功能,搜索
Edit Custom VM Options
-
添加下面内容:
-Dfile.encoding=UTF-8