• [转][android][利用JNI技术在Android中调用、调试C++代码]


    在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在Android中会生成Linux系统下的.so文件(好吧,其实我基本没用过Linux)。 
    没写过JNI的可以看看我之前的博客(Windows下利用Visual Studio开发的过程):http://cherishlc.iteye.com/admin/blogs/1328136 

    以及自动生成工具swig的使用方法(数组的支持不好!其他挺方便):http://cherishlc.iteye.com/admin/blogs/1689224 

    另外推荐一篇非常不错的NDK博文,(配置忽略,主要是各种数据的传递,下代码看看吧)http://vaero.blog.51cto.com/4350852/782787 
    扯远了,下面来看看真正在Android中的开发过程。 

    1、下载ADT及NDK 


    其中ADT中包含了Eclipse及google的开发套件,不用写C++的下载ADT就足够了。 
    NDK则是包含了GCC的编译器,以及各个平台(arm,X86,MIPS)的相关头文件,交叉编译的一些平台相关文件等。 

    2、在ADT中配置NDK路径 
    解压NDK压缩包到任意路径,按下图在ADT中(也即ADT解压后的Eclipse文件下的Eclipse中)设置NDK的路径。 
    设置方法如下图所示: 
     

    3、创建含有本地代码的Android Project 

    该过程分为以下两步: 

    • 创建普通的Android Application工程(注意最小支持的API版本要不小于14)
    • 加入本地代码支持


    具体过程如下图所示: 
    创建工程: 
     

    加入本地代码支持: 
     

    完成情况: 


    点击菜单栏Project->Build All命令进行编译。 


    注意:如果之前最小支持的API版本要不小于14,将出现编译错误。“Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 7 in ./AndroidManifest.xml” 

    解决方法如下: 
    打开AndroidManifest.xml,切换到源文件视图,将minSdkVersion 改为14以上: 


    4、编写Java端代码和C++端代码 
    Java端,注意不要继承自Android中的类,否则javah编译头文件时要指定android类路径。 

    Java代码  收藏代码
    1. package com.lc.testndk2;  
    2. import android.util.Log;  
    3. public class NativeClass {  
    4.     //数组a中的每个元素都加上b,返回值为在C++中数据是否为a中数据拷贝得到的(按值拷贝还是传递指针)  
    5.     public static native boolean jniArrayAdd(int[] a, int b);  
    6.   // 在C++中创建Java中的int数组,其中元素为 数组a中的对应元素乘以b  
    7.     public static native int[] jnitArrayMul(int[] a,int b);  
    8.     static {  
    9.         Log.i("NativeClass","before load library");  
    10.         System.loadLibrary("TestNDK2");//注意这里为自己指定的.so文件,无lib前缀,亦无后缀  
    11.         Log.i("NativeClass","after load library");    
    12.     }  
    13. }  



    javah推荐两种方法: 


    在Eclipse中配置javah外部工具方法为: 
     

    上图中最长的一行命令如下: 

    Cmdineclipse代码  收藏代码
    1. -v -classpath "${project_loc}/bin/classes" -d "${project_loc}/jni" ${java_type_name}  


    配置好之后: 
     
    点刚才配置好的javah工具,生成.h文件,然后: 
     


    Java端调用JNI方法的代码: 
    将MainActivity改为: 

    Java代码  收藏代码
    1. package com.lc.testndk2;  
    2.   
    3. import java.util.Arrays;  
    4.   
    5. import android.app.Activity;  
    6. import android.os.Bundle;  
    7. import android.widget.TextView;  
    8.   
    9. public class MainActivity extends Activity {  
    10.   
    11.     @Override  
    12.     protected void onCreate(Bundle savedInstanceState) {  
    13.         super.onCreate(savedInstanceState);  
    14.         TextView  tv = new TextView(this);       
    15.         int[] array = new int[] { 12,  3};      
    16.         String str = "数组,调用C++前" + Arrays.toString(array);  
    17.         boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array,             1);  
    18.         str += " 在C++中为副本?  " + isCopyOfArrayInCpp;  
    19.         str += " 数组,调用C++后:" + Arrays.toString(array);  
    20.         tv.setText(str);  
    21.         setContentView(tv);  
    22.     }  
    23. }  




    编写C++代码: 
    打开刚才系统生成的TestNDK2.cpp,修改成如下样子: 

    C++代码  收藏代码
    1. #include <jni.h>  
    2. #include "com_lc_testndk2_NativeClass.h"  
    3. #ifdef __cplusplus  //最好有这个,否则被编译器改了函数名字找不到不要怪我  
    4. extern "C" {  
    5. #endif  
    6. /* 
    7.  * Class:     com_lc_testndk2_NativeClass 
    8.  * Method:    jinArrayAdd 
    9.  * Signature: ([II)[I 
    10.  */JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(  
    11.         JNIEnv * env, jclass, jintArray array, jint b) {  
    12.   
    13.     jsize size = env->GetArrayLength(array);  
    14. //  jintArray sum=env->NewIntArray(2);  
    15.   
    16.     jboolean isCopy;  
    17.     jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);  
    18.     for (int i = 0; i < size; i++)  
    19.         pArray[i] += b;  
    20.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);  
    21.     //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);  
    22.   
    23.     return isCopy;  
    24. }  
    25.   
    26. /* 
    27.  * Class:     com_lc_testndk2_NativeClass 
    28.  * Method:    jnitArrayMul 
    29.  * Signature: ([II)[I 
    30.  */JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(  
    31.         JNIEnv * env, jclass, jintArray array, jint b) {  
    32.   
    33.     jsize size = env->GetArrayLength(array);  
    34.     jintArray product = env->NewIntArray(size);  
    35.     jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, 0);  
    36.     jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);  
    37. //  jintArray product = env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃  
    38.     for (int i = 0; i < size; i++)  
    39.         pProduct[i] =pArray[i]* b;  
    40.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);  
    41.     env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);  
    42.     return product;  
    43. }  
    44.   
    45. #ifdef __cplusplus  
    46. }  
    47. #endif  



    5、配置生成的.so文件的目标平台 
    Java是跨平台的可是C++生成动态链接文件不是!!!同是Android,底层的CPU架构不同,动态链接文件也不同。。。好吧,这个我不知道原因。。。 
    于是乎,还得为不同的CPU创建不同的动态链接库文件,好在一行命令搞定~所有的动态链接一起打包,管他是哪个CPU,统统适用,Happy啊。 
    参考自:http://bbs.csdn.net/topics/390158301 
    过程如下: 
     
    再编译时会发现生成了对应以上四个平台的.so文件~~~ 
     

    一切搞定,可以运行了!!!运行结果如下: 
     


    6、Java与C++联合调试 
    参见:http://blog.csdn.net/wjr2012/article/details/7993722 

    注意: 

    • C++的调试器有几秒的延迟才能启动好,也就是程序运行了一会儿才可以开始调试,所以要调试的代码一定要是几秒钟后才能调试!!!
    • 断点设置在C++中才有效。。。



    过程为: 
    右键点击工程文件, 在properties -> C/C++ Build中: 

    完了设置断点(只能在C++中)就可以启动调试了~~ 



    好吧,,,调试怎么不灵光呢。。。再想想刚才的注意事项。。。好吧,早执行完了JNI代码了。。。 
    于是乎,修改MainActivity代码如下: 

    Java代码  收藏代码
    1. package com.lc.testndk2;  
    2.   
    3. import java.util.Arrays;  
    4. import java.util.Timer;  
    5. import java.util.TimerTask;  
    6.   
    7. import android.annotation.SuppressLint;  
    8. import android.app.Activity;  
    9. import android.os.Bundle;  
    10. import android.os.Handler;  
    11. import android.os.Message;  
    12. import android.util.Log;  
    13. import android.widget.TextView;  
    14.   
    15. /** 
    16.  * @author LC 
    17.  * 
    18.  *完整的演示Android通过JNI调用C++代码的工程 
    19.  */  
    20. public class MainActivity extends Activity {  
    21.   
    22.     TextView tv = null;  
    23.     int count = 0;  
    24.     Timer timer;  
    25.   
    26.      @SuppressLint("HandlerLeak")  
    27.     class MyHandler extends Handler{  
    28.         @Override  
    29.         public void handleMessage(Message msg) {  
    30.             if (tv != null) {  
    31.                 tv.setText(msg.getData().getString("text"));  
    32.             }  
    33.             super.handleMessage(msg);  
    34.         }  
    35.     };  
    36.       
    37.     Handler handle=  new MyHandler();  
    38.       
    39.       
    40.     class refreshTask extends TimerTask {  
    41.   
    42.         @Override  
    43.         public void run() {  
    44.             try {  
    45.                 count++;  
    46.                 Log.i("MainActivity""before call native code,count="  
    47.                         + count);  
    48.                 int[] array = new int[] { count, -count, 2*count };  
    49.   
    50.                 String str = "第" + count + "次了 ";  
    51.   
    52.                 str += "数组,调用C++前" + Arrays.toString(array);  
    53.   
    54.                 boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array,1);  
    55.                 str += " 在C++中为副本?  " + isCopyOfArrayInCpp;  
    56.                 str += " 数组,调用C++后:" + Arrays.toString(array)+" ";  
    57.   
    58.                 str+="测试在C++中创建数组: ";  
    59.                 str +=  Arrays.toString(array)+"* 2 =";  
    60.                 str+=Arrays.toString(NativeClass.jnitArrayMul(array, 2))+" ";  
    61.                   
    62.                 Message msg=new Message();  
    63.                 Bundle b=new Bundle();  
    64.                 b.putString("text", str);  
    65.                 msg.setData(b);               
    66.                 handle.sendMessage(msg);  
    67.                   
    68.                 Log.i("MainActivity""after call native code");  
    69.             } catch (Exception e) {  
    70.                 Log.i(MainActivity.class.getSimpleName(), e.toString());  
    71.                 e.printStackTrace();  
    72.             }  
    73.   
    74.         }  
    75.     };  
    76.       
    77.       
    78.     @Override  
    79.     protected void onCreate(Bundle savedInstanceState) {  
    80.         super.onCreate(savedInstanceState);  
    81.         tv = new TextView(this);  
    82.         tv.setText("我是初始值");  
    83.         setContentView(tv);  
    84.     }  
    85.   
    86.     @Override  
    87.     protected void onPause() {  
    88.         Log.i(MainActivity.class.getSimpleName(),"onPuase()");  
    89.         timer.cancel();  
    90.         timer=null;  
    91.         super.onPause();  
    92.     }  
    93.   
    94.     @Override  
    95.     protected void onResume() {  
    96.         Log.i(MainActivity.class.getSimpleName(),"onResume()");  
    97.         timer=new Timer();  
    98.         timer.scheduleAtFixedRate(new refreshTask(), 01000);  
    99.         super.onResume();  
    100.     }  
    101.   
    102. }  



    运行到断点的结果: 

  • 相关阅读:
    使用javaDate类代数据仓库维度表
    Hermes和开源Solr、ElasticSearch 不同
    MapReduce 异常 LongWritable cannot be cast to Text
    吐槽CSDN编辑
    Codeforces 452A Eevee
    看不清楚未来,请做好如今
    JDBC数据库连接
    mixpanel实验教程(2)
    使用jquery+一般处理程序异步载入信息
    Eclipse中的Maven项目报Unbound classpath variable错误
  • 原文地址:https://www.cnblogs.com/nigel0913/p/3363619.html
Copyright © 2020-2023  润新知