Android NDK开发(2)----- JNI多线程

3 篇文章 0 订阅

一、概述

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


二、要求

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


三、实现

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button 
        android:id="@+id/button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="启动JNI线程"
        />

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

package com.sym.mythread;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	Button button;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		button = (Button) findViewById(R.id.button);
		button.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				//调用JNI中的函数来启动JNI中的线程
				mainThread();
			}
		});
		//初始化JNI环境
		setJNIEnv();
	}
	
	private static void fromJNI(int i){
		Log.v("Java------>", ""+i);
	}
	
	//本地方法
	private native void mainThread();
	private native void setJNIEnv();
	static{
		//加载动态库
		System.loadLibrary("JNIThreads");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

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

#include<jni.h>
#include<android/log.h>

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "native-activity", __VA_ARGS__))

//线程数
#define NUMTHREADS 5

//全局变量
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;


void *thread_fun(void* arg)
{
    JNIEnv *env;
    jclass cls;
    jmethodID mid;

    //Attach主线程
    if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
    {
        LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
        return NULL;
    }
    //找到对应的类
    cls = (*env)->GetObjectClass(env,g_obj);
    if(cls == NULL)
    {
        LOGE("FindClass() Error.....");
        goto error;
    }
    //再获得类中的方法
    mid = (*env)->GetStaticMethodID(env, cls, "fromJNI", "(I)V");
    if (mid == NULL)
    {
//        LOGE("GetMethodID() Error.....");
        goto error;
    }
    //最后调用java中的静态方法
        (*env)->CallStaticVoidMethod(env, cls, mid ,(int)arg);


error:
    //Detach主线程
    if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
    {
        LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
    }


    pthread_exit(0);
}

//由java调用以创建子线程
JNIEXPORT void Java_com_sym_mythread_MainActivity_mainThread( JNIEnv* env, jobject obj)
{
    int i;
    pthread_t pt[NUMTHREADS];

    for (i = 0; i < NUMTHREADS; i++)
        //创建子线程
        pthread_create(&pt[i], NULL, &thread_fun, (void *)i);
}


//由java调用来建立JNI环境
JNIEXPORT void Java_com_sym_mythread_MainActivity_setJNIEnv( JNIEnv* env, jobject obj)
{
    //保存全局JVM以便在子线程中使用
    (*env)->GetJavaVM(env,&g_jvm);
    //不能直接赋值(g_obj = obj)
    g_obj = (*env)->NewGlobalRef(env,obj);
}


//当动态库被加载时这个函数被系统调用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    //获取JNI版本
    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
    {
        LOGE("GetEnv failed!");
            return result;
    }


    return JNI_VERSION_1_4;
}

最后,编写Android.mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := JNIThreads
LOCAL_SRC_FILES := JNIThreads.c

LOCAL_LDLIBS    := -llog

include $(BUILD_SHARED_LIBRARY)
运行该程序:



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






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值