20.Eclipse下Ndk开发(pthread开启线程调用Java方法)
本项目最终的目的是在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)

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
04.Java网络编程(转载)
1.网络编程 1.1计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定的物理设备将处于不同位置的计算机连接起来组成的网络,这个网络中包含的设备有:计算机、路由器、交换机等等。 其实从软件编程的角度来说,对于物理设备的理解不需要很深刻,就像你打电话时不需要很熟悉通信网络的底层实现是一样的,但是当深入到网络编程的底层时,这些基础知识是必须要补的。 路由器和交换机组成了核心的计算机网络,计算机只是这个网络上的节点以及控制等,通过光纤、网线等连接将设备连接起来,从而形成了一张巨大的计算机网络。 网络最主要的优势在于共享:共享设备和数据,现在共享设备最常见的是打印机,一个公司一般一个打印机即可,共享数据就是将大量的数据存储在一组机器中,其它的计算机通过网络访问这些数据,例如网站、银行服务器等等。 如果需要了解更多的网络硬件基础知识,可以阅读《计算机网络》教材,对于基础进行强化,这个在基础学习阶段不是必须的,但是如果想在网络编程领域有所造诣,则是一个必须的基本功。 对于网络编程来说,最主要的是计算机和计算机之间的通信,这样首要的问题就...
- 下一篇
Eclipse的Java build path,可以将一个项目依赖于另外一个项目
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/79523320 我一直想要解决一个问题,就是找到一个模块化的方法,把两个项目中公用的代码抽离,比如说数据库表对应文件的DAO、entity,以及对应的service,但没有找到我想要的那种方案——我也不知道该怎么描述我想要的这种方案,总之就像插件一样吧! 但Eclipse中有一种下策,就是利用Java build path将另外一个项目加入到依赖项目当中。 引入被依赖项目ymeng后,假如说ymeng项目中有一个公共类/ymeng/src/main/java/com/cmower/common/util/DateUtil.java,那么就可以在新的项目中直接使用。 不知道有没有更合理的办法,因为对于ymeng项目来说,我只需要其部分代码,而不是全部代码。 Life is not always what we want it to be. We fight, we cry, and sometimes we give up. But there is ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装Nodejs环境
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Linux系统CentOS6、CentOS7手动修改IP地址