【Android JNI - 2】JNI初探

JNI简单示例

    • 一、软件下载
      • 1. NDK 下载: 目录 Anroid studio 中已经不再使用 ndk 了,转而使用cmake
      • 2. Android Studio 下载
      • 3. cmake 下载
    • 二、JNI调用C简单示例
      • 1. 创建一个简单的java Project
      • 2. 编写Java JNI 代码 & 加载C 库 libHello.so 文件
      • 3. 编写C代码
      • 4. 编译成 libHello.so 库文件
      • 5. 在Java 调用JNI 方法
      • 6. 运行虚拟机的时候报错:
    • 三、JNI调用C简单示例二
      • 1. 实现 sayGoodBye 的C语言方法
      • 2. 增加 JNI 的sayGoodBye方法
      • 3. 在Java 中调用JNI中的 sayGoodBye 方法


一、软件下载

1. NDK 下载: 目录 Anroid studio 中已经不再使用 ndk 了,转而使用cmake

https://developer.android.google.cn/ndk/downloads/

选择 x64 的NDK软件:
https://dl.google.com/android/repository/android-ndk-r20-windows-x86_64.zip

2. Android Studio 下载

https://developer.android.google.cn/studio/

选择 x64 的Android Studio
https://dl.google.com/dl/android/studio/install/3.5.0.21/android-studio-ide-191.5791312-windows.exe

3. cmake 下载

下载好后,添加环境变量,添加好后记得重启下 Android Studio。
https://cmake.org/download/
如果报错说 cmake 找不到: Could not find method cmake() for arguments,就是cmake 编译工具没有找到。

参考 : https://blog.csdn.net/xiaoyu_93/article/details/53082088


二、JNI调用C简单示例

  1. 在 Java 里面写 Native 代码
  2. 在main 目录下,建立 JNI 目录,写 C 代码
  3. 在CMakeLists.txt 中配置动成链接库的名称
  4. 加载动态链接库
  5. 在java 中调用 JNI 方法

1. 创建一个简单的java Project

【Android JNI - 2】JNI初探_第1张图片

2. 编写Java JNI 代码 & 加载C 库 libHello.so 文件

在 JNI_Daemon2\app\src\main\java\com\android\jni_daemon\ 目录下新建文件 JNI.java

@ \JNI_Daemon2\app\src\main\java\com\android\jni_daemon\JNI.java

package com.android.jni_daemon;

public class JNI {
    {
        //加载动态链接库
        System.loadLibrary("Hello");
    }
    //定义Native 方法, Java 调用对应的 C 代码
    public native String sayHello();

}

3. 编写C代码

在 \JNI_Daemon2\app\src\main\ 目录下新建 jni 目录:

@ \JNI_Daemon2\app\src\main\jni\Hello.c

// Created by Administrator on 2019/9/14.

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

// Java_全类名_方法名
// JNIEnv * env: 里面有很多方法
// jObject jobj: 谁调用了这个方法就是谁的实例
// 当前就是 JNI.this
jstring Java_com_android_jni_1daemon_JNI_sayHello(JNIEnv* env, jobject jobj){
    //jstring (*NewStringUTF)(JNIEnv* , const char *)
    char * text = "I am from C";
    return (*env)->NewStringUTF(env, text);
}

4. 编译成 libHello.so 库文件

由于最新的 Android Studio 中已经不支持 NDK 了,所以我们使用 cmake 来编译so库文件

修改:\JNI_Daemon2\app\build.gradle

@ \JNI_Daemon2\app\build.gradle

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.android.jni_daemon"
        minSdkVersion 27
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        //使用cmake 工具 ++++
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters "arm64-v8a", "armeabi-v7a", "x86" ,"x86_64" //CPU 的类型
            }
        }
        //使用cmake 工具 ----
    }
    //使用cmake 工具 ++++
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
    //使用cmake 工具 ----
    
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

增加cmake 所要编绎的文件: \JNI_Daemon2\app\CMakeLists.txt

@ \JNI_Daemon2\app\CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.
#CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
       # 设置so文件名称.  libHelo.so
       Hello
       
       # Sets the library as a shared library.
       SHARED
       # 设置这个so文件为共享.

       # Provides a relative path to your source file(s).
       # 设置这个so文件为共享.
       src/main/jni/Hello.c)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
       log-lib

       # Specifies the name of the NDK library that
       # you want CMake to locate.
       log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
            # 制定目标库.
            Hello

            # Links the target library to the log library
            # included in the NDK.
            ${log-lib} )

库文件编译成功后,可以看到一系列编译好的库文件:
【Android JNI - 2】JNI初探_第2张图片

5. 在Java 调用JNI 方法

打开文件: \JNI_Daemon2\app\src\main\java\com\android\jni_daemon\MainActivity.java

@ \JNI_Daemon2\app\src\main\java\com\android\jni_daemon\MainActivity.java

package com.android.jni_daemon;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String result = new JNI().sayHello();  	// new 一个 JNI 对象,并调用其 sayHello() 方法
        System.out.println("result=" + result);	// 打印 log ,将 c 中返回的数据返回来
    }
}

将编译好的apk 安装在虚拟机上,运行
测试结果:
【Android JNI - 2】JNI初探_第3张图片

6. 运行虚拟机的时候报错:

09-14 15:25:19.659  8546  8546 I zygote  : 
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" 
on path: DexPathList[[zip file "/data/app/com.android.jni_daemon-kihiF3BkpbXLfp0oUvewLQ==/base.apk"],
nativeLibraryDirectories=[/data/app/com.android.jni_daemon-kihiF3BkpbXLfp0oUvewLQ==/lib/x86, 
/data/app/com.android.jni_daemon-kihiF3BkpbXLfp0oUvewLQ==/base.apk!/lib/x86, /system/lib, /vendor/lib]]

原因为 虚拟机的版本号 和 代码换版本号不一致,确保版本一致即可。

三、JNI调用C简单示例二

前面我们已经实现好了流程,在前面的基础上,我们再来新增一个方法:
sayGoodBye
代码修改如下:

1. 实现 sayGoodBye 的C语言方法

@ \JNI_Daemon2\app\src\main\jni\Hello.c

在其中新增sayGoodBye的C语言实现代三:

jstring Java_com_android_jni_1daemon_JNI_sayGoodBye(JNIEnv * env, jobject jobj){
    char *text = "From C 拜拜!";
    return  (*env)->NewStringUTF(env,text);
}

2. 增加 JNI 的sayGoodBye方法

@ \JNI_Daemon2\app\src\main\java\com\android\jni_daemon\JNI.java

package com.android.jni_daemon;
public class JNI {
    {
        //加载动态链接库
        System.loadLibrary("Hello");
    }
    //定义Native 方法, Java 调用对应的 C 代码
    public native String sayHello();
    public native String sayGoodBye();
}

3. 在Java 中调用JNI中的 sayGoodBye 方法

@ \JNI_Daemon2\app\src\main\java\com\android\jni_daemon\MainActivity.java

package com.android.jni_daemon;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewDebug;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        String result = new JNI().sayHello();
        System.out.println("sayHello return result = " + result);
        
        String i = new JNI().sayGoodBye();
        System.out.println("sayGoodBye return result = " + i);
    }
}

编译运行结果为:

2019-09-15 09:57:02.651 6743-6743/com.android.jni_daemon I/System.out: sayHello return result = I am from C
2019-09-15 09:57:02.651 6743-6743/com.android.jni_daemon I/System.out: sayGoodBye return result = From C 拜拜!

在这里插入图片描述

你可能感兴趣的:(06--Android,Java)