您现在的位置是:首页 > 文章详情

20.Eclipse下Ndk开发(pthread开启线程调用Java方法)

日期:2018-03-11点击:500

本项目最终的目的是在pthread线程中,调用Java一个工具类得到多个uuid,然后调用类中另一个方法弹出toast,实现在c中获取安卓上下文对象Context

编译native方法,生成头文件的一系列过程不再赘述,直接上代码,都在注释中

PosixUtils:

package com.example.ndk_pthread; public class PosixUtils { static{ System.loadLibrary("ndk_pthread"); } /** * pthread开启子线程前的一些初始化操作,比如获取本类的jclass对象,生成需要的 * 全局引用等等,在子线程中无法获取到类的jclass对象,就是这行代码,获取class必须要在主线程中 * jclass uuidutils_class_tmp = (*env)->FindClass(env,"com/example/ndk_pthread/UUIDUtils"); */ public native void init(); /** * 执行一些善后的操作,比如init方法中生成的全局引用的销毁等等 */ public native void destroy(); /** * 子线程操作 */ public native void pthread(); } 

UUIDUtils:

package com.example.ndk_pthread; import java.util.UUID; import android.content.Context; import android.widget.Toast; public class UUIDUtils { public static String get(){ return UUID.randomUUID().toString(); } public static void showToast(Context context){ Toast.makeText(context, "c端弹出了toast", 0).show(); } } 

编译生成的头文件com_example_ndk_pthread_UUIDUtils.h:

package com.example.ndk_pthread; import java.util.UUID; import android.content.Context; import android.widget.Toast; public class UUIDUtils { public static String get(){ return UUID.randomUUID().toString(); } public static void showToast(Context context){ Toast.makeText(context, "c端弹出了toast", 0).show(); } } 

MainActivity:

package com.example.ndk_pthread; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; public class MainActivity extends Activity { private PosixUtils p; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); p = new PosixUtils(); p.init(); } public void start(View btn){ p.pthread(); } @Override protected void onDestroy() { super.onDestroy(); //页面销毁时同时销毁c端的一些东西 p.destroy(); } } 

ndk_pthread.c

#include "com_example_ndk_pthread_PosixUtils.h" #include <stdio.h> #include <pthread.h> #include <android/log.h> #include <unistd.h> #define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"renzhenming",FORMAT,##__VA_ARGS__); #define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"renzhenming",FORMAT,##__VA_ARGS__); JavaVM *javaVM; jobject uuidutils_jcls; jmethodID get_mid; jmethodID toastId; jobject jcontext; //动态库加载时会被调用执行,不需要我们手动调用 //兼容Android SDK 2.2之后,2.2没有这个函数 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){ LOGI("%s","JNI_OnLoad"); javaVM = vm; return JNI_VERSION_1_4; } /** * 目标:调用UUIDUtils类中的方法得到一个uuid */ void thread_fun(void* arg){ //得到这个类UUIDUtils,需要用到JNIEnv,但是这是一个子线程,每个线程都有独立的JNIEnv,所以我们需要获取到 //这个线程的JNIEnv,通过JavaVM关联当前线程,获取当前线程的JNIEnv,(*javaVM)->AttachCurrentThread(javaVM,&env,NULL); //那么就需要先获取到JavaVM //如何获取JavaVM? //1.在JNI_OnLoad函数中获取 //2.(*env)->GetJavaVM(env,&javaVM); //的到JavaVM后,会得到env并赋值给变量 JNIEnv* env = NULL; (*javaVM)->AttachCurrentThread(javaVM,&env,NULL); char* no = (char*)arg; int i; for (i = 0; i < 5; ++i) { LOGI("thread %s, i:%d",no,i); jobject uuid = (*env)->CallStaticObjectMethod(env,uuidutils_jcls,get_mid); char* uuid_char = (*env)->GetStringUTFChars(env,uuid,NULL); LOGI("%s",uuid_char); if(i == 4){ goto end; } (*env)->ReleaseStringUTFChars(env,uuid,uuid_char); sleep(1); } end: //如果直接将这两行写在循环之后是有问题的,当i=4的时候,线程直接退出,导致没有执行DetachCurrentThread,报异常 (*javaVM)->DetachCurrentThread(javaVM); pthread_exit((void*)0); } /** * 当在JNI调用Android自带的类时,经常需要传入Context参数,那怎么在JNI层获取Context呢? * 我们知道Application和Activity是Context的子类,由于每个Activity对应的Context是不一样的, * 所以一般情况下我们使用Application的Context,它在整个程序中只有一个实例。所以现在问题就变成了 * 怎么在JNI中获取Application呢? * Android APP在启动时会创建一个Activity Thread作为主线程,只要程序存活,这个线程就一直存在, * 所以我们可以考虑从Activity Thread中获取Application,查看Activity Thread的源码发现, * 它提供了一个方法可以获取Application,如下: * * public Application getApplication() { * return mInitialApplication; * } * * 也就是说我们只需要获取到Activity Thread的对象即可,Activity Thread提供了一个静态方法用于获取其实例,如下: * * public static ActivityThread currentActivityThread() { * return sCurrentActivityThread; * } * * 至此获取Context的步骤已经很清晰了 */ JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_init (JNIEnv *env, jobject jobj){ /** * 打印uuid */ //获取class必须要在主线程中 jclass uuidutils_class_tmp = (*env)->FindClass(env,"com/example/ndk_pthread/UUIDUtils"); //创建全局引用 uuidutils_jcls = (*env)->NewGlobalRef(env,uuidutils_class_tmp); //获取jmethodId也可以在子线程中 get_mid = (*env)->GetStaticMethodID(env,uuidutils_jcls,"get","()Ljava/lang/String;"); /** * show toast */ //获取jmethodId也可以在子线程中 toastId = (*env)->GetStaticMethodID(env,uuidutils_jcls,"showToast","(Landroid/content/Context;)V"); //获取Activity Thread的实例对象 jclass activityThread = (*env)->FindClass(env,"android/app/ActivityThread"); jmethodID currentActivityThread = (*env)->GetStaticMethodID(env,activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;"); jobject at = (*env)->CallStaticObjectMethod(env,activityThread, currentActivityThread); //获取Application,也就是全局的Context jmethodID getApplication = (*env)->GetMethodID(env,activityThread, "getApplication", "()Landroid/app/Application;"); jobject context = (*env)->CallObjectMethod(env,at, getApplication); jcontext = (*env)->NewGlobalRef(env,context); } JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_destroy (JNIEnv *env, jobject jobj){ //释放全局引用 (*env)->DeleteGlobalRef(env,uuidutils_jcls); } JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_pthread (JNIEnv *env, jobject jobj){ LOGI("%s","begin"); //获取JavaVM的第一种方式,本项目中我们采用在JNI_OnLoad中获取的方式 //(*env)->GetJavaVM(env,&javaVM); pthread_t tid; pthread_create(&tid,NULL,thread_fun,(void*)"NO1"); //调用Java函数show toast (*env)->CallStaticVoidMethod(env,uuidutils_jcls,toastId,jcontext); } 

Android.mk

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ndk_pthread LOCAL_SRC_FILES := ndk_pthread.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) 
原文链接:https://yq.aliyun.com/articles/657424
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章