Ndk開發筆記

<pre name="code" class="cpp">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 進行編譯,假設使用到庫的話需要使用 -statickeyword進行靜態連接庫文件.

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應用程序. 
	它大概的功能是提供四個button,當按下當中某個button的時候,就讓某個燈亮起來.
     

      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.


	javah -d ../jni com.onesuncomm.JniCallCTest




三: 使用android源碼編譯apk應用程序.System.loadLibaray("led"); 
        
        zshh@HP:~/work/android/android/07Jar/JniAndroidSrc$ ls
	Android.mk  led.c
	
	使用這兩個文件編譯生成libled.so文件.

	//zshh@HP:~/work/android/android/06Jni/JniAndroidSrc$ vim Android.mk。 
	僅僅需要。Android.mk和led.c兩個文件.

        1.使用android源碼編譯android應用程序, 
          LOCAL_PATH := $(call my-dir)
	  LOCAL_MODULE    := libled         //注意必須是在led前面加上lib,編譯生成的庫名是.libled.so,使用	
	  LOCAL_SRC_FILES := led.c            
	  LOCAL_SHARED_LIBRARIES :=libc   
	  include $(BUILD_SHARED_LIBRARY)    
	
	2.編譯libled.so庫文件.
	  zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source  setenv 
	  zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/06Jni/JniAndroidSrc/
	
	3.輸出編譯完畢的路徑例如如下.
	  Install: out/target/product/tiny4412/system/lib/libled.so
	4.將生成的libled.so push到/system/lib中.
	 Install: out/target/product/tiny4412/system/lib/libled.so
	 
	zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/lib$ adb push libled.so /system/lib/
	115 KB/s (5276 bytes in 0.044s)

	
	5.生成Android fremework文件夾的 jar包。
	  zshh@HP:~/work/android/android/07Jar/Jar$ ls
	  Android.mk  com/embsky/Led.java              //包括兩個java文件.
	 
       	  package com.embsky;
	  public class Led {
		native int openLed();
		native int closeLed();
		native int ledOn(int no);
		native int ledOff(int no);

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

		public int ledStart(){
			/*nothing*/
			return openLed();
		}
	
		public int ledStop(){
			return closeLed();
		}	

		public int ledOps(int no, int on){
			if(on == 1){
				return ledOn(no);
			}
	
			return ledOff(no);
		}
	  }

	6.改動Android.mk文件.
	  LOCAL_PATH 			:=$(call my-dir)
	  LOCAL_SRC_FILES		:=$(call all-subdir-java-files)  //當前src文件夾下的所有文件.
	  LOCAL_MODULE			:=led
	  LOCAL_JAVA_LIBRARIES	        :=
	  include $(BUILD_JAVA_LIBRARY)                                 //包括這個生成的是Java的類庫.
	
	7.完畢以後。可以編譯生成這個.jar文件.
	  zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source  setenv 
          zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/07Jar/Jar/
       
	8. 生成的jar包會存放在,
           Install: out/target/product/tiny4412/system/framework/led.jar
      注意編譯的時候,該文件夾必定要由讀寫權限.不然會失敗.
	  假設因爲權限問題。可以使用chown  zshh:zshh out/target/product/tiny4412/system/framework -R
	  更改用戶屬主.
   	  生成完畢以後需要將這個framework下的led.jar放到開發版的/system/framework文件夾下.
	    zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/framework$ adb push led.jar  /system/framework/
	    
	9.需要改動開發板中 /etc/permissions   //再platform.xml文件裏聲明咱們的框架庫文件.不然這個led.jar沒法使用.
         root@android:/etc/permissions # vim platform.xml  
	 <library name="led" file="/system/framework/led.jar"/> 
     以後從新啓動一下開發版,
	

        10.編譯android apk
	  
            zshh@HP:~/work/android/android/07Jar/Apk$ ls  //apk包括例如如下文件和文件夾.
	    AndroidManifest.xml  Android.mk  res  src
           
	   需要改動AndroidManifest.xml文件.
		<?xml version="1.0" encoding="utf-8"?>
		<manifest xmlns:android="http://schemas.android.com/apk/res/android"
		    package="com.embsky"
		    android:versionCode="1"
		    android:versionName="1.0" >

		    <uses-sdk
			android:minSdkVersion="17"
			android:targetSdkVersion="17" />

		    <application
			android:allowBackup="true"
			android:icon="@drawable/ic_launcher"
			android:label="@string/app_name"
			android:theme="@style/AppTheme" >
			<uses-library android:name="led" />                 //聲明依賴的led.jar文件.假設不聲明可以編譯經過,但運行會由錯誤.
			<activity
			    android:name="com.embsky.MainActivity"
			    android:label="@string/app_name" >
			    <intent-filter>
				<action android:name="android.intent.action.MAIN" />

				<category android:name="android.intent.category.LAUNCHER" />
			    </intent-filter>
			</activity>
		    </application>
		</manifest>
            
           11.zshh@HP:~/work/android/android/07Jar/Apk$ gedit Android.mk 
		LOCAL_PATH		:=$(call my-dir)
		LOCAL_SRC_FILES		:=$(call all-subdir-java-files)
		LOCAL_PACKAGE_NAME	:=Led
		LOCAL_JAVA_LIBRARIES	:=led                           //注意這裏必須聲明依賴的java庫.
		include $(BUILD_PACKAGE)
        
            12.zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/07Jar/Apk/
		這個是編譯生成的apk文件Install: out/target/product/tiny4412/system/app/Led.apk
		zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/app$ adb push Led.apk /system/app/
		5099 KB/s (294516 bytes in 0.056s)
相關文章
相關標籤/搜索