安卓JNI静态注册

如今很多android项目的重要逻辑代码都放在了so文件里(当然也有人偏要把非重要逻辑也用c/c++实现),so文件即共享目标文件也称共享库文件,由c/c++代码生成的,所以在android开发中如何调用c/c++代码中的方法就需要一个媒介,这个媒介就是本文提到的JNI。

JNI是Java Native Interface的缩写,即java本地接口,通过使用 Java本地接口编写程序,可以确保代码的可移植性。

注意:标题我写的是android下的JNI静态注册,之所以强调android,是因为JNI不光用到android开发领域,其它java开发项目一样可以使用,本文针对android环境。

本文默认你的android开发环境和ndk开发环境都已配置完毕。
本文默认你使用java语言来开发android项目。

那如何在android项目中使用c/c++代码中的方法呢?

要使用c/c++代码中的方法,就需要在android代码中对c/c++代码中的方法进行注册,注册方式有两种: 静态注册和动态注册,本文讲的是JNI静态注册

首先打开AS并新建一个空项目

%title插图%num
%title插图%num

随手布个局,在默认的activity中放一个按钮控件和一个textview控件,后面写个简单的按钮事件,点击按钮时调用c/c++代码中的方法,并将结果显示在textview控件

%title插图%num

现在开始编辑java代码,使用native关键字定义方法。

package com.example.jnistaticregister;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    public native String YourJNI();//使用native关键字定义一个方法YourJNI
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

缩略图如下:

%title插图%num

上面还未编写按钮事件相关的代码,现在编写按钮事件相关代码,即点击按钮时去调用YourJNI方法,将YourJNI方法的返回内容呈现在TextView控件中。

现在整个MainActivity.java的内容如下:

package com.example.jnistaticregister;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    public native String YourJNI();//使用native关键字定义一个方法YourJNI
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);//根据控件ID获取Button控件
        button.setOnClickListener(new View.OnClickListener() { 
            @Override
            public void onClick(View v) {
                TextView textView = findViewById(R.id.textView);//根据控件ID获取TextView控件
                String content = YourJNI();//调用YourJNI方法并获取返回内容
                textView.setText(content);//将返回内容放入TextView控件
            }
        });
    }
}

缩略图如下:

%title插图%num

接着开始生成YourJNI方法的c/c++代码,注意我截图中的目录,目录不能错,我们先生成头文件,通过javah指令在终端下生成

%title插图%num

javah -jni com.example.jnistaticregister.MainActivity

%title插图%num

回车后生成的头文件如下图:

%title插图%num

建立一个jni目录,与java目录同级,并将生成的头文件移动到jni目录下,我嫌名称太长将com_example_jnistaticregister_MainActivity.h改成了MyJNI.h,如下图:

%title插图%num

紧接着在jni目录下建立c/c++的源文件,我这里建立MyJNI.cpp,内容如下:

#include "MyJNI.h"

JNIEXPORT jstring JNICALL Java_com_example_jnistaticregister_MainActivity_YourJNI(JNIEnv *env,jobject)
{
    return env->NewStringUTF("Hi,我是YourJNI,我来自MyJNI.so");
}

缩略图如下:

%title插图%num

再接着在jni目录下分别添加Android.mk和Application.mk

Android.mk内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm            #指令集
LOCAL_MODULE := MyJNI            #so文件名称
LOCAL_SRC_FILES := MyJNI.cpp       #源文件名称
include $(BUILD_SHARED_LIBRARY)

Application.mk内容如下:

APP_ABI := armeabi-v7a x86 #我这里指定了2种ABI:armeabi-v7a和x86,因为是在模拟器里跑所以需要x86的ABI

缩略图如下:

%title插图%num

然后使用ndk-build编译,需要在jni目录下执行ndk-build指令,还是在终端下进行:

ndk-build

%title插图%num

可以看到jni同级目录下会生成libs目录,结构如下图:

%title插图%num

再然后需要去MainActivity.java中加载这个so文件

代码如下:

package com.example.jnistaticregister;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    public native String YourJNI();//使用native关键字定义一个方法YourJNI

    static {
        System.loadLibrary("MyJNI");    //加载生成的so文件
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TextView textView = findViewById(R.id.textView);
                String content = YourJNI();
                textView.setText(content);
            }
        });
    }
}

最后点击AS绿色启动按钮,该按钮点击后会自动运行模拟器同时安装并运行APP

效果如下:

%title插图%num

发表回复