项目目录

CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
add_library(hello-jnicallback SHARED
hello-jnicallback.c)
# Include libraries needed for hello-jnicallback lib
target_link_libraries(hello-jnicallback
android
log)
hello-jnicallback.c
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include <jni.h>
#include <android/log.h>
#include <assert.h>
// Android log function wrappers,对android log进行了封装
static const char* kTAG = "hello-jniCallback";
#define LOGI(...) \
((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))
// processing callback to handler class,上下文
typedef struct tick_context {
JavaVM *javaVM;
jclass jniHelperClz;--------->对应于JniHandler
jobject jniHelperObj;
jclass mainActivityClz;------>对应于MainActivity
jobject mainActivityObj;
pthread_mutex_t lock;
int done;
} TickContext;
TickContext g_ctx;
/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* hello-jniCallback/app/src/main/java/com/example/hellojnicallback/MainActivity.java
*/
JNIEXPORT jstring JNICALL
Java_com_example_hellojnicallback_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}
/*
* A helper function to show how to call
* java static functions JniHelper::getBuildVersion()
* java non-static function JniHelper::getRuntimeMemorySize()
* The trivial implementation for these functions are inside file
* JniHelper.java
这个展示了如何调用java的静态方法,非静态方法。
*/
void queryRuntimeInfo(JNIEnv *env, jobject instance) {
// Find out which OS we are running on. It does not matter for this app
// just to demo how to call static functions.
// Our java JniHelper class id and instance are initialized when this
// shared lib got loaded, we just directly use them
// static function does not need instance, so we just need to feed
// class and method id to JNI
jmethodID versionFunc = (*env)->GetStaticMethodID(
env, g_ctx.jniHelperClz,
"getBuildVersion", "()Ljava/lang/String;");//获取静态方法
if (!versionFunc) {
LOGE("Failed to retrieve getBuildVersion() methodID @ line %d",
__LINE__);
return;
}
jstring buildVersion = (*env)->CallStaticObjectMethod(env,
g_ctx.jniHelperClz, versionFunc);//调用静态的方法。
const char *version = (*env)->GetStringUTFChars(env, buildVersion, NULL);//把jsting转换为c类型的字符串
if (!version) {
LOGE("Unable to get version string @ line %d", __LINE__);
return;
}
LOGI("Android Version - %s", version);
/*
在调用 GetStringUTFChars 函数从 JVM 内部获取一个字符串之后,JVM 内部会分配一块新的内存,用于存储源字符串的拷贝,以便本地代码访问和修改。即然有内存分配,用完之后马上释放是一个编程的好习惯。通过调用ReleaseStringUTFChars 函数通知 JVM 这块内存已经不使用了,你可以清除了。注意:这两个函数是配对使用的,用了 GetXXX 就必须调用 ReleaseXXX,而且这两个函数的命名也有规律,除了前面的 Get 和 Release 之外,后面的都一样。
*/
(*env)->ReleaseStringUTFChars(env, buildVersion, version);//释放jstring。
/*
局部引用:
JNI 函数内部创建的 jobject 对象及其子类( jclass 、 jstring 、 jarray 等) 对象都是局部引用,它们在 JNI 函数返回后无效;
一般情况下,我们应该依赖 JVM 去自动释放 JNI 局部引用;但下面两种情况必须手动调用 DeleteLocalRef() 去释放:
-
(在循环体或回调函数中)创建大量 JNI 局部引用,即使它们并不会被同时使用,因为 JVM 需要足够的空间去跟踪所有的 JNI 引用,所以可能会造成内存溢出或者栈溢出;
-
如果对一个大的 Java 对象创建了 JNI 局部引用,也必须在使用完后手动释放该引用,否则 GC 迟迟无法回收该 Java 对象也会引发内存泄漏.
全局引用:
全局引用允许你持有一个 JNI 对象更长的时间,直到你手动销毁;但需要显式调用 NewGlobalRef() 和 DeleteGlobalRef() :
*/
// we are called from JNI_OnLoad, so got to release LocalRef to avoid leaking
(*env)->DeleteLocalRef(env, buildVersion);
/*
方法签名
调用JNI的GetMethodID函数获取一个jmethodID时,需要传入一个方法名称和方法签名,方法名称就是在Java中定义的方法名,方法签名的格式为:(形参参数类型列表)返回值。

*/
// Query available memory size from a non-static public function
// we need use an instance of JniHelper class to call JNI
jmethodID memFunc = (*env)->GetMethodID(env, g_ctx.jniHelperClz,
"getRuntimeMemorySize", "()J");//获取成员函数
if (!memFunc) {
LOGE("Failed to retrieve getRuntimeMemorySize() methodID @ line %d",
__LINE__);
return;
}
jlong result = (*env)->CallLongMethod(env, instance, memFunc);
LOGI("Runtime free memory size: %" PRId64, result);
(void)result; // silence the compiler warning
}
/*
* processing one time initialization:
* Cache the javaVM into our context
* Find class ID for JniHelper
* Create an instance of JniHelper
* Make global reference since we are using them from a native thread
* Note:
* All resources allocated here are never released by application
* we rely on system to free all global refs when it goes away;
* the pairing function JNI_OnUnload() never gets called at all.
*/
/*
实现JNI中本地函数注册有两种方式:
1.采用默认的本地函数注册流程。
2.自己重写JNI_OnLload()函数。
当Android的VM执行到C组件(*so)里的System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数,其用途有二
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.javaVM = vm;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR; // JNI version not supported.
}
jclass clz = (*env)->FindClass(env,
"com/example/hellojnicallback/JniHandler");
g_ctx.jniHelperClz = (*env)->NewGlobalRef(env, clz);----->获取并保存获取的java类
jmethodID jniHelperCtor = (*env)->GetMethodID(env, g_ctx.jniHelperClz,
"<init>", "()V");-------->获取int的方法
jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz,
jniHelperCtor);----->使用获取的init的方法,进行构造相应的对象
g_ctx.jniHelperObj = (*env)->NewGlobalRef(env, handler);
queryRuntimeInfo(env, g_ctx.jniHelperObj);
g_ctx.done = 0;
g_ctx.mainActivityObj = NULL;
return JNI_VERSION_1_6;
}
/*
* A helper function to wrap java JniHelper::updateStatus(String msg)
* JNI allow us to call this function via an instance even it is
* private function.
发送一个Java的消息
*/
void sendJavaMsg(JNIEnv *env, jobject instance,
jmethodID func,const char* msg) {
jstring javaMsg = (*env)->NewStringUTF(env, msg);
(*env)->CallVoidMethod(env, instance, func, javaMsg);
(*env)->DeleteLocalRef(env, javaMsg);
}
/*
* Main working thread function. From a pthread,
* calling back to MainActivity::updateTimer() to display ticks on UI
* calling back to JniHelper::updateStatus(String msg) for msg
两个callback,一个展示时钟在UI界面上,一个回调发送消息
*/
void* UpdateTicks(void* context) {
TickContext *pctx = (TickContext*) context;
JavaVM *javaVM = pctx->javaVM;
JNIEnv *env;
jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6);
if (res != JNI_OK) {
res = (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
if (JNI_OK != res) {
LOGE("Failed to AttachCurrentThread, ErrorCode = %d", res);
return NULL;
}
}
jmethodID statusId = (*env)->GetMethodID(env, pctx->jniHelperClz,
"updateStatus",
"(Ljava/lang/String;)V");//获取方法
sendJavaMsg(env, pctx->jniHelperObj, statusId,
"TickerThread status: initializing...");//发送消息正在初始化
// get mainActivity updateTimer function
jmethodID timerId = (*env)->GetMethodID(env, pctx->mainActivityClz,
"updateTimer", "()V");//获取更新的的方法
struct timeval beginTime, curTime, usedTime, leftTime;
const struct timeval kOneSecond = {
(__kernel_time_t)1,
(__kernel_suseconds_t) 0
};
sendJavaMsg(env, pctx->jniHelperObj, statusId,
"TickerThread status: start ticking ...");
while(1) {
gettimeofday(&beginTime, NULL);
pthread_mutex_lock(&pctx->lock);
int done = pctx->done;
if (pctx->done) {
pctx->done = 0;
}
pthread_mutex_unlock(&pctx->lock);
if (done) {
break;
}
(*env)->CallVoidMethod(env, pctx->mainActivityObj, timerId);
gettimeofday(&curTime, NULL);
timersub(&curTime, &beginTime, &usedTime);
timersub(&kOneSecond, &usedTime, &leftTime);
struct timespec sleepTime;
sleepTime.tv_sec = leftTime.tv_sec;
sleepTime.tv_nsec = leftTime.tv_usec * 1000;
if (sleepTime.tv_sec <= 1) {
nanosleep(&sleepTime, NULL);
} else {
sendJavaMsg(env, pctx->jniHelperObj, statusId,
"TickerThread error: processing too long!");
}
}
sendJavaMsg(env, pctx->jniHelperObj, statusId,
"TickerThread status: ticking stopped");//发送时钟消息
(*javaVM)->DetachCurrentThread(javaVM);
return context;
}
/*
* Interface to Java side to start ticks, caller is from onResume()
*/
JNIEXPORT void JNICALL
Java_com_example_hellojnicallback_MainActivity_startTicks(JNIEnv *env, jobject instance) {
pthread_t threadInfo_;
pthread_attr_t threadAttr_;
pthread_attr_init(&threadAttr_);
pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED);
pthread_mutex_init(&g_ctx.lock, NULL);
jclass clz = (*env)->GetObjectClass(env, instance);
g_ctx.mainActivityClz = (*env)->NewGlobalRef(env, clz);
g_ctx.mainActivityObj = (*env)->NewGlobalRef(env, instance);
int result = pthread_create( &threadInfo_, &threadAttr_, UpdateTicks, &g_ctx);
assert(result == 0);
pthread_attr_destroy(&threadAttr_);
(void)result;
}
/*
* Interface to Java side to stop ticks:
* we need to hold and make sure our native thread has finished before return
* for a clean shutdown. The caller is from onPause
*/
JNIEXPORT void JNICALL
Java_com_example_hellojnicallback_MainActivity_StopTicks(JNIEnv *env, jobject instance) {
pthread_mutex_lock(&g_ctx.lock);
g_ctx.done = 1;
pthread_mutex_unlock(&g_ctx.lock);
// waiting for ticking thread to flip the done flag
struct timespec sleepTime;
memset(&sleepTime, 0, sizeof(sleepTime));
sleepTime.tv_nsec = 100000000;
while (g_ctx.done) {
nanosleep(&sleepTime, NULL);
}
// release object we allocated from StartTicks() function
(*env)->DeleteGlobalRef(env, g_ctx.mainActivityClz);
(*env)->DeleteGlobalRef(env, g_ctx.mainActivityObj);
g_ctx.mainActivityObj = NULL;
g_ctx.mainActivityClz = NULL;
pthread_mutex_destroy(&g_ctx.lock);
}
Jnihandler.java
public class JniHandler {
/*
* Print out status to logcat
*/
@Keep
private void updateStatus(String msg) {
if (msg.toLowerCase().contains("error")) {
Log.e("JniHandler", "Native Err: " + msg);
} else {
Log.i("JniHandler", "Native Msg: " + msg);
}
}
/*
* Return OS build version: a static function
*/
@Keep
static public String getBuildVersion() {
return Build.VERSION.RELEASE;
}
/*
* Return Java memory info
*/
@Keep
public long getRuntimeMemorySize() {
return Runtime.getRuntime().freeMemory();
}
}
MainAcitivity.java
@Keep
private void updateTimer() {
++second;
if(second >= 60) {
++minute;
second -= 60;
if(minute >= 60) {
++hour;
minute -= 60;
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
String ticks = "" + MainActivity.this.hour + ":" +
MainActivity.this.minute + ":" +
MainActivity.this.second;
MainActivity.this.tickView.setText(ticks);
}
});
}
static {
System.loadLibrary("hello-jnicallback");
}
public native String stringFromJNI();
public native void startTicks();
public native void StopTicks();
本文详细介绍了Android中JNI的使用,包括如何通过JNI调用Java的静态和非静态方法,以及如何处理回调。示例代码展示了如何在C/C++中创建线程,定时更新UI,并与Java类进行交互。此外,还涵盖了JNI生命周期管理和资源释放,以及本地函数注册的两种方式。
824

被折叠的 条评论
为什么被折叠?



