提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、宏定义日志输出
//宏函数定义,调用 头文件#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);
}