侧边栏壁纸
博主头像
Eoser's page! 博主等级

@学习@生活@自己

  • 累计撰写 121 篇文章
  • 累计创建 31 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Android JNI笔记第一弹:入门

eoser
2023-04-16 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

Android JNI 官方文档

啥是JNI?

Jin是java 与 C/C++ 互访问的一种方案

  • 从 java 中访问 C/C++
  • 从 C/C++ 中访问 java

一般什么时候使用Jni

  • 性能需求:利用C/C++的高性能特点,处理一些数据
  • 三方库:比如openvc这类库一般都是用 C 写,当时这些库基本都给封装好了jar调用库
  • 驱动:这是 android 与底层驱动互调用的重要手段

jni 的简单应用

用Android Studio创建一个Empty Activity项目

  • MainActivity 代码
package org.eu.uiai.jnilearn;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    static {
        //缺失 libc++_shared.so 解决问题的库,通过CMake直接编译到软件中
        System.loadLibrary("help_lib_name");
        //编译出的库文件为 libmylib.so
        System.loadLibrary("mylib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView ss=findViewById(R.id.show_string);
        TextView st=findViewById(R.id.show_text);
        JniApi jApi=new JniApi();
        int a=9,b=10;
        ss.setText(jApi.getString(1));
        st.setText(a+"+"+b+"="+jApi.getSum(a,b));
    }
}
  • Java 调用 C 的 API 接口
package org.eu.uiai.jnilearn;

public class JniApi {
    public native String getString(int id);
    public native int getSum(int a,int b);
}
  • 通过命令行将JniApi的代码生成 C++ 的头文件
javac ./app/src/main/java/org/eu/uiai/jnilearn/JniApi.java -h ./app/src/main/jni
  • 头文件 org_eu_uiai_jnilearn_JniApi.h 内容
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class org_eu_uiai_jnilearn_JniApi */

#ifndef _Included_org_eu_uiai_jnilearn_JniApi
#define _Included_org_eu_uiai_jnilearn_JniApi
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_eu_uiai_jnilearn_JniApi
 * Method:    getString
 * Signature: (I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_eu_uiai_jnilearn_JniApi_getString
  (JNIEnv *, jobject, jint);

/*
 * Class:     org_eu_uiai_jnilearn_JniApi
 * Method:    getSum
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_org_eu_uiai_jnilearn_JniApi_getSum
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
  • 根据JNI头文件写 C++ 的实现代码
#include 
#include "org_eu_uiai_jnilearn_JniApi.h"

JNIEXPORT jstring JNICALL Java_org_eu_uiai_jnilearn_JniApi_getString
        (JNIEnv *env, jobject, jint id) {
    int iid = (int) id;
    if (iid == 0) {
        return env->NewStringUTF("Hello world!");
    } else if (iid == 1) {
        return env->NewStringUTF("你好世界!");
    }
    return env->NewStringUTF("未知ID");
}

JNIEXPORT jint JNICALL Java_org_eu_uiai_jnilearn_JniApi_getSum
        (JNIEnv *, jobject, jint a, jint b) {
    return a + b;
}
  • 编译出不同平台的 so 库 NDK的路径:“D:\DevSoft\AndroidSDK\ndk” C++编译工具的路径,版本不同有些区别:“24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++” 编译的架构:架构-linux-android21
# arm64 平台的 
D:\DevSoft\AndroidSDK\ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ -target aarch64-linux-android21 ./app/src/main/jni/mylib.cpp -o ./app/libs/arm64-v8a/libmylib.so -fpic -shared
# x64 平台的 
D:\DevSoft\AndroidSDK\ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ -target x86_64-linux-android21 ./app/src/main/jni/mylib.cpp -o ./app/libs/x86_64/libmylib.so -fpic -shared
# x86 平台的 
D:\DevSoft\AndroidSDK\ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ -target i686-linux-android21 ./app/src/main/jni/mylib.cpp -o ./app/libs/x86/libmylib.so -fpic -shared
# arm32 平台的 
D:\DevSoft\AndroidSDK\ndk\24.0.8215888\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ -target armv7a-linux-androideabi21 ./app/src/main/jni/mylib.cpp -o ./app/libs/armeabi-v7a/libmylib.so -fpic -shared

-修改build.gradle配置文件

plugins {
    id 'com.android.application'
}

android {
    namespace 'org.eu.uiai.jnilearn'
    compileSdk 32

    defaultConfig {
        applicationId "org.eu.uiai.jnilearn"
        minSdk 28
        targetSdk 32
        versionCode 1
        versionName "1.0"
        //添加位置1 begin
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
        sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
        externalNativeBuild {
            cmake {
                //为了解决找不到的问题 libc++_shared.so的问题
                arguments "-DANDROID_STL=c++_shared"
            }
        }
        //添加位置2 end
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    //添加位置2 begin
    //为了解决找不到 libc++_shared.so 问题
    externalNativeBuild {
        cmake {
            path "src/main/jni/CMakeLists.txt"
        }
    }
    //添加位置2 end

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
  • 缺少库的报错 由于默认的编译方式是最小化编译,动态库。我使用的 Android 模拟器没有库,导致 libc++_shared.so 报错,需要解决。其实可以直接通过CMake脚本编译目标so库的,但是我这里是模拟导入SO库,我要另外写一个Cpp文件和Cmake文件来解决找不到so的问题
java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
  • 创建src/main/jni/CMakeLists.txt文件
cmake_minimum_required(VERSION 3.6.0)
add_library(help_lib_name SHARED help_lib.cpp)
  • 创建帮助导入 libc++_shared.so 的cpp文件help_lib.cpp
# 空的,不用任何内容
OK,这样就可以编译过了

实现效果

0

评论区