一、概述

JNI编程和Linux上的C/C++编程还是挺相似的,每次java调用JNI中的函数时都会传入有关JVM的一些参数(如JNIEnv,jobject),每次JNI回调java中的方法时都要通过JVM的有关参数来实现,当在JNI中涉及到多线程的话还是有一些不一样的地方,就是要在子线程函数里使用AttachCurrentThread()和DetachCurrentThread()这两个函数,在这两个函数之间加入回调java方法所需要的代码。

二、要求

掌握JNI多线程编程的方法。

三、实现

新建工程MyThread,修改main.xml文件,在里面只有一个Button,如下:

  1. 1 <?xml version="1.0" encoding="utf-8"?>
    2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3 android:layout_width="fill_parent"
    4 android:layout_height="fill_parent"
    5 android:orientation="vertical" >
    6
    7 <Button
    8 android:id="@+id/button"
    9 android:layout_width="fill_parent"
    10 android:layout_height="wrap_content"
    11 android:text="启动JNI线程"
    12 />
    13
    14 </LinearLayout>

修改MyThreadActivity.java文件,实现按钮的监听,在里面调用JNI中的函数来启动JNI中的线程,比较简单,如下:

  1. 1 package com.nan.thread;
    2
    3 import android.app.Activity;
    4 import android.os.Bundle;
    5 import android.util.Log;
    6 import android.view.View;
    7 import android.widget.Button;
    8
    9 public class MyThreadActivity extends Activity
    10 {
    11 private Button mButton = null;
    12
    13 /** Called when the activity is first created. */
    14 @Override
    15 public void onCreate(Bundle savedInstanceState)
    16 {
    17 super.onCreate(savedInstanceState);
    18 setContentView(R.layout.main);
    19
    20 mButton = (Button)this.findViewById(R.id.button);
    21 //按钮监听
    22 mButton.setOnClickListener(new View.OnClickListener()
    23 {
    24
    25 @Override
    26 public void onClick(View v)
    27 {
    28 // TODO Auto-generated method stub
    29 //调用JNI中的函数来启动JNI中的线程
    30 mainThread();
    31
    32 }
    33 });
    34 //初始化JNI环境
    35 setJNIEnv();
    36 }
    37
    38 //由JNI中的线程回调
    39 private static void fromJNI(int i)
    40 {
    41 Log.v("Java------>", ""+i);
    42 }
    43
    44 //本地方法
    45 private native void mainThread();
    46 private native void setJNIEnv();
    47
    48 static
    49 {
    50 //加载动态库
    51 System.loadLibrary("JNIThreads");
    52 }
    53
    54 }

编写JNI_Thread.c文件,在mainThread()函数里启动5个子线程,在子线程函数里回调java中的静态方法fromJNI()来输出当前子线程是第几个被启动的线程。完整的内容如下:

  1. 1 #include<stdio.h>
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 #include<pthread.h>
    5
    6 #include<jni.h>
    7 #include<android/log.h>
    8
    9 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
    10 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
    11 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))
    12
    13 //线程数
    14 #define NUMTHREADS 5
    15
    16 //全局变量
    17 JavaVM *g_jvm = NULL;
    18 jobject g_obj = NULL;
    19
    20
    21 void *thread_fun(void* arg)
    22 {
    23 JNIEnv *env;
    24 jclass cls;
    25 jmethodID mid;
    26
    27 //Attach主线程
    28 if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
    29 {
    30 LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
    31 return NULL;
    32 }
    33 //找到对应的类
    34 cls = (*env)->GetObjectClass(env,g_obj);
    35 if(cls == NULL)
    36 {
    37 LOGE("FindClass() Error.....");
    38 goto error;
    39 }
    40 //再获得类中的方法
    41 mid = (*env)->GetStaticMethodID(env, cls, "fromJNI", "(I)V");
    42 if (mid == NULL)
    43 {
    44 LOGE("GetMethodID() Error.....");
    45 goto error;
    46 }
    47 //最后调用java中的静态方法
    48 (*env)->CallStaticVoidMethod(env, cls, mid ,(int)arg);
    49
    50
    51 error:
    52 //Detach主线程
    53 if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
    54 {
    55 LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
    56 }
    57
    58
    59 pthread_exit(0);
    60 }
    61
    62 //由java调用以创建子线程
    63 JNIEXPORT void Java_com_nan_thread_MyThreadActivity_mainThread( JNIEnv* env, jobject obj)
    64 {
    65 int i;
    66 pthread_t pt[NUMTHREADS];
    67
    68 for (i = 0; i < NUMTHREADS; i++)
    69 //创建子线程
    70 pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
    71 }
    72
    73
    74 //由java调用来建立JNI环境
    75 JNIEXPORT void Java_com_nan_thread_MyThreadActivity_setJNIEnv( JNIEnv* env, jobject obj)
    76 {
    77 //保存全局JVM以便在子线程中使用
    78 (*env)->GetJavaVM(env,&g_jvm);
    79 //不能直接赋值(g_obj = obj)
    80 g_obj = (*env)->NewGlobalRef(env,obj);
    81 }
    82
    83
    84 //当动态库被加载时这个函数被系统调用
    85 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
    86 {
    87 JNIEnv* env = NULL;
    88 jint result = -1;
    89
    90 //获取JNI版本
    91 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
    92 {
    93 LOGE("GetEnv failed!");
    94 return result;
    95 }
    96
    97 return JNI_VERSION_1_4;
    98 }

最后,编写Android.mk文件:

  1. 1 LOCAL_PATH := $(call my-dir)
    2
    3 include $(CLEAR_VARS)
    4
    5 LOCAL_MODULE := JNIThreads
    6 LOCAL_SRC_FILES := JNI_Threads.c
    7
    8 LOCAL_LDLIBS := -llog
    9
    10 include $(BUILD_SHARED_LIBRARY)

使用ndk-build成功编译出动态库后,运行该程序:

并点击2下按钮,LogCat输出如下:

Android NDK开发----- JNI多线程的更多相关文章

  1. Android NDK开发 JNI操作java构造方法,普通方法,静态方法(七)

    Android NDK开发 JNI操作java普通.静态.构造方法 1.Jni实例化一个Java类的实例jobject 1.通过FindClas( ),获取Java类的的jclass 2.通过GetM ...

  2. Android NDK开发 Jni中打日志LOG(二)

    HelloJni.c文件中,加入头文件和函数声明.最终文件如下: #include <jni.h> #include <string.h> #include<androi ...

  3. Android NDK开发 Jni中Debug(三)

    下载LLDB 配置Android Native - Debugger 调式结果如下 #include <jni.h> #include <string.h> #include& ...

  4. Android NDK开发 JNI类型签名和方法签名(六)

    在Java存在两种数据类型: 基本类型 和 引用类型 ,大家都懂的 . 在JNI的世界里也存在类似的数据类型,与Java比较起来,其范围更具严格性,如下: 1.primitive types ---- ...

  5. Android NDK开发Hello Word!

    在之前的博客中已经为大家介绍了,如何在win环境下配置DNK程序,本篇我将带大家实现一个简单的Hello jni程序,让大家真正感受一下NDK开发的魅力.这里我们选择使用C+JAVA开发Android ...

  6. Android NDK开发初识

    神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...

  7. Android NDK开发

    Android NDK 开发教程(极客学院) 一.Android NDK环境搭建 使用最新ndk,直接抛弃cygwin,以前做Android的项目要用到NDK就必须要下载NDK,下载安装Cygwin( ...

  8. Android NDK 开发(四)java传递数据到C【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处 ...

  9. Android NDK 开发(三)--常见错误锦集合Log的使用【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41826511  Android NDK开发经常因某些因素会出现一些意想不到的错误, ...

随机推荐

  1. TeX中的引号(UVa272)

    问题: 在Tex中,做双引号的" `` ",右双引号是"  '' "(两个回车左边的).输入一篇包含双引号的文章,你的任务是把它转换成TeX的格式. 样例输入: ...

  2. Dockerfile 备份

    dotnet core app FROM microsoft/dotnet:1.1.0-runtime WORKDIR /mvcApp COPY ./out . ENTRYPOINT ["d ...

  3. lr如何屏蔽全局变量的影响

    首先要熟悉C语言的全局变量和局部变量的含义: C语言中的变量详解 先说说变量的作用域,比如,在函数中,形参变量只是在被调用期间才分配内存单元,调用结束立即释放.这就说明形参变量只有在函授内才是有效的, ...

  4. 基于Ubuntu系统搭建以太坊go-ethereum源码的开发环境

    第一.先安装geth的CLI环境sudo apt-get install geth,这个很重要 第二.下载源代码 git clone https://github.com/ethereum/go-et ...

  5. thinkphp5.0模块设计

    5.0版本对模块的功能做了灵活设计,默认采用多模块的架构,并且支持单一模块设计,所有模块的命名空间均以app作为根命名空间(可配置更改). 目录结构 标准的应用和模块目录结构如下: ├─applica ...

  6. 洛谷P2408 不同字串个数 [后缀数组]

    题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...

  7. Spring Cloud Config 使用总结

    Spring Cloud Config 使用总结 源码 https://github.com/ChangMuChen/Spring-Boot/tree/master/studies/sourcecod ...

  8. 在ASP.NET中实现图片、视频文件上传方式

    一.图片 1.在前端用<asp:FileUpload ID="UpImgName" runat="server"/>控件 2.在后台.cs中写上 p ...

  9. BZOJ1038 瞭望塔

    学习了半平交面. 我这里写的是训练指南中的双端队列,每次判断是否删去更优然后更新. 看hzwer中有一处不太明白就是为何要将两段加入队列 后来对拍出错才知道是因为精度,当两线重合时他们叉积返回值是一个 ...

  10. luoguP4457 [BJOI2018]治疗之雨 概率期望 + 高斯消元

    应该是最后一道紫色的概率了....然而颜色啥也代表不了.... 首先看懂题意: 你现在有$p$点体力,你的体力上限为$n$ 在一轮中, 1.如果你的体力没有满,你有$\frac{1}{m + 1}$的 ...