NDK sample之一HellojniCallback

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、宏定义日志输出

//宏函数定义,调用 头文件#include <android/log.h>中的日志函数
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__))

在上面就可以看出 宏函数中的参数 … 与 __VA_ARGS__对应

二、定义全局变量

// processing callback to handler class
typedef struct tick_context {
    
    
    JavaVM  *javaVM;  //JVM的引用,便于Native thread 回掉Java层方法
    jclass   jniHelperClz; // jni类型的全局引用
    jobject  jniHelperObj;
    jclass   mainActivityClz;
    jobject  mainActivityObj;
    pthread_mutex_t  lock; //互斥锁
    int      done;
} TickContext;
TickContext g_ctx;

2.1 初始化全局变量

准备工作:

public class JniHandler {
    
    
    @Keep  //注意,这里的注解重要,保证编译时期,该方法不会被移除
    private void updateStatus(String msg) {
    
    
        if (msg.toLowerCase().contains("error")) {
    
    
            Log.e("JniHandler", "Native Err: " + msg);
        } else {
    
    
            Log.i("JniHandler", "Native Msg: " + msg);
        }
    }
    
    @Keep
    static public String getBuildVersion() {
    
    
        return Build.VERSION.RELEASE;
    }
    @Keep
    public long getRuntimeMemorySize() {
    
    
        return Runtime.getRuntime().freeMemory();
    }
}

接下来的Native层会调用该方法。
开始初始化:

//#define JNIIMPORT
//#define JNIEXPORT  __attribute__ ((visibility ("default")))
//#define JNICALL
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVm* vm, void* reserved){
    
    
	JNIEnv* env;
	//将TickContext g_ctx;内存空间中的值重置为0
	memset(&g_ctx,0,sizeof(g_ctx));
	//将JavaVm记录到全局变量
	g_ctx.javaVm = vm;
	//判断JNI版本号
	if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
    
    
        return JNI_ERR; // JNI version not supported.
    }
	
	//获取Java类对象和类字节码
	jclass clazz = (*env)->FindClass(env,"com/example/hellojnicallback/JniHandler");
	//针对字节码初始化为全局变量
	g_ctx.jniHelperClz = (*env)->NewGlobalRef(env,clazz);
	//获取字节码内的无参构造方法
	jmethodID constructor = (*env)->GetMethodId(env,g_ctx.jniHelperClz,"<init>","()V");
	//执行构造方法获取Java类的实例对象
	jobject jhandler = (*env)->NewObject(env,g_ctx.jniHelperClz,constructor);
	//将jobject转化为全局引用
	g_ctx.jniHelperObj = (*env)->NewGlobalRef(env,jhandler);
	//使用GlobalRef对象,在下一小节进行分期
	queryRuntimeInfo(env, g_ctx.jniHelperObj);
	g_ctx.done = 0;
	g_ctx.mainActivityObj = NULL;
	return JNI_VERSION_1_6;
}

2.2 回调Java层方法

// instance , Java层一个对象的引用
void queryRuntimeInfo(JNIEnv* env, jobject instance){
    
    
	//获取方法
	jmethodID versionFunc = (*env)->GetMethodID(evn,g_ctx.jniHelperClz,"getBuildVersion","()Ljava/lang/String;");
	//检查获取方法时候成功
	if(!versionFunc){
    
    
		//__LINE__ 宏定义,当前报错的行号
		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);
	//打印版本信息
	LOGI("Android Version - %s",version);
	//释放字符串资源
	(*env)->RealeaseStringUTFChars(env,buildVersion,version);
	(*env)->DeleteLocalRef(env,buildVersion);
	
   	//接下来调用非静态方法
    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
}

三、通过回掉实现计时器的显示

3.1 Java层代码


public class MainActivity extends AppCompatActivity {
    
    
    @Keep
    private void updateTimer() {
    
    
        //统计回掉次数,并转化为时间;每调用一次的时间间隔为1s
        ...
    }
    static {
    
    
        System.loadLibrary("hello-jnicallback");
    }
	
	//开始计时器
    public native void startTicks();
    //暂停计时器
    public native void StopTicks();
}

3.2 native层的实现

JNIEXPORT void JNICALL
Java_com_example_hellojnicallback_MainActivity_startTicks(JNIEnv *env, jobject instance) {
    
    
	//要创建的子线程ID
    pthread_t       threadInfo_;
    //声明创建子线程时,传入的属性参数
    pthread_attr_t  threadAttr_;
    //初始化属性参数,并设置为DETACHED状态
    pthread_attr_init(&threadAttr_);
    pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED);
	
	//初始化同步锁
    pthread_mutex_init(&g_ctx.lock, NULL);

	//获取Java层引用
    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;
}

子线程UpdateTicks的具体实现:

/*
 * 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
 */
void*  UpdateTicks(void* context) {
    
    
	//转换解析,子线程接收到参数
    TickContext *pctx = (TickContext*) context;
    JavaVM *javaVM = pctx->javaVM;
    JNIEnv *env;
    //判断当先线程是否 Attach到JVM上,如果没有,则需要显示Attach,为的是能够使改线程可以调用Java的方法
    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;
        }
    }
	
	//获取Java层方法对应的MethodId
    jmethodID statusId = (*env)->GetMethodID(env, pctx->jniHelperClz,
                                             "updateStatus",
                                             "(Ljava/lang/String;)V");
    sendJavaMsg(env, pctx->jniHelperObj, statusId,
                "TickerThread status: initializing...");

    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);
        // pctx->done的值为非0,则退出循环
        if (done) {
    
    
            break;
        }
        (*env)->CallVoidMethod(env, pctx->mainActivityObj, timerId);
		//获取当前的时间
        gettimeofday(&curTime, NULL);
        //将线程执行时间存入到usedTime
        timersub(&curTime, &beginTime, &usedTime);
        //将还需要等待的值存入到leftTime
        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");
    //将当前native线程从VM中进行Detach操作            
    (*javaVM)->DetachCurrentThread(javaVM);
    return context;
}

停止计时

JNIEXPORT void JNICALL
Java_com_example_hellojnicallback_MainActivity_StopTicks(JNIEnv *env, jobject instance) {
    
    
	//通过互斥锁,将循环标记设置为1
    pthread_mutex_lock(&g_ctx.lock);
    g_ctx.done = 1;
    pthread_mutex_unlock(&g_ctx.lock);

 	//通过循环等待,确定计时器退出循环后,才继续执行
    struct timespec sleepTime;
    memset(&sleepTime, 0, sizeof(sleepTime));
    sleepTime.tv_nsec = 100000000;
    while (g_ctx.done) {
    
    
        nanosleep(&sleepTime, NULL);
    }

    //清楚全局引用
    (*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);
}

猜你喜欢

转载自blog.csdn.net/dirksmaller/article/details/108737847
ndk