• Android NDK开发----- JNI多线程


    一、概述

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

    二、要求

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

    三、实现

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

    复制代码
     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 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 #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 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输出如下:

  • 相关阅读:
    Linux同一机器设置多个IP2019-7-6
    使用Apache服务部署静态网站2019-7-5
    系统状态检测命令2019-7-5
    简单的shell脚本
    常用的系统工作命令2019-7-4
    Lnmp架构部署动态网站环境.2019-7-3-1.4
    Lnmp架构部署动态网站环境.2019-7-3-1.3
    Linux安装ftp服务-详细步骤
    循环删除List集合的元素
    反射-父类获取子类属性并赋值
  • 原文地址:https://www.cnblogs.com/fnlingnzb-learner/p/7366043.html
Copyright © 2020-2023  润新知