Android NDK開發(2)----- 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輸出如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章