首页 > 代码库 > Ndk开发笔记

Ndk开发笔记

ndk开发:
1.编译android本地程序的二种方法.q
2.安装ndk编译工具.
3.编写android.apk程序.
4.编写jni接口.定义应用程序接口,
5.编写Java文件,生成相应的字节码文件.
6.使用javah -jni Test 命令生成该java文件对应的c的头文件.
7.使用ndk-build命令生成对应的库文件.

一:创建一个arm本地程序.直接使用arm-linux-gcc 进行编译,如果使用到库的话需要使用 -static关键字进行静态链接库文件.

1.编译android本地程序的三种方法:
	1.使用ndk开发工具进行编译.
            a)安装ndk android-ndk-r9d-linux-x86_64.tar.bz2 使用tar -xvf android-ndk-r9d-linux-x86_64.tar.bz2.
	    b)解压完成需要配置环境变量: 
              vim ~/.bashrc
	      添加下面的行. export PATH=/home/zshh/android-ndk-r9d:$PATH    //这个是解压后ndk所在文件目录/home/zshh/android-ndk-r9d
	    c)拷贝一个ndk的样例文件到测试目录.样例文件存在/home/zshh/android-ndk-r9d/samples中.
	      $ cd /home/zshh/android-ndk-r9d/samples 下.
	      $ cp -a hello-jni/ ~/work/android/JNI/
	      $ zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Android.mk 
             
	      LOCAL_PATH := $(call my-dir)     //    LOCAL_PATH 指的是要编译的目录. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,该目录指的就是~/work/android/JNI/hello-jni/目录.
	      include $(CLEAR_VARS)            //    使用ndk进行编译的时候.必须指定这项.它会清空Makefile中所有的变量值.使用android源代码进行编译的时候,不能使用该项.
	      LOCAL_MODULE    := hello-jni     //    编译完成之后的模块的名称.
	      LOCAL_SRC_FILES := hello-jni.c   //    生成模块需要的.c文件.

	      //下面的是两者选其一.
	      include $(BUILD_SHARED_LIBRARY)  //  指定生成什么样的文件.还是动态库文件.
 	      include $(BUILD_EXECUTABLE)      //  指定生成什么样的文件.是可执行文件
	      
	     修改一下hello-jni.c输出Hello字符.


2.修改文件.生成可执行文件.
	zshh@HP:~/work/android/JNI/hello-jni/jni$ vim hello-jni.c 
		#if 0 #endif 注释其他文件.
                修改为如下:
			#include <string.h>
		        #include <jni.h>
			int main(void)
			{
				printf("Hello\n");
				return 0 ;
			}
                  
	zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build
        /home/zshh/work/android/JNI/hello-jni/jni/hello-jni.c:57:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]
 	进行编译会输出如上警告.是以为printf没有包含stdlib.h头文件.需要添加#include<stdlib.h>头文件.

	zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build  生成如下文件.

	zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build
	[armeabi-v7a] Gdbserver      : [arm-linux-androideabi-4.6] libs/armeabi-v7a/gdbserver
	[armeabi-v7a] Gdbsetup       : libs/armeabi-v7a/gdb.setup
	[armeabi] Gdbserver      : [arm-linux-androideabi-4.6] libs/armeabi/gdbserver
	[armeabi] Gdbsetup       : libs/armeabi/gdb.setup
	[x86] Gdbserver      : [x86-4.6] libs/x86/gdbserver
	[x86] Gdbsetup       : libs/x86/gdb.setup
	[mips] Gdbserver      : [mipsel-linux-android-4.6] libs/mips/gdbserver
	[mips] Gdbsetup       : libs/mips/gdb.setup
	[armeabi-v7a] Install        : hello-jni => libs/armeabi-v7a/hello-jni
	[armeabi] Install        : hello-jni => libs/armeabi/hello-jni
	[x86] Install        : hello-jni => libs/x86/hello-jni
	[mips] Install        : hello-jni => libs/mips/hello-jni
	
	他会编译生成三个平台的可执行文件,平台如上. x86,mips,armeabi-v7a,
	
3.修改编译生成指定平台的可执行文件.
      
      zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Application.mk 
      APP_ABI := all  这个是生成支持的三种平台的可执行文件.
      APP_ABI := armeabi-v7a 只会生成armeabi-v7a平台的可执行代码.

4.将生成的文件下载到开发版执行.
      //开机进入系统只会.检查usb是否插好.等待进入系统之后,执行挂载命令.是
      zshh@HP:~/work/android/JNI/hello-jni/jni$ adb shell mount -o remount,rw /system    //这个命令的作用重新使用读写权限挂载这个文件.
      
      //切换到编译完成的可执行文件所在目录.
      zshh@HP:$ cd ~/work/android/JNI/hello-jni/libs/armeabi-v7a

      zshh@HP:~/work/android/JNI/hello-jni/libs/armeabi-v7a$ adb push hello-jni  /system  //将应用程序下载到开发版的/system路径下.
      208 KB/s (9500 bytes in 0.044s)
     
5.最后测试是否能成功输出,Hello.
      zshh@HP:~/work/android/JNI/hello-jni/libs/armeabi-v7a$ adb shell       //通过android调试桥登入android操作系统.
      root@android:cd /system
      root@android:/system # ./hello-jni                                             
      Hello
      最后测试完成.输出Hello.

6.如果使用编译完成的android源代码进行编译的话.需要修改Android.MK文件,
	
    a. 修改Android.mk
	zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Android.mk 
              LOCAL_PATH := $(call my-dir)     //    LOCAL_PATH 指的是要编译的目录. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,该目录指的就是~/work/android/JNI/hello-jni/目录.
	      #include $(CLEAR_VARS)            //    这项需要注释掉,它的作用是清除MK文件中变量的值.使用android源代码编译时,不需要这样做.切记注释.
	      LOCAL_MODULE    := hello-jni     //    编译完成之后的模块的名称.
	      LOCAL_SRC_FILES := hello-jni.c   //    生成模块需要的.c文件.

	      //下面的是两者选其一,指定生成什么样的文件.
	      include $(BUILD_SHARED_LIBRARY)  // 这个是生成动态库文件.
 	      include $(BUILD_EXECUTABLE)      // 这个是生成可执行文件

	zshh@HP$ cd /home/zshh/work/arm/ARM1/Android/android-4.2.2_r1
      
    b.设置当前shell的执行环境.
        zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source ~/.bashrc
	zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source  setenv 
	including device/asus/grouper/vendorsetup.sh
	including device/asus/tilapia/vendorsetup.sh
	including device/friendly-arm/tiny4412/vendorsetup.sh
	including device/generic/armv7-a-neon/vendorsetup.sh
	including device/generic/armv7-a/vendorsetup.sh
	including device/generic/mips/vendorsetup.sh
	including device/generic/x86/vendorsetup.sh
	including device/lge/mako/vendorsetup.sh
	including device/samsung/maguro/vendorsetup.sh
	including device/samsung/manta/vendorsetup.sh
	including device/samsung/toroplus/vendorsetup.sh
	including device/samsung/toro/vendorsetup.sh
	including device/ti/panda/vendorsetup.sh
	including sdk/bash_completion/adb.bash
    
    c.使用mmm编译需要编译的目录.
	zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/JNI/jni_source_build/jni/

	   target Executable: hello-jni (out/target/product/tiny4412/obj/EXECUTABLES/hello-jni_intermediates/LINKED/hello-jni)
	   /home/zshh/work/arm/ARM1/Android/android-4.2.2_r1/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.6
	    /bin/../lib/gcc/arm-linux-androideabi/4.6.x-google/../../../../arm-linux-androideabi/bin/ld: 
	   out/target/product/tiny4412/obj/lib/crtbegin_dynamic.o: in function _start:crtbrand.c(.text+0x60): error: undefined reference to '__libc_init'
	    
	    如果出现的这个错误.那么需要做包含libc库, 
	    LOCAL_SHARED_LIBRARIES :=libc
	    添加完成之后的Android.mk文件如下
		      LOCAL_PATH := $(call my-dir)     //    LOCAL_PATH 指的是要编译的目录. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,该目录指的就是~/work/android/JNI/hello-jni/目录.
		      #include $(CLEAR_VARS)            //    这项需要注释掉,它的作用是清除MK文件中变量的值.使用android源代码编译时,不需要这样做.切记注释.
		      LOCAL_MODULE    := hello-jni     //    编译完成之后的模块的名称.
		      LOCAL_SRC_FILES := hello-jni.c   //    生成模块需要的.c文件.
	              LOCAL_SHARED_LIBRARIES :=libc
		      //下面的是两者选其一,指定生成什么样的文件.
		      include $(BUILD_SHARED_LIBRARY)  // 这个是生成动态库文件.
	 	      include $(BUILD_EXECUTABLE)      // 这个是生成可执行文件

	    zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/JNI/jni_source_build/jni/'
	    Install: out/target/product/tiny4412/system/bin/hello-jni
		
	   
	  如果成功编译,生成的目标文件存在hello-jni文件out/target/product/tiny4412/system/bin/hello-jni
	

    d.接下来的步骤,和步骤5.是一样的.


二:创建一个NDK本地程序. 该程序的功能是控制led的亮灭.
   1.搭建一个eclipse环境.eclipse.tar.gz
     a.解压这文件.
       zshh@HP:~/work/android$ tar -xvf eclipse.tar.gz -C software   //解压
       zshh@HP:~/work/android$ cd software/eclipse/
       zshh@HP:~/work/android/software/eclipse$ ./eclipse &           //运行eclipse
     b.使用这个ide环境创建一个android apk应用程序. 
	它大概的功能是提供四个按钮,当按下其中某个按钮的时候,就让某个灯亮起来.
     

      c.创建一个Test文件,其中包括接口文件如下:  
	1.定义c和java之间的接口.控制亮灭,
	  1.1.第一个接口应该是打开设备文件.
		native int openLed();
	  1.2.最后一个是关闭设备文件.
		native int closeLed(); 
	  1.3.第二个接口应该是亮,
		native int ledOn(int no);
	  1.4.第三个接口应该是灭. 
		native int ledOff(int no);

	2. 
         zshh@HP:~/work/android/JNI/NDKnative$ vim Test.java
	 /*************************************************************************
	    > File Name: Test.java
	    > Author: zshh0604
	    > Mail: zshh0604@.com 
	    > Created Time: Mon 22 Dec 2014 10:48:09 PM
	 ************************************************************************/
	 public class Test
	 {
		native int openLed(); 
		native int closeLed(); 
		native int onLed(int no); 
		native int offLed(int no);

		static{
			System.loadLibrary("led");
		}

		public static void main(String[] args)
		{
		
		}
	  }

	3.编译生成字节码文件.
          zshh@HP:~/work/android/JNI/NDKnative$ javac Test.java    
	
	4.生成对应的头文件,该命令执行完成之后.会生成Test.h头文件.
          zshh@HP:~/work/android/JNI/NDKnative$ javah -jni Test   
	
	5.拷贝一个NDK代码例子.并把Test.h头文件拷贝到当前文件的jni目录中.
	  zshh@HP:~/work/android/JNI$ mkdir Jni
	
	6.获取ndk开发工具的代码样例.
	  zshh@HP:~/work/android/JNI/Jni$ cp -a ../../../../android-ndk-r9d/samples/hello-jni/ ./
          zshh@HP:~/work/android/JNI/Jni/jni$ cp ../../NDKnative/Test.h
	  
	7.将Test.h改名为led.c, mv Test.h led.c
	  zshh@HP:~/work/android/JNI/Jni/jni$ mv Test.h led.c
	
	8.获取apk应用的完整类名.com_embsky_MainActivity      //讲这个名称替换当前Test类名.
	  使用vim打开led.c文件,再命令行模式下使用如下命令吧Test替换成com_embsky_MainActivity 
     	  
	  :%s/Test/com_embsky_MainActivity/g.
	  得到如下文件: 
		#include <jni.h>
		#include <sys/types.h>
		#include <stdlib.h> 
		#include <fcntl.h>
		#include <errno.h>

		static int fd;
		static int flags = 0;
		/*
		 * Class:     com_embsky_MainActivity
		 * Method:    openLed
		 * Signature: ()I
		 */
		JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_openLed
		  (JNIEnv *env, jobject obj)
		  {
			if(flags == 0)
			{
				fd = open("dev/leds",O_RDWR);
				if(fd< 0)
				{
					return -EPERM;
				}
				flags = 1;
				return 0 ;
			}
			return -EBUSY;
		  }

		/*
		 * Class:     com_embsky_MainActivity
		 * Method:    closeLed
		 * Signature: ()I
		 */
		JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_closeLed
		  (JNIEnv *env , jobject obj)
		  {
			if(flags == 1)
			{
				close(fd); 
				flags = 0; 
				return 0;
			}
			return -ENODEV;
		  }

		/*
		 * Class:     com_embsky_MainActivity
		 * Method:    onLed
		 * Signature: (I)I
		 */
		JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_onLed
		  (JNIEnv *env , jobject obj, jint no)
		  {
			int ret; 
			if(flags == 1)
			{
				ret = ioctl(fd,1 ,no); 
				if(ret < 0 )
				{
					return -EPERM;
				}
				return 0 ;
			}
			return -ENODEV; 
		  }

		/*
		 * Class:     com_embsky_MainActivity
		 * Method:    offLed
		 * Signature: (I)I
		 */
		JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_offLed
		  (JNIEnv * env, jobject obj, jint no)
		  {
			int ret; 
			if(flags == 1)
			{
				ret = ioctl(fd, 0, no); 
				if(ret < 0)
				{
					return -EPERM; 
				}
				return 0;
			}
			return -ENODEV;
		  }


       9.修改Android.mk文件,如下:
	LOCAL_PATH := $(call my-dir)
	include $(CLEAR_VARS)
	LOCAL_MODULE    := leds
	LOCAL_SRC_FILES := led.c
	include $(BUILD_SHARED_LIBRARY)
       
       10.zshh@HP:~/work/android/JNI/Jni/jni$ ndk-build


       11.生成动态库如下:[armeabi-v7a] Install : libleds.so => libs/armeabi-v7a/libleds.so

       12.将动态库push到开发板的/system/lib目录下.
          zshh@HP:~/work/android/JNI/Jni/libs/armeabi-v7a$ adb push libleds.so  /system/lib

       13.动态库制作完成.
	
       14.创建一个apk应用,创建一个类:  com.embsky.MainActivity.再这个类中加载并调用本地库led.

Ndk开发笔记