安卓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并新建一个空项目
随手布个局,在默认的activity中放一个按钮控件和一个textview控件,后面写个简单的按钮事件,点击按钮时调用c/c++代码中的方法,并将结果显示在textview控件
现在开始编辑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); } }
缩略图如下:
上面还未编写按钮事件相关的代码,现在编写按钮事件相关代码,即点击按钮时去调用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控件 } }); } }
缩略图如下:
接着开始生成YourJNI方法的c/c++代码,注意我截图中的目录,目录不能错,我们先生成头文件,通过javah指令在终端下生成
javah -jni com.example.jnistaticregister.MainActivity
回车后生成的头文件如下图:
建立一个jni目录,与java目录同级,并将生成的头文件移动到jni目录下,我嫌名称太长将com_example_jnistaticregister_MainActivity.h改成了MyJNI.h,如下图:
紧接着在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"); }
缩略图如下:
再接着在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
缩略图如下:
然后使用ndk-build编译,需要在jni目录下执行ndk-build指令,还是在终端下进行:
ndk-build
可以看到jni同级目录下会生成libs目录,结构如下图:
再然后需要去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
效果如下: