Android源码学习之如何使用eclipse+NDK【一】







网上已经有太多的有关如何配置eclipse+NDK了,本人就不再重复这些了,只是想记录下自己开始写第一个NDK程序的整个流程(保证可执行),共自己和大家分享。

首先安装一个能够支持Native代码的eclipse插件Sequoyah,然后在eclipse中的“窗口-首选项-Android”中多出来了一个“本机开发”选项,在“NDK Location”选择你的android-ndk的路径。Android源码学习之如何使用eclipse+NDK【一】_第1张图片有了Sequoyah插件进行Android Native开发就简单多了。下面根据自己的第一个Android Native程序开发过程,做一个记录。

1. 创建Android应用程序MyFirstNativeStore,过程省去。

2. 创建用来保存int和String类型的

public enum StoreType {
 Integer, String
}和Store类,该类使用到Native函数,利用本地语言进行保存和读取操作。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package  com.yemeishu.myfirstnativestore.store;
 
public  class  Store {
 
     static {
         System.loadLibrary( "MyFirstNativeStore" );
     }
     
     public  native  int  getInteger(String pKey);
     public  native  void  setInteger(String pKey, int  pInt);
     
     public  native  String getString(String pKey);
     public  native  void  setString(String pKey, String pString);
}

 其中在eclipse中会提示“此项目没有本机库支持”

这时候可以点开左边的提示,选择“加上本机支持”:

Android源码学习之如何使用eclipse+NDK【一】_第2张图片

之后会在出现添加本机支持有关的参数名称(如NDK所在路径、生成的lib**.so文件名称等等)

Android源码学习之如何使用eclipse+NDK【一】_第3张图片

添加完成之后,在项目中多了一个“jni”文件夹,该文件夹这时候多出了两个文件,如本程序中多了“MyFirstNativeStore.cpp”和“Android.mk”,有了这两个文件,我们不用自己手动编写麻烦的“Android.mk”文件了。这两个文件的含义也不用多说了。

2. 利用javah编译生成头文件,首先在eclipse中“运行-外部工具-外部工具配置”选项中配置javah工具:

3. 执行外部工具javah,执行完之后,刷新本工程,会在jni文件夹下自动生成头文件“com_yemeishu_myfirstnativestore_store_Store.h”(包名+java类名)。

4. 完成一些基本类配置之后,我们来设计android显示层,显示需要导入的store数据和读取store数据,设计界面

Android源码学习之如何使用eclipse+NDK【一】_第4张图片

xml代码如下:

 在StoreActivity类中 直接获取控件资源,设置两个按钮的按键事件函数onSetValue()和onGetValue。

 两个函数代码如下:

 5. 完成了界面设计了,java层基本完成,现在把注意力放在jni文件夹下,读取和保存Store类型的数据的Native层实现了。

5.1 在文件夹下jni由于没配置好c++编译器和NDK命令等,所以自动生成的.cpp和.h等文件出现错误,所以先进行配置,首先在工程属性中,在“C/C++ Build”下的“Tool Chain Editor”选择GCC等工具。

Android源码学习之如何使用eclipse+NDK【一】_第5张图片

"C/C++ Build的Builder Settings"中配置"Build command:"直接输入ndk-build。

Android源码学习之如何使用eclipse+NDK【一】_第6张图片

在“Behaviour”中设置

Android源码学习之如何使用eclipse+NDK【一】_第7张图片

5.2 首先在jni文件夹下创建Store.h文件,主要创建一些保存Store类型、Store数组等枚举、结构体等信息,直接上代码(你肯定能看得懂):

 这里还定义了一些操作Store类型信息的操作函数,如findEntry()查找保存在pStore链表中的Store数据,并返回StoreEntry结构数据,即数据的key、数据类型(int和string类型)和数据值。具体实现函数在jni文件夹下创建Store.c类中实现,代码如下:

5.3 完成了基本Store操作之后,现在开始完成连接java和Native桥梁的“MyFirstNativeStore.cpp”(还记得吧?),在该类中直接实现由javah外部工具生成的“com_yemeishu_myfirstnativestore_store_Store.h”的头文件定义的四个函数,“com_yemeishu_myfirstnativestore_store_Store.h”中的四个函数定义如下显示

 具体实现函数代码如下

 5.4 最后修改下自动生成的Android.mk文件,将Store.c也包含进去

?
LOCAL_PATH := $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_MODULE    := MyFirstNativeStore
### Add all source file names to be included in lib separated by a whitespace
LOCAL_SRC_FILES := MyFirstNativeStore.c Store.c
 
include $(BUILD_SHARED_LIBRARY)

 6. 编译、执行。结果省去。

利用NDK进行Android编程,主要流程为:

一、编写Android程序,创建需要利用Native编写的实现函数类(包含native关键字的函数,和static{System.loadLibrary("xxx")})。

二、利用javah外部工具生成包含Native函数的头文件(头文件名为:包名+函数名称)。

三、通过“本机开发”(eclipse插件Sequoyah)自动生成Android.mk文件和实现Native函数的.c或者.cpp文件,实现函数功能。

四、利用Android提供的“Android GCC”或者“Cygwin GCC”或者“MinGW GCC”等工具和NDK-build编译Native函数,生成lib***.so(windows下)库文件。

五、完成实现java与Native的函数调用和回调、结果返回等。

 7. 最后提供源码程序:MyFirstNativeStore 和插件sequoyah(org.eclipse.sequoyah.feature.2.0.0.I20110609-0753)

 










JIN学习一、Android使用已有C/C++代码、第三方SO库的方法

1.配置好NDk编译环境(这个Google一下,:))

2.将已有C/C++代码,编译成SO文件

3.Eclipse中新建Android工程,创建一个jni目录。如下所示

Android源码学习之如何使用eclipse+NDK【一】_第8张图片

4.在jni目录创建C/C++文件,里面调用步骤2函数,或者第三方SO的函数

5.创建Android.mk文件,内容为:

LOCAL_PATH := $(call my-dir)
# Link libXXX.so
include $(CLEAR_VARS)  
LOCAL_MODULE :=XXX  
LOCAL_SRC_FILES := libXXX.so  
#LOCAL_SRC_FILES := /jni/libXXX.so  
include $(PREBUILT_SHARED_LIBRARY)  

include $(CLEAR_VARS)
LOCAL_MODULE    := XXXAgent
LOCAL_SRC_FILES := XXXAgent.cpp
LOCAL_SHARED_LIBRARIES := XXX
LOCAL_LDLIBS := -llog 
include $(BUILD_SHARED_LIBRARY)

将XXX替换为编译出的SO或者第三方SO的名字

6.将jni目录下的C/C++编译成Android通过JNI调用的SO文件,这里名字为  libXXXAgent.so

7.Android工程中使用时,首先加载一下用到的SO库,即可正常使用了。

System.loadLibrary("XXX");
System.loadLibrary("XXXAgent");

说明:这里的重点是如何调用第三方SO库,所有部分细节没有表述。比如Javah生成相关头文件,jni调用等。


补充:上面步骤可能比较多,这里举一个例子加以说明。某天,你得到一个第三SO库,名字就叫XXX。你发现里面的功能正好是你Android项目里需要的,可以拿来就用。这个消息令你高兴万分啊那是。但是,如何在Android里使用呢,XXX库里的调用约定不符合JNI规定,是不能直接来用的。哈哈,想到了,创建一个XXXAgent.so。它的调用采用JNI,然后在实现里调用第三方SO就可以了。以上就是大概的思路。


SO文件是如何自动安装到虚拟机或者手机上呢,NDK生成的SO文件会自动放到libs/armeabi目录下。而此目录下的SO文件都会自动打包到APK,安装后释放到/data/data/项目名字/lib/下。这里XXXAgent.SO文件生成libs/armeab,XXXAgent.SO依赖XXX文件,XXX文件自动拷贝到libs/armeab下。(XXX文件放在jni下,和Android.mk同级,这个是和Android.mk里的设置配合使用的)









java调用dll或so动态库文件(c++/c)



java调用dll或so动态库文件(c++/c)开发平台:Eclipse3.3.1.1+CDT(cdt-master-4.0.3)+MinGW(MinGW-5.1.4) 

一:下面是java调用dll(C++) 
1:下载并安装cdt :http://www.eclipse.org/cdt/downloads.php  :选择自己eclipse 支持的cdt插件,下载,并且 
通过eclipse-->software update-->find and install 安装cdt 

2:下载并安装mingw :http://sourceforge.net/project/showfiles.php?group_id=2435 
然后,点击mingw.exe,选择 下载并安装  ,然后都选中(速度可能有点慢,要有耐心), 

3:环境变量配置(在系统变量或者用户变量里添加以下变量,注意路径根据实际安装的进行修改): 
  PATH:    D:\Program Files\MinGW\bin   
  C_INCLUDE_PATH:  D:\Program Files\MinGW\include 
  CPLUS_INCLUDE_PATH:  D:\Program Files\MinGW\include\c++\3.4.5;D:\Program Files\MinGW\include\c++\3.4.5\mingw32;D:\Program                        Files\MinGW\include\c++\3.4.5\backward;D:\Program Files\MinGW\include 
   LIBRARY_PATH:   D:\Program Files\MinGW\lib 

   LIBRARY_PATH 这个 变量最好加上,以前没有加,也可以编译出正确的dll,但是后来编译出来的dll就有问题,最后定位出来没有加LIBRARY_PATH这个变量,造成编译出来的dll不能正常运行.  
如果添加完所有变量 最好重启电脑。
 

4:相关设置 
eclipse-->Window->Preferences->C/C++->New CDT project wizard->Makefile Project 
找到 Binary Parser 把Elf Parser取消, 选中 PE Windows Parser. 

由于在MinGW目录下的make文件名为"mingw32-make.exe", eclipse默认的调用文件名为"make.exe" 
所以先将MinGW目录下文件名为"mingw32-make.exe"做个备份,然后将该文件重命名为"make.exe" 

5:java工程和 c++工程,还是以经典的HelloWorld为例 
Java代码   收藏代码
  1. public class Hello {  
  2.   
  3. public native void sayHello();  
  4.   
  5. static{  
  6.     System.loadLibrary("Hello");  
  7. }  
  8.   
  9.   
  10. public static void main(String[] args){  
  11.     Hello h = new Hello();  
  12.     h.sayHello();  
  13. }  

然后编译生成class文件,用命令javah class文件生成头文件Hello.h 

创建c++工程: 
注意: 

1>:在eclipse设置c++ build 模式:将 release 状态改为active,否则java程序调用生成的dll会报以下错误 
Exception in thread "main" java.lang.UnsatisfiedLinkError: 


2>:编译的时候如果报以下错误,解决方法就是在.h文件和.cpp文件里添加一个int mian()方法 

**** Rebuild of configuration Debug for project HelloC++ **** 

**** Internal Builder is used for build               **** 
g++ -O0 -g3 -Wall -c -fmessage-length=0 -oHello.o ..\Hello.cpp 
..\Hello.cpp:10:2: warning: no newline at end of file 
g++ -LD:\Program Files\Java\jdk1.6.0_10\include\win32 -LD:\Program Files\Java\jdk1.6.0_10\include -oHelloC++.exe Hello.o 
D:/Program Files/mingw/bin/../lib/gcc/mingw32/3.4.5/../../../libmingw32.a(main.o):main.c:(.text+0xbd): undefined reference to `WinMain@16' 
collect2: ld returned 1 exit status 
Build error occurred, build is stopped 
Time consumed: 5192  ms.  
------------------------------------------------------------------------------
修改后的Hello.h内容: 
C++代码   收藏代码
  1.         /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class Hello */  
  4.   
  5. #ifndef _Included_Hello  
  6. #define _Included_Hello  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     Hello 
  12.  * Method:    sayHello 
  13.  * Signature: ()V 
  14.  */  
  15.     int main() ;  
  16.       
  17.       
  18. JNIEXPORT void JNICALL Java_Hello_sayHello  
  19.   (JNIEnv *, jobject);  
  20.   
  21. #ifdef __cplusplus  
  22. }  
  23. #endif  
  24. #endif  

修改后的Hello.cpp内容: 
C++代码   收藏代码
  1. #include <iostream>    
  2. #include "Hello.h"   
  3. using namespace std;      
  4.   
  5. int main()      
  6.      {   
  7.     return 1;  
  8.      }  
  9.   
  10.   
  11. JNIEXPORT void JNICALL Java_Hello_sayHello(JNIEnv *, jobject){  
  12.     printf("Hello world!\n");  
  13.     return ;  
  14. }  

a:然后用eclipse c++插件的 make targets--build生成Hello.o 
target name 随便 
make target 随便 
命令默认就可以 
   b:将wingw/bin下的g++考到 刚才生成的Hello.o同级目录下,然后在命令行里切换到Hello.o所在的目录 
   执行命令,注意参数(我的java目录D:\Program Files\Java\): 
   g++ -I"D:\Program Files\Java\jdk1.6.0_10\include"  -Wl,--add-stdcall-alias -shared -o Hello.dll Hello.o   
   
  6:运行: 
    将刚才生成的dll文件拷贝到java工程的根目录下,运行Hello.class即可 


  注意:如果测试类是在某个包下的话,例如:com.test.Hello,那我们编译class文件的时候: javac com\test\Hello.java 
  然后: javah com.test.Hello,这样生成的头文件中的方法在被调用的时候才能找到。 
   


二:java调用so(C) 
   java和c / c++通信都可以通过jni来实现。 在java代码中: 
   System.loadLibrary("Hello"); 
   Hello不能写成Hello.dll或者Hello.so,它会根据系统平台自动填充,需要注意的是在unix/linux下生成.so动态库文件的时候, 
   需要在Hello.so前添加lib,否则找不到.so文件(libHello.so),运行的时候需要指定.so的路径: 
   java  -Djava.library.path=/homw/user/so所在目录  -jar  Hello.jar 
   
三:命令整理: 
  以c为例(如果是c++,则把gcc改成g++就OK): 
  1:在unix/linux环境下 
  1.1:生成.o文件 
  gcc -I/usr/lib/j2sdk1.5-ibm/include -fPIC -c example.c 
  1.2:生成动态库.so文件 
  gcc -shared -WI -soname example.o -o  libexample.so 
  2:在windows环境下 
  2.1:生成.o文件 
  gcc -c -I"D:\Program Files\Java\jdk1.6.0_10\include" -I"D:\Program Files\Java\jdk1.6.0_10\include\win32" -o Hello.o Hello.c 
  2.2:生成dll文件 
  gcc -I"D:\Program Files\Java\jdk1.6.0_10\include" -Wl,--add-stdcall-alias -shared -o Hello.dll Hello.o 

由于时间关系,写得比较乱,o(∩_∩)o...










你可能感兴趣的:(Android源码学习之如何使用eclipse+NDK【一】)