JNI线程

转载地址:http://blog.csdn.net/shaohuazuo/article/details/43149193


博客:http://blog.csdn.net/shaohuazuo/article/details/43149193
转载请注明出处:http://blog.csdn.net/shaohuazuo


1.Android线程介绍.

  1.线程的概念:

            这个是百度百科里的一段话.

      线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。

      一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

      另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,

      只拥有一点儿在运行中必不可少的资源, 但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

      一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。

      由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪阻塞运行三种基本状态。

      就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;

      阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。

      每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

    2.Android进程.

         在默认情况下,一个App内的各类别(Activity,BroadcastReceiver,Serverice等)都会在同一个进程中执行,

      而且这个进程由主线程负责执行,在Android里.一个app也可以同时运行多个进程,可以在manifest.xml文件中进行声明.

      再组件后面添加android:process="name" name是进程的名字.可以随意取名.

  3.Android线程.

        3.1 在android中为了保持良好的用户体验,Android主线程主用来和用户交互.并产生子线程完成一些幕后工作.

      3.2 有些后台用户的处理,比如播放音乐,下载图片,等一些耗时的工作就不能由主线程来完成了.

      3.3 解决方法由两个.一个是线程.一个是使用进程.在Android中.我们通常使用线程来解决这样的问题.

          也可以使用进程来解决.但是创建一个进程需要的资源和线程比,就和前面说的一样,一个是重量级的,一个是LWP的.

      3.4 选定Android多线程解决的问题的话.那么不可避免的就是线程安全.资源的竞争问题,Java层的解决方法

          和C层的解决原理是一样的.尽量不要产生竞争问题.如果由,则需要加锁保护.比如,java中的lock,同步,

          c中的信号量,互斥锁mutex和读写锁,spinlock等等.

      3.5 JNI是如何处理线程资源冲突问题的.请看第二节.


2.JavaVM对象和JNIEvn对象.

    1.JavaVM结构体.

            在Android环境中.定义两个主要的结构体.JavaVM和JavaEvn,在Java环境中.每个进程里可以诞生许多VM实例.

      每个实例会有一个JavaVM结构体实例和他对应.但是在Android环境中.每个进程只能诞生一个VM实例,

      所以只有一个JavaVM结构体对实例. 通常在VM加载*.so程序库时,

      会先调用JNI_OnLoad()函数,在JNI_OnLoad()函数中会将JavaVM指针对象保存到c层JNI的全局变量中.

    JavaVM对象是所有线程共享的.

    2.JNIEnv结构体

           2.1  JNIEnv对象,当Java线程调用到C层的JNI函数的时候.一定会进入VM,VM会产生一个相应的JNIEnv对象.

            这JNIEnv对象和线程是一一对应的关系.

    2.2   在调用JNIEnv中的函数时.多个线程调用的JNIEnv中的本地函数都是独立的.

            因为VM会为每个线程产生一个JNIEnv对象实体.

    2.3   在调用函数的时候如果不方便传递JNIEnv对象,可以先获取JavaVM对象,再使用GetEnv()函数获取JNIEnv对象.

    2.4   如果c/c++层自己创建的线程,就必须向VM替它诞生相应的JNIEnv对象.并且回传该对象的指针的值.


3.jclass, jmethodID和jfieldID

    3.1  功能

              jclass,jmethodID和jfieldID三者都指针.通过这个三个指针,能获取到java中的属性和方法.

    3.2  生命周期

              这个类被载入的时候,这些指针都是有效的.一直到这个类被卸载为止.

    3.3  作用域.

                jmethodID和jfieldID可以直接存储在c获取C++的全局变量中.它不需要NewGlobalRef()函数进行转换 .

        它们是线程和函数之间共享的对象.                

        但是jobject的所有子类,必须使用NewGlobalRef()方法转化才能生成一个全局对象的引用.

        它们默认情况下时局部的.不能函数共享,和多线程共享.

        另外还有两个对象, GetStringUTFChars()和GetBytesArrayElements()函数所回传的数据是不需要

        使用NewGlobalRef()转换的.


4.Java线程进入JNI本地函数. 

  4.1 介绍.

              在Android中不管是主线程还是子线程都能通过JNI标准调用c++或者C函数.

       同样C函数同样也可以调用到Android主线程和其他线程中的函数.

    4.2  Demo流程介绍.

              1. ResultValue 存储计算的结果信息的类.

       2. ActNative是用来执行计算动作的类.

       3. CounterNative它讲ResultValue对象传递给C层,并获取返回的一个数.

       4. JNI Module获取ConterNative传递的ResultValue对象之后.获取setValue()方法.

       5. JNI Module获取CounterNative对象的number对象.

       6. 并把这个值赋值给ResultValue对象.

  4.3 类图

  

  4.4 代码

               1. Java层代码  

MainActivity代码: 

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5. import android.view.Menu;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9. import android.widget.TextView;  
  10.   
  11. public class MainActivity extends Activity implements OnClickListener {  
  12.       
  13.     private Button runbtn;  
  14.     private Button exitbtn;  
  15.     private TextView counterView;  
  16.     private CounterNative cn;  
  17.   
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.activity_main);  
  22.         exitbtn = (Button) this.findViewById(R.id.exit);  
  23.         runbtn = (Button) this.findViewById(R.id.run);  
  24.           
  25.         counterView = (TextView) this.findViewById(R.id.counterView);  
  26.           
  27.         exitbtn.setOnClickListener(this);  
  28.         runbtn.setOnClickListener(this);  
  29.           
  30.         cn = new CounterNativesub1(); //这new的是子类对象.子类会先调用父类的构造函数,再调用自己的构造函数.  
  31.       
  32.     }  
  33.   
  34.     @Override  
  35.     public boolean onCreateOptionsMenu(Menu menu) {  
  36.         // Inflate the menu; this adds items to the action bar if it is present.  
  37.         getMenuInflater().inflate(R.menu.main, menu);  
  38.         return true;  
  39.     }  
  40.   
  41.     @Override  
  42.     public void onClick(View arg0) {  
  43.         int id = arg0.getId();  
  44.         switch (id) {  
  45.         case R.id.run:  
  46.            ActNative.nativeExec();  
  47.            counterView.setText(cn.result.getResult() + "");  
  48.             break;  
  49.         case R.id.exit:  
  50.             this.finish();  
  51.             break;  
  52.         default:  
  53.             this.finish();  
  54.             break;  
  55.         }  
  56.     }  
  57.   
  58. }  


   
[java]  view plain  copy
  1. ActNative.java代码  

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. public class ActNative {  
  4.     public static native void nativeExec();  
  5. }  

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. public abstract class CounterNative {  
  4.     private int number =10 ;  
  5.     public ResultValue result;  
  6.     static{  
  7.         System.loadLibrary("native");  
  8.     }  
  9.     public CounterNative()  
  10.     {  
  11.         number = getNumber();  
  12.         result = new ResultValue();  
  13.         nativeSetup(result);  
  14.     }  
  15.     public abstract int getNumber();  
  16.     private native void nativeSetup(Object obj);  
  17. }  

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. public class CounterNativesub1 extends CounterNative{  
  4.     @Override  
  5.     public int getNumber() {  
  6.         // TODO Auto-generated method stub  
  7.         return 13;  
  8.     }  
  9. }  
  10.    

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. import android.util.Log;  
  4.   
  5. public class ResultValue {  
  6.     private int result;  
  7.     private String mThreadName;  
  8.       
  9.     public int getResult() {  
  10.         return result;  
  11.     }  
  12.   
  13.     public void setResult(int result) {  
  14.       
  15.         this.result = result;  
  16.         Thread.currentThread().setName( Thread.currentThread().getName()+"-SetValue");  
  17.         mThreadName = Thread.currentThread().getName();  
  18.         Log.d("ResultValue",mThreadName);  
  19.           
  20.     }   
  21.       
  22.   
  23. }  

       2. Jni层代码C代码

[cpp]  view plain  copy
  1. #include "act.h"  
  2. #include "native.h"  
  3. #include <android/log.h>  
  4. #include <stdio.h>  
  5. //该过程中需要使用到的方法.  
  6. //1.GetFieldId()  
  7. //2.GetMethodID()  
  8. //3.GetIntField()  
  9. //4.CallVoidMethod();  
  10. //我们整理一下调用这些方法需要的参数  
  11. //jclass (JNICALL *GetObjectClass)  
  12. //     (JNIEnv *env, jobject obj);  
  13.   
  14. //1.  
  15. //jfieldID (JNICALL *GetFieldID)  
  16. //(JNIEnv *env, jclass clazz, const char *name, const char *sig);  
  17. //从GetFieldID方法中我们找到他需要jclass对象.  
  18. //我们只有CouterNative对象的jobject对象.  
  19. //那么我们可以使用GetObjectClass()方法来获取该对象.  
  20. //第一要获取的GetFieldID参数搞定.  
  21.   
  22. //2.   
  23. //jmethodID (JNICALL *GetMethodID)  
  24. //(JNIEnv *env, jclass clazz, const char *name, const char *sig);  
  25. //我们上层需要获取的的方法是setValue方法.这个方法的类是ResultValue.  
  26. //那么我们可以使用resultThis方法获取Class这对象的jclass对象.  
  27.   
  28. //3.jint (JNICALL *GetIntField)  
  29. //(JNIEnv *env, jobject obj, jfieldID fieldID);  
  30. //因为jobject对象都是局部对象.如果需要保存到全局变量中.  
  31. //需要使用NewGlobalRef函数进行转换.  
  32. //所有在natvieSetup中需要将this转换成全局变量.  
  33.   
  34. //4.void (JNICALL *CallVoidMethod)  
  35. //(JNIEnv *env, jobject obj, jmethodID methodID, ...);  
  36. //resultThis这个对象也需要使用NewGlobalRef转换成全局的变量.  
  37.   
  38. jfieldID number_id;  
  39. jmethodID setv_id;  
  40. jobject counter, result;  
  41.   
  42. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_CounterNative_nativeSetup  
  43.   (JNIEnv * env, jobject this, jobject resultThis)  
  44. {  
  45.     //获取jfieldID和jmethodID需要jclass对象.获取这个两个jclass对象.  
  46.   
  47.     __android_log_print(ANDROID_LOG_INFO,"zshh","aaaaaaaaa");  
  48.     jclass cnclz = (*env)->GetObjectClass(env,this);  
  49.     jclass resclz = (*env)->GetObjectClass(env,resultThis);  
  50.       
  51.     __android_log_print(ANDROID_LOG_INFO,"zshh","bbbbbbbb");  
  52.     number_id = (*env)->GetFieldID(env, cnclz,"number","I");  
  53.   
  54.     __android_log_print(ANDROID_LOG_INFO,"zshh","ccccccccc");  
  55.     setv_id = (*env)->GetMethodID(env,resclz,"setResult","(I)V");  
  56.       
  57.     __android_log_print(ANDROID_LOG_INFO,"zshh","ddddddddd");  
  58.     //将对象转换成全局对象.   
  59.     counter = (*env)->NewGlobalRef(env,this);  
  60.     result = (*env)->NewGlobalRef(env,resultThis);  
  61.     __android_log_print(ANDROID_LOG_INFO,"zshh","eeeeeeeeeee");  
  62. }  
  63.   
  64. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec  
  65.   (JNIEnv *env, jclass this)  
  66. {  
  67.     //获取number的值,累加10次之后.调用java层的setValue()设置给value  
  68.     //并在界面上显示出来.  
  69.     int i = 0;  
  70.     int number = (*env)->GetIntField(env,counter,number_id);  
  71.           
  72.     __android_log_print(ANDROID_LOG_INFO,"zshh","number = %d", number);  
  73.     for(i = 0 ; i<10; i++)   
  74.     {  
  75.         number += 10;  
  76.     }  
  77.       
  78.     __android_log_print(ANDROID_LOG_INFO,"zshh","number = %d", number);  
  79.     //调用setValue进行赋值.  
  80.     (*env) -> CallVoidMethod(env,result,setv_id,number);  
  81.       
  82.     //end  
  83. }  

       3. Android.mk

[cpp]  view plain  copy
  1. # Copyright (C) 2009 The Android Open Source Project  
  2. #  
  3. # Licensed under the Apache License, Version 2.0 (the "License");  
  4. # you may not use this file except in compliance with the License.  
  5. # You may obtain a copy of the License at  
  6. #  
  7. #      http://www.apache.org/licenses/LICENSE-2.0  
  8. #  
  9. # Unless required by applicable law or agreed to in writing, software  
  10. # distributed under the License is distributed on an "AS IS" BASIS,  
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  12. # See the License for the specific language governing permissions and  
  13. # limitations under the License.  
  14. #  
  15. SRC_PATH_ROOT:=$(LOCAL_PATH)/../../src    
  16. LOCAL_PATH := $(call my-dir)  
  17.   
  18. include $(CLEAR_VARS)  
  19.   
  20. LOCAL_MODULE    := native  
  21. LOCAL_SRC_FILES := native.c   
  22. LOCAL_LDLIBS := -llog  
  23. #LOCAL_SHARED_LIBRARIES :=libc  
  24. include $(BUILD_SHARED_LIBRARY)  

  4. 例程分析

         当Java层的主线程准备执nativeSetup函数时,VM就会产生一个JNIEnv机构体对象.这个对象专属主线程.

     接着将这个对象传递给nativeSetup()函数的第一参数. 如果由其他的线程也一样.VM也会替其他线程产生对应的JNIEnv对象.

     所以不同的线程进入JNI层c函数时,他们JNIEnv的参数是不同的, 但是同一个线程每次进入本地函数,

     他们使用的JNIEnv是同一个.这样我们可以避免多线程之间的线程安全问题.而且可以达成跨函数的数据共享.

         

作者:左少华
博客:http://blog.csdn.net/shaohuazuo/article/details/43149193
转载请注明出处:http://blog.csdn.net/shaohuazuo

5.JNI本地线程进入Java函数. 

   1.c中的线程如何进入Java层执行函数调用.

    在上面我们都是Java的线程通过JNI调用的C.我们需要看看C中的线程如何进入Java层进行调用.

    我们在C中生成一个线程.那么这个线程同样需要VM帮助我们去产生JNIEnv对象.

       再Java线程进入C本地函数是,这个对象自动VM自动创建的.那么C本地线程进入Java

       则需要向VM进行登记告示VM,VM会为这个C线程创建一个JNIEnv对象,之后这个线程就能进入Java层了.

  2 .Demo流程说明

        Activity : 用来显示Android线程计算的数据,和人机交互的界面.

        PrimeNative: 这个类是用来和C和java沟通的类.

     C模块:诞生四个线程计算质数.计算完成之后由注册的到java虚拟机中的线程调用java层的方法将数据显示出来.


        有了上面三个类.我们们看看整例子的流程.

        第一步.我们再Activity中实例话一个PrimeNative,这个时候会调用static{}静态代码块中的代码.

              就会去加载动态库,这个时候就会调用到C模块中JNI_OnLoad()函数,我们需要在这个代码中注册我们的线程到VM中.

        第二步.当上面完成之后.Java会执行构造函数中的代码.我们在构造函数中使用调用nativeSetup()函数,主要是为了

        C能获取jclass或者jmethodID,jfieldID对象,

        第三步.从java传递过来的参数.计算该参数范围内有那些是质数.然后把这些质数从C中返回到Android,然后Android

        在进行实现.

     第四步.将一个质数返回到Android过程如下,每当线程计算完毕,并且当前计算的是一个质数的话.我们就调用java层的

         函数将这个质数显示出来.


  3 .类图

   

  *上面是整个程序的框图,我们再看看c模块线程之间程序分析图.*

  .c层代码内存线程分析图.



 4 .代码

        例一: JNI单进程进入Java层调用

         我们C程序中只有一个进程,负责计算质数.当计算的数是质数的话,就调用callBack返回该质数显示到UI中.


1.java层代码.        

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5. import android.view.Menu;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9. import android.widget.TextView;  
  10.   
  11. public class MainActivity extends Activity implements OnClickListener {  
  12.       
  13.     private Button runbtn;  
  14.     private Button exitbtn;  
  15.     public static TextView counterView;  
  16.     private PrimeNative pn;  
  17.     public static MainActivity ref;  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         ref = this;   
  23.         setContentView(R.layout.activity_main);  
  24.         exitbtn = (Button) this.findViewById(R.id.exit);  
  25.         runbtn = (Button) this.findViewById(R.id.run);  
  26.         counterView = (TextView) this.findViewById(R.id.counterView);  
  27.         pn = new PrimeNative(10100);  
  28.         exitbtn.setOnClickListener(this);  
  29.         runbtn.setOnClickListener(this);  
  30.     }  
  31.   
  32.     @Override  
  33.     public boolean onCreateOptionsMenu(Menu menu) {  
  34.         // Inflate the menu; this adds items to the action bar if it is present.  
  35.         getMenuInflater().inflate(R.menu.main, menu);  
  36.         return true;  
  37.     }  
  38.   
  39.     @Override  
  40.     public void onClick(View arg0) {  
  41.         int id = arg0.getId();  
  42.         switch (id) {  
  43.         case R.id.run:  
  44.            ActNative.exec();  
  45.             break;  
  46.         case R.id.exit:  
  47.             this.finish();  
  48.             break;  
  49.         default:  
  50.             this.finish();  
  51.             break;  
  52.         }  
  53.     }  
  54.   
  55. }  

[html]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. public class ActNative {  
  4.     public static native void exec();  
  5. }  

[java]  view plain  copy
  1. package com.zuoshaohua.threadtest;  
  2.   
  3. import android.os.Handler;  
  4. import android.os.Message;  
  5.   
  6. public class PrimeNative {  
  7.     private int start;  
  8.     private int end;  
  9.   
  10.     private static Handler h;  
  11.     private static String thName;  
  12.     static {  
  13.         System.loadLibrary("prime");  
  14.     }  
  15.   
  16.     public PrimeNative(int start, int end) {  
  17.         this.start = start;  
  18.         this.end = end;  
  19.         if (start > 0 && start < end) {  
  20.             init(start, end); // 将需要计算的数据传递C层.  
  21.         }  
  22.         h = new Handler() {  
  23.             public void handleMessage(Message msg) {  
  24.                 MainActivity.ref  
  25.                         .setTitle("value = " + String.valueOf(msg.arg1));  
  26.                 MainActivity.ref.counterView.append(msg.arg1+", ");  
  27.             }  
  28.         };  
  29.     }  
  30.   
  31.     void callBack(int result) {  
  32.         Message m = h.obtainMessage(1, result, 3null);  
  33.         thName = Thread.currentThread().getName();  
  34.         h.sendMessage(m);  
  35.     }  
  36.   
  37.     private native void init(int start, int end);  
  38. }  

  2.JNI代码

[cpp]  view plain  copy
  1. /************************************************************************* 
  2.     > File Name: prime.c 
  3.     > Author: zuoshaohua 
  4.     > Mail: [email protected]  
  5.     > Created Time: Wed 28 Jan 2015 04:40:40 PM 
  6.  ************************************************************************/  
  7. #include"prime.h"  
  8. #include"act.h"  
  9. #include<android/log.h>  
  10. #include<stdio.h>  
  11. JavaVM * jvm;  
  12. jmethodID callbackId;  
  13. jobject  obj;  
  14. static int startNum;  
  15. static int endNum;  
  16. static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative";  
  17. static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative";  
  18.   
  19. static JNINativeMethod methodPrime[] = {      
  20.     {"init""(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup}  
  21. };  
  22.   
  23. static JNINativeMethod methodExec[] = {  
  24.     {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec}  
  25. };  
  26.   
  27.   
  28. static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){  
  29.   
  30.     jclass clz = (*env)->FindClass(env,className);                    //通过完整的类路径得jclass结构体.    
  31.     (*env)->RegisterNatives(env, clz, gMethods, numMethods);   
  32.     return JNI_TRUE;                                     
  33. }  
  34.   
  35. static int registerNatives(JNIEnv* env){  
  36.     registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0]));      
  37.     registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0]));      
  38.     return JNI_TRUE;  
  39.   
  40. }  
  41.   
  42. jint JNI_OnLoad(JavaVM* vm, void* reserved){  
  43.     JNIEnv *env;  
  44.     jvm = vm;  
  45.     if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体  
  46.         return -1;  
  47.     if (registerNatives(env) != JNI_TRUE)                             //注册我们的本地方法到VM中.  
  48.         return -1;  
  49.     return JNI_VERSION_1_4;  
  50. }  
  51.   
  52.   
  53. /* 
  54.  * Class:     com_zuoshaohua_threadtest_PrimeNative 
  55.  * Method:    nativeSetup 
  56.  * Signature: (II)V 
  57.  */  
  58. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup  
  59.   (JNIEnv *env, jobject this, jint start, jint end)  
  60. {     
  61.       
  62.     //获取start,和end.保存到一个起来.  
  63.     __android_log_print(ANDROID_LOG_INFO, "PrimeNative""start = %d, end =%d\n", start, end);  
  64.     startNum = start;  
  65.     endNum = end;  
  66.     //1. 获取jclass对象.  
  67.     jclass clz =(*env)->GetObjectClass(env, this);  
  68.     //2. 获取callBack方法.  
  69.     callbackId =  (*env)->GetMethodID(env,clz,"callBack","(I)V");      
  70.     //3. 将clz转化成全局变量.     
  71.     obj = (*env)->NewGlobalRef(env,this);      
  72. }  
  73.   
  74. int isPrime(int num)  
  75. {  
  76.     int i = 0;  
  77.     for(i = 2; i*i <= num; i++)     //这里必须是 i*i<= num, 比如25,如果 i*i<25,  
  78.     {                               //那么这个数就会被判为奇数.以为,i=5的时候. i*i = <span style="font-family:Courier New;">25,退出循环,</span>    
  79.         if( num%i == 0 ){  
  80.             return 0;  
  81.         }  
  82.     }  
  83.     return 1;  
  84. }  
  85.   
  86. /* 
  87.  * Class:     com_zuoshaohua_threadtest_ActNative 
  88.  * Method:    nativeExec 
  89.  * Signature: ()V 
  90.  */  
  91. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec  
  92. (JNIEnv * env, jclass clz)  
  93. {  
  94.     int i = 0 ;  
  95.     for(i  =startNum; i<endNum; i++)  
  96.     {  
  97.         if(isPrime(i))   
  98.         {  
  99.             __android_log_print(ANDROID_LOG_INFO, "ActNative""prime = %d", i);  
  100.             (*env)->CallVoidMethod(env,obj,callbackId,i);    //回调java层方法,将数据显示到Java页面.  
  101.         }  
  102.     }  
  103.     //我们调用java端代码将数据显示到Android界面上  
  104.     //那么我们需要将nativeSetup方法的调用对象转化成全局的对象并获取callBack方法的ID.  
  105.     __android_log_print(ANDROID_LOG_INFO, "ActNative""Java_com_zuoshaohua_threadtest_ActNative_nativeExec");  
  106.   
  107. }  


例二: 一个JNI本地线程进入Java执行调用.  

需要修改jni层的代码如下:

//arm-Linux-androideabi-g++: error: pthread: No such file or directory

    

[cpp]  view plain  copy
  1. /************************************************************************* 
  2.     > File Name: prime.c 
  3.     > Author: zuoshaohua 
  4.     > Mail: [email protected]  
  5.     > Created Time: Wed 28 Jan 2015 04:40:40 PM 
  6.  ************************************************************************/  
  7. #include"prime.h"  
  8. #include"act.h"  
  9. #include<android/log.h>  
  10. #include<stdio.h>  
  11. #include<pthread.h>  
  12. JavaVM * jvm;  
  13. jmethodID callbackId;  
  14. jobject  obj;  
  15. static pthread_t primeThread;  
  16. static int startNum;  
  17. static int endNum;  
  18. static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative";  
  19. static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative";  
  20. extern void* pthreadPirme( void*);  
  21. static JNINativeMethod methodPrime[] = {      
  22.     {"init""(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup}  
  23. };  
  24.   
  25. static JNINativeMethod methodExec[] = {  
  26.     {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec}  
  27. };  
  28.   
  29.   
  30. static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){  
  31.   
  32.     jclass clz = (*env)->FindClass(env,className);                    //通过完整的类路径得jclass结构体.    
  33.     (*env)->RegisterNatives(env, clz, gMethods, numMethods);   
  34.     return JNI_TRUE;                                     
  35. }  
  36.   
  37. static int registerNatives(JNIEnv* env){  
  38.     registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0]));      
  39.     registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0]));      
  40.     return JNI_TRUE;  
  41.   
  42. }  
  43.   
  44. jint JNI_OnLoad(JavaVM* vm, void* reserved){  
  45.     JNIEnv *env;  
  46.     jvm = vm;  
  47.     if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体  
  48.         return -1;  
  49.     if (registerNatives(env) != JNI_TRUE)                             //注册我们的本地方法到VM中.  
  50.         return -1;  
  51.     return JNI_VERSION_1_4;  
  52. }  
  53.   
  54.   
  55. /* 
  56.  * Class:     com_zuoshaohua_threadtest_PrimeNative 
  57.  * Method:    nativeSetup 
  58.  * Signature: (II)V 
  59.  */  
  60. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup  
  61.   (JNIEnv *env, jobject this, jint start, jint end)  
  62. {     
  63.       
  64.     //获取start,和end.保存到一个起来.  
  65.     __android_log_print(ANDROID_LOG_INFO, "PrimeNative""start = %d, end =%d\n", start, end);  
  66.     startNum = start;  
  67.     endNum = end;  
  68.     //1. 获取jclass对象.  
  69.     jclass clz =(*env)->GetObjectClass(env, this);  
  70.     //2. 获取callBack方法.  
  71.     callbackId =  (*env)->GetMethodID(env,clz,"callBack","(I)V");      
  72.     //3. 将clz转化成全局变量.     
  73.     obj = (*env)->NewGlobalRef(env,this);      
  74. }  
  75.   
  76. int isPrime(int num)  
  77. {  
  78.     int i = 0;  
  79.     for(i = 2; i*i <= num; i++)     //这里必须是 i*i<= num, 比如25,如果 i*i<25,  
  80.     {                               //那么这个数就会被判为奇数.以为,i=5的时候. i*i = 25,  
  81.                                     //但是它循环不会继续.      
  82.         if( num%i == 0 ){  
  83.             return 0;  
  84.         }  
  85.     }  
  86.     return 1;  
  87. }  
  88.   
  89. /* 
  90.  * Class:     com_zuoshaohua_threadtest_ActNative 
  91.  * Method:    nativeExec 
  92.  * Signature: ()V 
  93.  */  
  94. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec  
  95. (JNIEnv * env, jclass clz)  
  96. {  
  97.     int i = 0 ;  
  98. #if 0   
  99.     for(i  =startNum; i<endNum; i++)  
  100.     {  
  101.         if(isPrime(i))   
  102.         {  
  103.             __android_log_print(ANDROID_LOG_INFO, "ActNative""prime = %d", i);  
  104.             (*env)->CallVoidMethod(env,obj,callbackId,i);    //回调java层方法,将数据显示到Java页面.  
  105.         }  
  106.     }  
  107. #endif  
  108.     pthread_create(&primeThread,NULL,pthreadPirme,NULL);   
  109.     //我们调用java端代码将数据显示到Android界面上  
  110.     //那么我们需要将nativeSetup方法的调用对象转化成全局的对象并获取callBack方法的ID.  
  111.     __android_log_print(ANDROID_LOG_INFO, "ActNative""Java_com_zuoshaohua_threadtest_ActNative_nativeExec");  
  112.         pthread_join(primeThread, NULL);  
  113. <span style="font-family:Courier New;">        </span>//回收子进程.  
  114.       
  115. }  
  116. #if 0  
  117. #include <pthread.h>        //包含这个头文件.  
  118. int pthread_create(pthread_t *threadconst pthread_attr_t *attr,   
  119.         void *(*start_routine) (void *), void *arg);  
  120. Compile and link with -pthread.    //编译是需要链接这个动态库.  
  121. 如果您使用的是ubuntu系统,可以使用man pthread_create查看函数的说明.   
  122. 参数一: pthread_t类型的指针,它是一个全局区域的一个变量. 该方法会填充这个结构体.  
  123. 参数二: pthread_attr_t 它用来指定线程的属性.  
  124. 参数三: 他是线程执行的函数.  
  125. 参数四: 它是线程执行函数的参数.  
  126.   
  127.   
  128.  1.先GetEnv方法获取这个env对象.如果已经存在,则不需要执行AttachCurrentThread()了.如果不存在,则需要注册.   
  129.   
  130. #endif   
  131. void* pthreadPirme(void* args){  
  132.     int i;  
  133.     int status;  
  134.     JNIEnv *env;  
  135.     jboolean isAttached = JNI_FALSE;  
  136.     status = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_4);         
  137.     if(status < 0) {  
  138.         status = (*jvm)->AttachCurrentThread(jvm,&env, NULL);//将当前线程注册到虚拟机中.      
  139.         if(status < 0) return NULL;  
  140.         isAttached = JNI_TRUE;  
  141.     }  
  142.   
  143. <span style="font-family:Courier New;">       </span> for(i  =startNum; i<endNum; i++)  
  144.     {  
  145.         if(isPrime(i))   
  146.         {  
  147.             __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""prime = %d", i);  
  148.             (*env)->CallVoidMethod(env,obj,callbackId,i);//回调java层方法,将数据显示到Java页面.  
  149.         }  
  150.     }     
  151.     if(isAttached) (*jvm)->DetachCurrentThread(jvm);      //当前线程退出的话,讲线程从vm的注销.  
  152.     return NULL;  
  153. }  

6.JNI本地函数的多线程安全.

例三: 多个JNI本地线程进入Java执行调用. 

[cpp]  view plain  copy
  1. /************************************************************************* 
  2.     > File Name: prime.c 
  3.     > Author: zuoshaohua 
  4.     > Mail: [email protected]  
  5.     > Created Time: Wed 28 Jan 2015 04:40:40 PM 
  6.  ************************************************************************/  
  7. #include"prime.h"  
  8. #include"act.h"  
  9. #include<android/log.h>  
  10. #include<stdio.h>  
  11. #include<pthread.h>  
  12. #include <stdlib.h>  
  13. #define PROC  4  
  14. JavaVM * jvm;  
  15.   
  16. //回调callBack函数的id.  
  17. jmethodID callbackId;  
  18.   
  19. //这个对象是init函数传递下来的对象.  
  20. //Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup  
  21. //是从这个方法中得到的一个对象.  
  22. jobject  syncObj;  
  23.   
  24. //存储java传递过出来的开始num和结束num;  
  25. static int startNum;  
  26. static int endNum;  
  27.   
  28. //一定义一个结构体.   
  29. //num: 需要判断num是一个质数还是一个偶数.  
  30. //mutex: 他一个共享锁,用来避免线程之间的竞争问题.  
  31. //cond:  它是一个条件变量.  
  32. typedef struct item_t  
  33. {  
  34.     int num;  
  35.     pthread_mutex_t mutex;  
  36.     pthread_cond_t  cond;  
  37. }Item_t;  
  38.   
  39. //主进程会把需要计算的数存放到item中.  
  40. static Item_t * item;  
  41. static int count[PROC];  
  42.   
  43.   
  44. //函数的定义  
  45. static void parent(void);  
  46. static void * child(void *p);  
  47. jboolean prime(int num);   
  48. static int  getNum(void);  
  49. static void setNum(int i);  
  50.   
  51. static const char *classPathPrime = "com/zuoshaohua/threadtest/PrimeNative";  
  52. static const char *classPathAct = "com/zuoshaohua/threadtest/ActNative";  
  53.   
  54.   
  55. static JNINativeMethod methodPrime[] = {      
  56.     {"init""(II)V", (void *)Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup}  
  57. };  
  58.   
  59. static JNINativeMethod methodExec[] = {  
  60.     {"exec","()V", (void*) Java_com_zuoshaohua_threadtest_ActNative_nativeExec}  
  61. };  
  62.   
  63.   
  64. static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){  
  65.   
  66.     jclass clz = (*env)->FindClass(env,className);                    //通过完整的类路径得jclass结构体.    
  67.     (*env)->RegisterNatives(env, clz, gMethods, numMethods);   
  68.     return JNI_TRUE;                                     
  69. }  
  70.   
  71. static int registerNatives(JNIEnv* env){  
  72.     registerNativeMethods(env, classPathPrime, methodPrime, sizeof(methodPrime) / sizeof(methodPrime[0]));      
  73.     registerNativeMethods(env, classPathAct, methodExec, sizeof(methodExec) / sizeof(methodExec[0]));      
  74.     return JNI_TRUE;  
  75.   
  76. }  
  77.   
  78. jint JNI_OnLoad(JavaVM* vm, void* reserved){  
  79.     JNIEnv *env;  
  80.     jvm = vm;  
  81.     if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) //将JNI的版本设置为1.4,并通过jvm获取JNIEnv结构体  
  82.         return -1;  
  83.     if (registerNatives(env) != JNI_TRUE)                             //注册我们的本地方法到VM中.  
  84.         return -1;  
  85.     return JNI_VERSION_1_4;  
  86. }  
  87.   
  88.   
  89. /* 
  90.  * Class:     com_zuoshaohua_threadtest_PrimeNative 
  91.  * Method:    nativeSetup 
  92.  * Signature: (II)V 
  93.  */  
  94. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_PrimeNative_nativeSetup  
  95.   (JNIEnv *env, jobject this, jint start, jint end)  
  96. {     
  97.       
  98.     //获取start,和end.保存到一个起来.  
  99.     __android_log_print(ANDROID_LOG_INFO, "PrimeNative""start = %d, end =%d\n", start, end);  
  100.     startNum = start;  
  101.     endNum = end;  
  102.     //1. 获取jclass对象.  
  103.     jclass clz =(*env)->GetObjectClass(env, this);  
  104.     //2. 获取callBack方法.  
  105.     callbackId =  (*env)->GetMethodID(env,clz,"callBack","(I)V");      
  106.     //3. 将clz转化成全局变量.     
  107.     syncObj = (*env)->NewGlobalRef(env,this);      
  108. }  
  109.   
  110. /* 
  111.  * Class:     com_zuoshaohua_threadtest_ActNative 
  112.  * Method:    nativeExec 
  113.  * Signature: ()V 
  114.  */  
  115. JNIEXPORT void JNICALL Java_com_zuoshaohua_threadtest_ActNative_nativeExec  
  116. (JNIEnv * env, jclass clz)  
  117. {  
  118.     int64_t i;   
  119.     int j, ret, flag = 0;   
  120.     pthread_t  tid[PROC];   
  121.   
  122.     item = (Item_t*) malloc(sizeof(Item_t));      
  123.     if (item == NULL) {  
  124.         exit(1);  
  125.     }  
  126.     item->num = 0;  
  127.     pthread_mutex_init(&item->mutex, NULL);  
  128.     pthread_cond_init(&item->cond, NULL);  
  129.     <span style="color:#FF0000;">for (i=0;i<PROC;i++) {                                   //创建多个本地线程.  
  130.   
  131.         count[i] = 0;  
  132.         ret = pthread_create(tid+i, NULL, child, (void *)i);  
  133.         if (ret != 0) {  
  134.             exit(1);  
  135.         }  
  136.     }</span>  
  137.     <span style="background-color: rgb(102, 0, 204);">parent();                            //分发任务</span>  
  138.     for(i = 0;i<PROC;i++)  
  139.     {  
  140.         pthread_join(tid[i],NULL);  
  141.     }  
  142.     pthread_mutex_destroy(&item->mutex);  
  143.     pthread_cond_destroy(&item->cond);  
  144.     free(item);   
  145. }  
  146.   
  147.   
  148. //判断一个数是步是质数.如果是质素返回JNI_TRUE =1 ,否则返回JNI_FALSE  = 0 ;  
  149. jboolean prime(int num)     
  150. {  
  151.     int i = 0;  
  152.     for(i = 2;i * i <= num; i++)  
  153.     {  
  154.         if(num  % i == 0)  
  155.         {  
  156.             return JNI_FALSE;  
  157.   
  158.         }  
  159.     }  
  160.     return JNI_TRUE;  
  161. }  
  162.   
  163. //下面我们写一个getNum()和setNum()的辅助方法.  
  164. static int  getNum(void)  
  165. {  
  166.     int num;  
  167.     num  = item->num;  
  168.     return num;  
  169. }  
  170.   
  171. static void setNum(int i)  
  172. {  
  173.     item->num = i;     
  174.     return;  
  175. }  
  176.   
  177. //下面我们需要写一个子线程中需要处理的任务.  
  178. //p:是传入线程再线程组中的下标,也就是  
  179. static void * child(void *p)  
  180. {     
  181.     JNIEnv *env;  
  182.     int ret ,i;  
  183.     int64_t j;        
  184.     int status;   
  185.     jboolean isAttached = JNI_FALSE;  
  186.   
  187.     <span style="background-color: rgb(51, 204, 0);">status = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_4); //如果当前线程中没有JNIEnv对象,则执行注册   
  188.     if(status < 0) {  
  189.         status = (*jvm)->AttachCurrentThread(jvm,&env, NULL);//将当前线程注册到虚拟机中.      
  190.         if(status < 0) return NULL;  
  191.         isAttached = JNI_TRUE;  
  192.     }  
  193. </span>  
  194.     //我们需要由一个while的大循环,  
  195.     //它的退出条件是当主进程把数据分发完成之后.  
  196.     //主进程会把num设置为-1,并发送广播进行通知.让所有子进程退出.  
  197.       
  198.     j = (int64_t)p;    
  199.     while(1)  
  200.     {  
  201.         //num是一个共享变量,所有获取前必须lock  
  202.         ret = pthread_mutex_lock(&item->mutex);   
  203.         if(ret != 0)  
  204.         {  
  205.             __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""pthread_muext_lock error\n");  
  206.             exit(1);      
  207.         }  
  208.         while((i = getNum())==0)  
  209.         {  
  210.             //如果i==0,那么我们需要解锁等待.当条件满足的时候.  
  211.             //pthread_cond_wait()会进行加锁,他是一个原子操作.  
  212.             //这里为时候使用while而不使用if呢.是因为一次成功的加锁之后.  
  213.             //它调用getNum,不一定能获取需要计算的数.所有只能获取的数为0,那么需要进入睡眠等待.  
  214.             ret = pthread_cond_wait(&item->cond,&item->mutex);  
  215.             if(ret != 0)  
  216.             {  
  217.                 __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""pthread_muext_lock error\n");  
  218.                 exit(1);      
  219.             }  
  220.         }  
  221.         if(i == -1)  
  222.         {  
  223.            //我们需要解锁退出.  
  224.            ret = pthread_mutex_unlock(&item->mutex);  
  225.            if(ret != 0)  
  226.            {  
  227.               __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""pthread_mutex_unlock(&item->mutex)\n");  
  228.              exit(1);     
  229.            }  
  230.            break;   
  231.         }  
  232.         //如果不是上面的两种值.那么我们将num设置为0,  
  233.         //并发送广播通知正在等待的主进程,给num赋值.     
  234.         setNum(0);  
  235.         ret = pthread_cond_broadcast(&item->cond);  
  236.         if(ret != 0)  
  237.         {  
  238.               __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""pthread_cond_broadcast(&item->cond)");  
  239.              exit(1);     
  240.         }  
  241.         ret = pthread_mutex_unlock(&item->mutex);  
  242.         if(ret != 0)  
  243.         {  
  244.               __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""pthread_mutex_unlock(&item->mutex)");  
  245.              exit(1);     
  246.         }  
  247.         //如果是一个质数,那么他需要进入Java层,将这个质数显示的界面上.   
  248.         if(prime(i))  
  249.         {         
  250.             //以为可能同时有多个线程进入java执行callBack函数.所以为了线程安全,我们必须调用MonitorEnter  
  251.             //相当于是对这个对象做加锁操作.  
  252.             <span style="color:#FF0000;">(*env)->MonitorEnter(env,syncObj);   
  253.             (*env)->CallVoidMethod(env,syncObj,callbackId,i);//回调java层方法,将数据显示到Java页面.     
  254.             (*env)->MonitorEnter(env,syncObj);</span>  
  255.         }  
  256.         count[j]++;  
  257.   
  258.     }  
  259.     //打算该线程计算了多少个质数.      
  260.     __android_log_print(ANDROID_LOG_INFO,"child","pthreadPrime[%d]", count[j]);   
  261.     //任务完成退出当前线程.         
  262.     <span style="background-color: rgb(0, 153, 0);">if(isAttached) (*jvm)->DetachCurrentThread(jvm);      //再从线程退出之前.我们需要将线程从vm中注销.</span>  
  263.     pthread_exit((void *)NULL);   
  264. }  
  265.   
  266. //我们再分析一下父进程需要做的事情.  
  267. //1.产生4个线程.  
  268. //2.把需要计算质数方法num中.通知子线程获取数据进行计算.  
  269. //3.当分发完这些数据之后,需要将num 设置为-1,并发送广播通知子线程退出.  
  270.   
  271. static void parent(void)  
  272. {  
  273.     int i,ret,j;   
  274.     for(i=startNum;i<endNum;i++)  
  275.     {  
  276.         while(1)  
  277.         {  
  278.             ret = pthread_mutex_lock(&item->mutex);  
  279.             if(ret != 0)  
  280.             {  
  281.                 __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: mutex_lock");  
  282.                 exit(1);     
  283.             }  
  284.             //如果大于则需要解锁等待.和上面的流程一样.  
  285.             while((j= getNum()) > 0)  
  286.             {  
  287.                 ret = pthread_cond_wait(&item->cond,&item->mutex);  
  288.                 if(ret != 0)  
  289.                 {  
  290.                     __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: cond_wait");  
  291.                     exit(0);  
  292.                 }  
  293.             }  
  294.             if(j==0)  
  295.             {  
  296.                 setNum(i);  
  297.                 ret = pthread_cond_broadcast(&item->cond);  
  298.                 if(ret != 0)  
  299.                 {  
  300.                     __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: brocast");  
  301.                     exit(0);  
  302.                 }  
  303.   
  304.                 ret = pthread_mutex_unlock(&item->mutex);      
  305.                 if(ret != 0)  
  306.                 {  
  307.                     __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: mutex_unlock");  
  308.                     exit(0);  
  309.                 }  
  310.                 break;    
  311.             }  
  312.   
  313.         }  
  314.     }  
  315.     while(1)  
  316.     {  
  317.         pthread_mutex_lock(&item->mutex);  
  318.         if (ret != 0) {  
  319.             __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: mutex_lock");  
  320.             exit(1);  
  321.         }  
  322.         while ((j = getNum()) > 0) {  
  323.             ret = pthread_cond_wait(&item->cond, &item->mutex);  
  324.             if (ret != 0) {  
  325.                 __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent::cond_wait");  
  326.                 exit(1);  
  327.             }  
  328.         }  
  329.         if (j == 0) {  
  330.             setNum(-1);  
  331.             ret = pthread_cond_broadcast(&item->cond);  
  332.             if (ret != 0) {  
  333.                 __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: broadcast");  
  334.                 exit(1);  
  335.             }  
  336.             ret = pthread_mutex_unlock(&item->mutex);  
  337.             if (ret != 0) {  
  338.                 __android_log_print(ANDROID_LOG_INFO, "pthreadPrime""parent:: mutex_unlock");  
  339.                 exit(1);  
  340.             }  
  341.             break;  
  342.         }  
  343.     }         
  344. }  

3.Android.mk文件

  

  

[cpp]  view plain  copy
  1. # Copyright (C) 2009 The Android Open Source Project  
  2. #  
  3. # Licensed under the Apache License, Version 2.0 (the "License");  
  4. # you may not use this file except in compliance with the License.  
  5. # You may obtain a copy of the License at  
  6. #  
  7. #      http://www.apache.org/licenses/LICENSE-2.0  
  8. #  
  9. # Unless required by applicable law or agreed to in writing, software  
  10. # distributed under the License is distributed on an "AS IS" BASIS,  
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  12. # See the License for the specific language governing permissions and  
  13. # limitations under the License.  
  14. #  
  15. SRC_PATH_ROOT:=$(LOCAL_PATH)/../../src    
  16. LOCAL_PATH := $(call my-dir)  
  17.   
  18. include $(CLEAR_VARS)  
  19.   
  20. LOCAL_MODULE    := prime  
  21. LOCAL_SRC_FILES := prime.c   
  22. LOCAL_LDLIBS := -llog  
  23. LOCAL_SHARED_LIBRARIES := pthread  
  24. include $(BUILD_SHARED_LIBRARY)